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-07-21
- Revision:
- 5:65d4e94735b6
- Parent:
- 4:54cb552e50c4
- Child:
- 6:f1c3ea8bc850
File content as of revision 5:65d4e94735b6:
/* mbed Microcontroller Library
* Copyright (c) 2015 ARM Limited
*
* 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 "mbed.h"
#include "BLE.h"
#include "KeyboardService.h"
#include "BatteryService.h"
#include "DeviceInformationService.h"
#include "mcp23017.h"
#include "keymap.h"
static const char MODEL_NAME[] = "keyboard";
static const char SERIAL_NUMBER[] = "X00000";
static const char HARDWARE_REVISION[] = "0.1";
static const char FIRMWARE_REVISION[] = "0.1";
static const char SOFTWARE_REVISION[] = "0.0";
static const uint8_t DEVICE_NAME[] = "my keyboard";
static const uint8_t SHORT_DEVICE_NAME[] = "kbd1";
static const bool ENABLE_BONDING = true;
static const bool REQUIRE_MITM = true;
static const uint8_t PASSKEY[6] = {'1','2','3','4','5','6'}; // must be 6-digits number
static const uint16_t uuid16_list[] = {
GattService::UUID_HUMAN_INTERFACE_DEVICE_SERVICE,
GattService::UUID_DEVICE_INFORMATION_SERVICE,
GattService::UUID_BATTERY_SERVICE
};
KeyboardService* keyboardService;
BatteryService* batteryService;
DeviceInformationService* deviceInformationService;
void updateBatteryLevel() {
if (!batteryService) return;
static const float BATTERY_MAX = 2.4;
static const float REFERNECE = 1.2;
static const float PRESCALE = 3;
NRF_ADC->ENABLE = ADC_ENABLE_ENABLE_Enabled;
// Use internal 1.2V reference for batteryInput
// 1/3 pre-scaled input and 1.2V internal band gap reference
// ref. mbed-src/targets/hal/TARGET_NORDIC/TARGET_MCU_NRF51822/analogin_api.c
NRF_ADC->CONFIG =
(ADC_CONFIG_RES_10bit << ADC_CONFIG_RES_Pos) |
// Use VDD 1/3 for input
(ADC_CONFIG_INPSEL_SupplyOneThirdPrescaling << ADC_CONFIG_INPSEL_Pos) |
// Use internal band gap for reference
(ADC_CONFIG_REFSEL_VBG << ADC_CONFIG_REFSEL_Pos) |
(ADC_CONFIG_EXTREFSEL_None << ADC_CONFIG_EXTREFSEL_Pos);
// Start ADC
NRF_ADC->TASKS_START = 1;
while (((NRF_ADC->BUSY & ADC_BUSY_BUSY_Msk) >> ADC_BUSY_BUSY_Pos) == ADC_BUSY_BUSY_Busy) {
// busy loop
}
// Read ADC result
uint16_t raw10bit = static_cast<uint16_t>(NRF_ADC->RESULT);
float ratio = raw10bit / static_cast<float>(1<<10);
float batteryVoltage = ratio * (REFERNECE * PRESCALE);
float percentage = (batteryVoltage / BATTERY_MAX) * 100;
if (percentage > 100) {
percentage = 100;
}
printf("updateBatteryLevel %f V : %d/100\r\n", batteryVoltage, static_cast<uint8_t>(percentage));
batteryService->updateBatteryLevel(static_cast<uint8_t>(percentage));
}
static void onConnect(const Gap::ConnectionCallbackParams_t *params) {
printf("onConnect\r\n");
}
static void onDisconnect(const Gap::DisconnectionCallbackParams_t *params) {
printf("onDisconnect\r\n");
BLE::Instance(BLE::DEFAULT_INSTANCE).gap().startAdvertising();
}
static void onTimeout(const Gap::TimeoutSource_t source) {
printf("onTimeout\r\n");
BLE::Instance(BLE::DEFAULT_INSTANCE).gap().startAdvertising();
}
static void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::Passkey_t passkey) {
printf("Input passKey: ");
for (unsigned i = 0; i < Gap::ADDR_LEN; i++) {
printf("%c", passkey[i]);
}
printf("\r\n");
}
static void securitySetupCompletedCallback(Gap::Handle_t handle, SecurityManager::SecurityCompletionStatus_t status) {
if (status == SecurityManager::SEC_STATUS_SUCCESS) {
printf("Security success %d\r\n", status);
} else {
printf("Security failed %d\r\n", status);
}
}
static void securitySetupInitiatedCallback(Gap::Handle_t, bool allowBonding, bool requireMITM, SecurityManager::SecurityIOCapabilities_t iocaps) {
printf("Security setup initiated\r\n");
}
static void bleInitComplete(BLE::InitializationCompleteCallbackContext *params) {
// https://developer.mbed.org/compiler/#nav:/keyboard/BLE_API/ble/blecommon.h;
ble_error_t error;
BLE &ble = params->ble;
error = params->error;
if (error != BLE_ERROR_NONE) {
printf("error on ble.init() \r\n");
goto return_error;
}
ble.gap().onDisconnection(onDisconnect);
ble.gap().onConnection(onConnect);
ble.gap().onTimeout(onTimeout);
printf("setup ble security manager\r\n");
ble.securityManager().onSecuritySetupInitiated(securitySetupInitiatedCallback);
ble.securityManager().onPasskeyDisplay(passkeyDisplayCallback);
ble.securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback);
// bonding with hard-coded passkey.
error = ble.securityManager().init(ENABLE_BONDING, REQUIRE_MITM, SecurityManager::IO_CAPS_DISPLAY_ONLY, PASSKEY);
if (error != BLE_ERROR_NONE) {
printf("error on ble.securityManager().init()");
goto return_error;
}
keyboardService = new KeyboardService(ble);
deviceInformationService = new DeviceInformationService(ble, "lowreal.net", MODEL_NAME, SERIAL_NUMBER, HARDWARE_REVISION, FIRMWARE_REVISION, SOFTWARE_REVISION);
batteryService = new BatteryService(ble, 100);
updateBatteryLevel();
printf("general setup\r\n");
error = ble.gap().accumulateAdvertisingPayload(
GapAdvertisingData::BREDR_NOT_SUPPORTED |
GapAdvertisingData::LE_GENERAL_DISCOVERABLE
);
if (error != BLE_ERROR_NONE) goto return_error;
printf("set COMPLETE_LIST_16BIT_SERVICE_IDS\r\n");
error = ble.gap().accumulateAdvertisingPayload(
GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS,
(uint8_t*)uuid16_list, sizeof(uuid16_list)
);
if (error != BLE_ERROR_NONE) goto return_error;
printf("set advertising\r\n");
// see 5.1.2: HID over GATT Specification (pg. 25)
ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
printf("set advertising interval\r\n");
ble.gap().setAdvertisingInterval(50);
// XXX
// ble.gap().setAdvertisingTimeout(0);
printf("set keyboard\r\n");
error = ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::KEYBOARD);
if (error != BLE_ERROR_NONE) goto return_error;
printf("set complete local name\r\n");
error = ble.gap().accumulateAdvertisingPayload(
GapAdvertisingData::COMPLETE_LOCAL_NAME,
DEVICE_NAME, sizeof(DEVICE_NAME)
);
if (error != BLE_ERROR_NONE) goto return_error;
printf("set device name\r\n");
error = ble.gap().setDeviceName(DEVICE_NAME);
if (error != BLE_ERROR_NONE) goto return_error;
// ble.gap().setTxPower(-12);
printf("advertising\r\n");
error = ble.gap().startAdvertising();
if (error != BLE_ERROR_NONE) goto return_error;
return;
return_error:
printf("error with %d\r\n", error);
return;
}
class KeyboardMatrixController {
I2C& i2c;
MCP23017 gpio1;
MCP23017 gpio2;
static const uint8_t GPIO1_SLAVE_ADDRESS = 0b0100000;
static const uint8_t GPIO2_SLAVE_ADDRESS = 0b0100001;
/**
* COL=GPIOA (output normaly positive)
* ROW=GPIOB (input pulled-up)
*/
int setupGpio(MCP23017& gpio) {
int ok;
ok = gpio.write8(
MCP23017::IOCON,
0<<MCP23017::BANK |
1<<MCP23017::MIRROR |
1<<MCP23017::SEQOP |
0<<MCP23017::DISSLW |
1<<MCP23017::ODR // int pin is open drain
);
// IODIR
// 1: input
// 0: output
ok = gpio.write16(
MCP23017::IODIRA,
0b0000000011111111
);
// INPUT POLARITY
// 1: inverse polarity
// 0: raw
ok = gpio.write8(
MCP23017::IPOLB,
0b11111111
);
// INTERRUPT-ON-CHANGE Enable
ok = gpio.write8(
MCP23017::GPINTENB,
0b11111111
);
// INTERRUPT-ON-CHANGE Control
// 1: compared with DEFVAL
// 0: compared to previous value
ok = gpio.write8(
MCP23017::INTCONB,
0b00000000
);
// PULL-UP (for input pin)
// 1: pull-up enabled
// 0: pull-up disabled
ok = gpio.write8(
MCP23017::GPPUB,
0b11111111
);
ok = gpio1.write8(
MCP23017::GPIOA,
0b00000000
);
return ok;
}
public:
KeyboardMatrixController(I2C& _i2c) :
i2c(_i2c),
gpio1(i2c, GPIO1_SLAVE_ADDRESS),
gpio2(i2c, GPIO2_SLAVE_ADDRESS)
{
}
void init() {
setupGpio(gpio1);
// setupGpio(gpio2);
}
void scanKeyboard(uint8_t* keys) {
int ok;
// Disable interrupt
ok = gpio1.write8(
MCP23017::GPINTENB,
0b00000000
);
for (int i = 0; i < 8; i++) {
ok = gpio1.write8(
MCP23017::GPIOA,
~(1<<i)
);
keys[i] = gpio1.read8(MCP23017::GPIOB, ok);
}
// set all output to negative for interrupt
ok = gpio1.write8(
MCP23017::GPIOA,
0b00000000
);
// Enable interrupt
ok = gpio1.write8(
MCP23017::GPINTENB,
0b11111111
);
// Clear interrupt
gpio1.read8(MCP23017::GPIOB, ok);
// TODO gpio2
}
};
I2C i2c(I2C_SDA0, I2C_SCL0);
KeyboardMatrixController keyboardMatrixController(i2c);
Keymap keymap(keyboardService);
InterruptIn buttonInt(P0_5);
DigitalIn buttonIntIn(P0_5);
// ROWS=8
// COLS=16
// 列ごとに1バイトにパックしてキーの状態を保持する
static uint8_t keysA[COLS];
static uint8_t keysB[COLS];
static bool state = 0;
void buttonIntCallback() {
printf("pinChanged!!!\r\n");
uint8_t (&keysCurr)[COLS] = state ? keysA : keysB;
uint8_t (&keysPrev)[COLS] = state ? keysB : keysA;
printf("scanKeyboard\r\n");
keyboardMatrixController.scanKeyboard(keysCurr);
for (int col = 0; col < COLS; col++) {
uint8_t changed = keysPrev[col] ^ keysCurr[col];
if (changed) printf("changed: %x\r\n", changed);
for (int row = 0; row < ROWS; row++) {
if (changed & (1<<row)) {
bool pressed = keysCurr[col] & (1<<row);
printf("changed: col=%d, row=%d / pressed=%d\r\n", col, row, pressed);
// keymap.execute(col, row, pressed);
}
}
}
state = !state;
}
int main(void) {
printf("init\r\n");
// mbed's Serial of TARGET_RBLAB_BLENANO sucks
// DO NOT CONNECT RTS/CTS AUTOMATICALY!
NRF_UART0->PSELRTS = 0xFFFFFFFFUL;
NRF_UART0->PSELCTS = 0xFFFFFFFFUL;
int ok;
// 100kHz
i2c.frequency(100000);
buttonInt.mode(PullUp);
buttonInt.fall(buttonIntCallback);
keyboardMatrixController.init();
buttonIntCallback();
while (1) {
wait_ms(1000);
}
return;
// https://github.com/jpbrucker/BLE_HID/blob/master/examples/examples_common.cpp
printf("ble.init\r\n");
BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
ble.init(bleInitComplete);
while (!ble.hasInitialized()) { }
printf("ble.hasIntialized\r\n");
while (1) {
ble.waitForEvent();
}
}