Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: BLE_API mbed-dev nRF51822
main.cpp
- Committer:
- cho45
- Date:
- 2016-08-31
- Revision:
- 59:2d6c0bff2151
- Parent:
- 58:64df960619ce
- Child:
- 60:b899414e1d34
File content as of revision 59:2d6c0bff2151:
/* BLE Keyboard Implementation for mbed
* Copyright (c) 2016 cho45 <cho45@lowreal.net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cmath>
#include "mbed.h"
#include "HIDController_BLE.h"
#include "KeyboardMatrixController.h"
#include "BatteryLevel.h"
#include "WatchDog.h"
#include "keymap.h"
#include "config.h"
RawSerial serial(USBTX, USBRX);
static I2C i2c(I2C_SDA0, I2C_SCL0);
static KeyboardMatrixController keyboardMatrixController(i2c);
static Keymap keymap;
// Interrupt from MCP23017
// (pulled-up and two MCP23017 is configured with open drain INT)
static InterruptIn keyboardInterruptIn(P0_5);
#define PIN_STATUS_LED P0_4
static DigitalOut statusLed(PIN_STATUS_LED, 0);
// timeout for status led and wakeup from sleep
static Timeout timeout;
// ROWS=8
// COLS=16
// 列ごとに1バイトにパックしてキーの状態を保持する
static uint8_t keysA[COLS];
static uint8_t keysB[COLS];
static bool state = 0;
#define is_pressed(keys, row, col) (!!(keys[col] & (1<<row)))
// delay for interrupt
static volatile int8_t pollCount = 50;
static void keyboardInterrupt() {
// just for wakeup
pollCount = 25;
}
static void powerOff() {
DEBUG_PRINTF("power off\r\n");
NRF_POWER->SYSTEMOFF = 1;
}
static bool updateStatudLedEnabled = false;
static void updateStatusLed() {
switch (HIDController::status()) {
case TIMEOUT:
case DISCONNECTED:
case CONNECTED:
statusLed = 0;
updateStatudLedEnabled = false;
return;
case ADVERTISING:
case CONNECTING:
statusLed = !statusLed;
updateStatudLedEnabled = true;
timeout.attach(updateStatusLed, statusLed ? 0.1 : 0.5);
break;
}
}
static volatile bool keyIntervalInterrupt = false;
static void wakeupKeyIntervalSleep() {
keyIntervalInterrupt = true;
}
int main(void) {
{
const uint32_t reason = NRF_POWER->RESETREAS;
NRF_POWER->RESETREAS = 0xffffffff; // clear reason
// reset cause should be shown everytime
serial.printf("init [%x] sp:%x %x\r\n", reason, GET_SP(), *((uint32_t*)0));
}
{
const float battery = BatteryLevel::readBatteryVoltage();
if (battery < BatteryLevel::BATTERY_LOW) {
powerOff();
}
}
// Enable Pin-reset on DEBUG mode
// This makes possiable booting without normal mode easily.
NRF_POWER->RESET = 1;
// Disable Internal DC/DC step down converter surely
NRF_POWER->DCDCEN = 0;
// mbed's Serial of TARGET_RBLAB_BLENANO sucks
// DO NOT CONNECT RTS/CTS WITHOUT PRIOR CONSENT!
NRF_UART0->PSELRTS = 0xFFFFFFFFUL;
NRF_UART0->PSELCTS = 0xFFFFFFFFUL;
// Set LED Pin as HIGH Current mode
NRF_GPIO->PIN_CNF[PIN_STATUS_LED] =
(NRF_GPIO->PIN_CNF[PIN_STATUS_LED] & ~GPIO_PIN_CNF_DRIVE_Msk) |
(GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos);
// Unsed pins.
// Set as PullUp for power consumption
// Use GPIO registers directly for saving ram
// Pad pinouts
/*
DigitalIn unused_p0_7(P0_7, PullUp);
DigitalIn unused_p0_6(P0_6, PullUp);
DigitalIn unused_p0_15(P0_15, PullUp);
DigitalIn unused_p0_29(P0_29, PullUp);
DigitalIn unused_p0_28(P0_28, PullUp);
*/
NRF_GPIO->PIN_CNF[P0_7] =
(GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) |
(GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
(GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) |
(GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
(GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
NRF_GPIO->PIN_CNF[P0_6] =
(GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) |
(GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
(GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) |
(GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
(GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
NRF_GPIO->PIN_CNF[P0_15] =
(GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) |
(GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
(GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) |
(GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
(GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
NRF_GPIO->PIN_CNF[P0_29] =
(GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) |
(GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
(GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) |
(GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
(GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
NRF_GPIO->PIN_CNF[P0_28] =
(GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) |
(GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
(GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) |
(GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
(GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
/*
DigitalIn unused_p0_19(P0_19, PullUp); // This is on board LED which connected to VDD
DigitalIn unused_p0_11(P0_11, PullUp); // RXD
*/
NRF_GPIO->PIN_CNF[P0_19] = // This is on board LED which connected to VDD
(GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) |
(GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
(GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) |
(GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
(GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
NRF_GPIO->PIN_CNF[P0_11] = // RXD
(GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) |
(GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
(GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) |
(GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
(GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
WatchDog::init();
// only 100kHz/250khz/400khz
i2c.frequency(250000);
keyboardInterruptIn.mode(PullUp);
keyboardInterruptIn.fall(keyboardInterrupt);
keyboardMatrixController.init();
pollCount = 10;
HIDController::init();
// STOP UART RX for power consumption
NRF_UART0->TASKS_STOPRX = 1;
// Disable TWI by default.
NRF_TWI0->ENABLE = TWI_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos;
while (1) {
WatchDog::reload();
if (pollCount > 0) {
DEBUG_PRINTF("scan keys\r\n");
while (pollCount-- > 0) {
WatchDog::reload();
uint8_t (&keysCurr)[COLS] = state ? keysA : keysB;
uint8_t (&keysPrev)[COLS] = state ? keysB : keysA;
NRF_TWI0->ENABLE = TWI_ENABLE_ENABLE_Enabled << TWI_ENABLE_ENABLE_Pos;
keyboardMatrixController.scanKeyboard(keysCurr);
NRF_TWI0->ENABLE = TWI_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos;
bool queue = false;
for (int col = 0; col < COLS; col++) {
const uint8_t changed = keysPrev[col] ^ keysCurr[col];
if (changed) queue = true;
for (int row = 0; row < ROWS; row++) {
if (changed & (1<<row)) {
bool pressed = keysCurr[col] & (1<<row);
// DEBUG_KEYEVENT("changed: col=%d, row=%d / pressed=%d\r\n", col, row, pressed);
keymap.execute(row, col, pressed);
}
}
}
state = !state;
if (HIDController::status() == DISCONNECTED ||
HIDController::status() == TIMEOUT) {
if (
is_pressed(keysCurr, 0, 0) && // left top 1
is_pressed(keysCurr, 1, 0) && // left top 2
is_pressed(keysCurr, 0, 15) // right top
) {
DEBUG_PRINTF("re-init connection\r\n");
HIDController::initializeConnection(true);
} else {
if (HIDController::status() == TIMEOUT) {
DEBUG_PRINTF("wakeup\r\n");
HIDController::initializeConnection(false);
}
}
}
if (queue) HIDController::queueCurrentReportData();
// use timer to use wait 5ms
timeout.detach();
keyIntervalInterrupt = false;
timeout.attach_us(wakeupKeyIntervalSleep, 5000);
while (!keyIntervalInterrupt) HIDController::waitForEvent();
}
} else {
if (!updateStatudLedEnabled) updateStatusLed();
{
const float batteryVoltage = BatteryLevel::readBatteryVoltage();
const uint8_t batteryPercentage = BatteryLevel::readBatteryPercentage(batteryVoltage);
const bool isLowBattery = batteryVoltage < BatteryLevel::BATTERY_LOW;
DEBUG_PRINTF("%d%% [%d:%s] %x %s\r\n",
batteryPercentage,
HIDController::status(),
HIDController::statusString(),
GET_SP(),
isLowBattery ? "LOWBAT" : "WFE"
);
HIDController::updateBatteryLevel(batteryPercentage, batteryVoltage * 1000);
if (isLowBattery) {
powerOff();
}
}
if (HIDController::status() == DISCONNECTED) {
HIDController::initializeConnection(false);
}
HIDController::waitForEvent();
}
}
}