ble nano hid over gatt
Dependencies: BLE_API mbed-dev nRF51822
main.cpp
- Committer:
- cho45
- Date:
- 2016-09-15
- Revision:
- 86:e0fab77e669d
- Parent:
- 77:d4be914fb87c
File content as of revision 86:e0fab77e669d:
/* 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; } static volatile bool shouldUpdateBatteryLevel = true; static void wakeupIgnoreLongSleep() { // just wakeup for reloading WDT shouldUpdateBatteryLevel = true; } void setupIO () { // 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); } 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\r\n", reason, GET_SP()); } float batteryVoltage = BatteryLevel::readBatteryVoltage(); if (batteryVoltage < 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; setupIO(); 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; { // init battery level const uint8_t batteryPercentage = BatteryLevel::readBatteryPercentage(batteryVoltage); HIDController::updateBatteryLevel(batteryPercentage, batteryVoltage * 1000); } while (1) { if (pollCount > 0) { DEBUG_PRINTF("scan keys\r\n"); while (pollCount-- > 0) { 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(); if (shouldUpdateBatteryLevel) { shouldUpdateBatteryLevel = false; float batteryVoltage = BatteryLevel::readBatteryVoltage(); const uint8_t batteryPercentage = BatteryLevel::readBatteryPercentage(batteryVoltage); const bool isLowBattery = batteryVoltage < BatteryLevel::BATTERY_LOW; HIDController::updateBatteryLevel(batteryPercentage, batteryVoltage * 1000); if (isLowBattery) { DEBUG_PRINTF("LOWBAT %f %d%%", batteryVoltage, batteryPercentage); powerOff(); } } if (HIDController::status() == DISCONNECTED) { HIDController::initializeConnection(false); } DEBUG_PRINTF("WFE [%d:%s]\r\n", HIDController::status(), HIDController::statusString() ); if (!updateStatudLedEnabled) { timeout.detach(); timeout.attach(wakeupIgnoreLongSleep, 60); } HIDController::waitForEvent(); } } }