Merge changes I4d59900f,Ifd3d4c6b,I485bd67f,Ib2dd1d91,I29085da3, ...

* changes:
  [ion/device] Fix building console.o
  [apps/hwtest] Add CHARGE and KEYBOARD commands to lowlevel test
  [apps/hwtest] Proper MCU_SERIAL command
  [ion] Add a Ion::serialNumber function
  [apps/hwtest] Add ADC and BACKLIGHT commands to the lowlevel test
  [ion/device] Use the proper ADC channel
  [ion] Add a console facility
  [apps/hwtest] Blink the LED from the lowlevel test
  [apps/hwtest] The lowlevel test uses the UART
  [ion/device] Add USART register
  [apps] Add a lowlevel test app
  [ion/device] Add SPI registers
This commit is contained in:
Romain Goyet
2017-03-07 10:15:55 +01:00
committed by Gerrit
13 changed files with 449 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
objs += $(addprefix apps/hwtest/lowlevel/,\
lowlevel.o \
)
apps/hwtest/lowlevel/lowlevel.o: SFLAGS += -O3

View File

@@ -0,0 +1,195 @@
#include <stddef.h>
#include <ion.h>
#include <poincare.h>
typedef void (*CommandFunction)(const char * input);
void command_ping(const char * input);
void command_mcu_serial(const char * input);
class CommandHandler {
public:
constexpr CommandHandler(const char * name, CommandFunction function) :
m_name(name), m_function(function) {}
bool valid() const;
bool handle(const char * command) const;
private:
bool matches(const char * command) const;
const char * m_name;
CommandFunction m_function;
};
bool CommandHandler::valid() const {
return (m_name != nullptr && m_function != nullptr);
}
bool CommandHandler::handle(const char * command) const {
if (matches(command)) {
size_t nameLength = strlen(m_name);
if (command[nameLength] == '=') {
m_function(command+nameLength+1); // Skip the "Equal character"
} else {
m_function(nullptr);
}
return true;
}
return false;
}
bool CommandHandler::matches(const char * command) const {
const char * c = command;
const char * n = m_name;
while (true) {
if (*n == NULL) {
if (*c == NULL || *c == '=') {
return true;
}
}
if (*c != *n) {
return false;
}
c++;
n++;
}
}
class CommandList {
public:
constexpr CommandList(const CommandHandler * handlers) : m_handlers(handlers) {}
void dispatch(const char * command) const;
private:
const CommandHandler * m_handlers;
};
void CommandList::dispatch(const char * command) const {
const CommandHandler * handler = m_handlers;
while (handler->valid()) {
if (handler->handle(command)) {
return;
}
handler++;
}
Ion::Console::writeLine("NOT_FOUND");
}
static const char * sSyntaxError = "SYNTAX_ERROR";
void command_ping(const char * input) {
if (input != nullptr) {
Ion::Console::writeLine(sSyntaxError);
return;
}
Ion::Console::writeLine("PONG");
}
void command_mcu_serial(const char * input) {
if (input != nullptr) {
Ion::Console::writeLine(sSyntaxError);
return;
}
char response[11+24+1] = {'M', 'C', 'U', '_', 'S', 'E', 'R', 'I', 'A', 'L', '=', 0};
strlcpy(response+11, Ion::serialNumber(), 25);
Ion::Console::writeLine(response);
}
static inline int8_t hexChar(char c) {
if (c >= '0' && c <= '9') {
return (c - '0');
}
if (c >= 'A' && c <= 'F') {
return (c - 'A') + 0xA;
}
return -1;
}
static inline bool isHex(char c) { return hexChar(c) >= 0; }
static inline uint32_t hexNumber(const char * s) {
uint32_t result = 0;
while (*s != NULL) {
result = (result << 4) | hexChar(*s++);
}
return result;
}
void command_led(const char * input) {
// Input must be of the form "0xAABBCC"
if (input == nullptr || input[0] != '0' || input[1] != 'x' || !isHex(input[2]) ||!isHex(input[3]) || !isHex(input[4]) || !isHex(input[5]) || !isHex(input[6]) || !isHex(input[7]) || input[8] != NULL) {
Ion::Console::writeLine(sSyntaxError);
return;
}
uint32_t hexColor = hexNumber(input+2);
KDColor ledColor = KDColor::RGB24(hexColor);
Ion::LED::setColor(ledColor);
Ion::Console::writeLine("OK");
}
void command_backlight(const char * input) {
// Input must be of the form "0xAA"
if (input == nullptr || input[0] != '0' || input[1] != 'x' || !isHex(input[2]) ||!isHex(input[3]) || input[4] != NULL) {
Ion::Console::writeLine(sSyntaxError);
return;
}
uint32_t brightness = hexNumber(input+2);
Ion::Backlight::setBrightness(brightness);
Ion::Console::writeLine("OK");
}
void command_adc(const char * input) {
if (input != nullptr) {
Ion::Console::writeLine(sSyntaxError);
return;
}
float result = Ion::Battery::voltage();
constexpr int precision = 8;
constexpr int bufferSize = Poincare::Complex::bufferSizeForFloatsWithPrecision(precision);
char responseBuffer[bufferSize+4] = {'A', 'D', 'C', '='}; // ADC=
Poincare::Complex::convertFloatToText(result, responseBuffer+4, bufferSize, precision);
Ion::Console::writeLine(responseBuffer);
}
void command_charge(const char * input) {
if (input != nullptr) {
Ion::Console::writeLine(sSyntaxError);
return;
}
if (Ion::Battery::isCharging()) {
Ion::Console::writeLine("CHARGE=ON");
} else {
Ion::Console::writeLine("CHARGE=OFF");
}
}
void command_keyboard(const char * input) {
if (input != nullptr) {
Ion::Console::writeLine(sSyntaxError);
return;
}
char result[9+Ion::Keyboard::NumberOfKeys+1] = { 'K', 'E', 'Y', 'B', 'O', 'A', 'R', 'D', '=' };
for (uint8_t i=0; i<Ion::Keyboard::NumberOfKeys; i++) {
result[9+i] = Ion::Keyboard::keyDown((Ion::Keyboard::Key)i) ? '1' : '0';
}
result[9+Ion::Keyboard::NumberOfKeys] = 0;
Ion::Console::writeLine(result);
}
constexpr CommandHandler handles[] = {
CommandHandler("PING", command_ping),
CommandHandler("MCU_SERIAL", command_mcu_serial),
CommandHandler("LED", command_led),
CommandHandler("BACKLIGHT", command_backlight),
CommandHandler("ADC", command_adc),
CommandHandler("CHARGE", command_charge),
CommandHandler("KEYBOARD", command_keyboard),
CommandHandler(nullptr, nullptr)
};
constexpr const CommandList sCommandList = CommandList(handles);
constexpr int kMaxCommandLength = 255;
void ion_app() {
char command[kMaxCommandLength];
while (true) {
Ion::Console::readLine(command, kMaxCommandLength);
sCommandList.dispatch(command);
}
}

View File

@@ -4,6 +4,7 @@
#include <ion/backlight.h>
#include <ion/battery.h>
#include <ion/charset.h>
#include <ion/console.h>
#include <ion/display.h>
#include <ion/events.h>
#include <ion/keyboard.h>
@@ -26,6 +27,8 @@ namespace Ion {
void msleep(long ms);
void usleep(long us);
const char * serialNumber();
/* CAUTION: This is a complete reset! */
void reset();

15
ion/include/ion/console.h Normal file
View File

@@ -0,0 +1,15 @@
#ifndef ION_CONSOLE_H
#define ION_CONSOLE_H
namespace Ion {
namespace Console {
// The lines are NULL-terminated
void writeLine(const char * line);
void readLine(char * line, int maxLineLength);
}
}
#endif

View File

@@ -7,6 +7,7 @@ objs += $(addprefix ion/src/shared/, \
objs += $(addprefix ion/src/device/, \
backlight.o \
battery.o\
console.o \
device.o\
display.o\
keyboard.o\
@@ -25,6 +26,7 @@ objs += $(addprefix ion/src/device/, \
ifneq ($(DEBUG),1)
ifneq ($(COMPILER),llvm)
ion/src/device/led.o: SFLAGS+=-O3
ion/src/device/console.o: SFLAGS+=-O3
ion/src/device/display.o: SFLAGS+=-O3
endif
endif

View File

@@ -50,6 +50,11 @@ void init() {
// Step 2 - Enable the ADC
RCC.APB2ENR()->setADC1EN(true);
ADC.CR2()->setADON(true);
// Configure the ADC channel
ADC.SQR1()->setL(0); // Always sample the same channel
ADC.SQR3()->setSQ1(ADCChannel);
ADC.SMPR()->setSamplingTime(ADCChannel, ADC::SMPR::SamplingTime::Cycles480); // Use the max sampling time
}
void shutdown() {

View File

@@ -0,0 +1,81 @@
#include <ion.h>
#include "console.h"
/* This file implements a serial console.
* We use a 9600 8N1 serial port */
namespace Ion {
namespace Console {
void writeLine(const char * line) {
while (*line != NULL) {
Device::sendChar(*line++);
}
Device::sendChar('\r');
Device::sendChar('\n');
}
void readLine(char * line, int maxLineLength) {
if (maxLineLength <= 0) {
return;
}
char * cursor = line;
char * last = line+maxLineLength-1;
while (true) {
*cursor = Device::recvChar();
if (*cursor == '\r' || cursor == last) {
*cursor = 0;
return;
}
cursor++;
}
}
}
}
namespace Ion {
namespace Console {
namespace Device {
void init() {
RCC.APB2ENR()->setUSART1EN(true);
GPIOB.MODER()->setMode(3, GPIO::MODER::Mode::AlternateFunction);
GPIOB.MODER()->setMode(6, GPIO::MODER::Mode::AlternateFunction);
GPIOB.AFR()->setAlternateFunction(3, GPIO::AFR::AlternateFunction::AF7);
GPIOB.AFR()->setAlternateFunction(6, GPIO::AFR::AlternateFunction::AF7);
UARTPort.CR1()->setUE(true);
UARTPort.CR1()->setTE(true);
UARTPort.CR1()->setRE(true);
// Set the Baud rate
// base clock = 16 MHz
// Baud rate = fAPB2/(16*USARTDIV)
// USARTDIV = 104.16667
//
// DIV_Fraction = 16*0.16666667
// = 2.666667 -> 3
// DIV_Mantissa = 104 = 0x68
// USART_BRR = 0x683
UARTPort.BRR()->setDIV_FRAC(3);
UARTPort.BRR()->setDIV_MANTISSA(104);
}
char recvChar() {
while (UARTPort.SR()->getRXNE() == 0) {
}
return (char)UARTPort.DR()->get();
}
void sendChar(char c) {
while (UARTPort.SR()->getTXE() == 0) {
}
UARTPort.DR()->set(c);
}
}
}
}

31
ion/src/device/console.h Normal file
View File

@@ -0,0 +1,31 @@
#ifndef ION_DEVICE_CONSOLE_H
#define ION_DEVICE_CONSOLE_H
#include <ion/console.h>
#include "regs/regs.h"
namespace Ion {
namespace Console {
namespace Device {
/* Pin | Role | Mode
* -----+-------------------+--------------------
* PC11 | UART1 RX | Alternate Function
* PD8 | UART1 TX | Alternate Function
*/
void init();
void shutdown();
constexpr USART UARTPort = USART(1);
constexpr static GPIOPin RxPin = GPIOPin(GPIOB, 14);
constexpr static GPIOPin TxPin = GPIOPin(GPIOE, 9);
void sendChar(char c);
char recvChar();
}
}
}
#endif

View File

@@ -10,6 +10,7 @@ extern "C" {
#include "battery.h"
#include "sd_card.h"
#include "backlight.h"
#include "console.h"
#define USE_SD_CARD 0
@@ -61,6 +62,27 @@ void Ion::reset() {
CM4.AIRCR()->requestReset();
}
static inline char hex(uint8_t d) {
if (d > 9) {
return 'A'+d;
}
return '0'+d;
}
const char * Ion::serialNumber() {
static char serialNumber[25] = {0};
if (serialNumber[0] == 0) {
uint8_t * rawUniqueID = (uint8_t *)0x1FFF7A10;
for (int i=0; i<24; i++) {
uint8_t d = *rawUniqueID++;
serialNumber[2*i] = hex(d & 0xF);
serialNumber[2*i+1] = hex(d >> 4);
}
serialNumber[24] = 0;
}
return serialNumber;
}
// Private Ion::Device methods
namespace Ion {
@@ -101,6 +123,7 @@ void initPeripherals() {
#if USE_SD_CARD
SDCard::Device::init();
#endif
Console::Device::init();
}
void shutdownPeripherals() {

View File

@@ -74,6 +74,7 @@ public:
class APB1ENR : Register32 {
public:
REGS_BOOL_FIELD(TIM3EN, 1);
REGS_BOOL_FIELD(SPI3EN, 15);
REGS_BOOL_FIELD(PWREN, 28);
};
@@ -81,6 +82,7 @@ public:
public:
using Register32::Register32;
REGS_BOOL_FIELD(TIM1EN, 0);
REGS_BOOL_FIELD(USART1EN, 4);
REGS_BOOL_FIELD(ADC1EN, 8);
REGS_BOOL_FIELD(SDIOEN, 11);
REGS_BOOL_FIELD(SYSCFGEN, 14);

View File

@@ -13,7 +13,9 @@
#include "rcc.h"
#include "rng.h"
#include "sdio.h"
#include "spi.h"
#include "syscfg.h"
#include "tim.h"
#include "usart.h"
#endif

35
ion/src/device/regs/spi.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef REGS_SPI_H
#define REGS_SPI_H
#include "register.h"
class SPI {
public:
class CR1 : Register16 {
public:
REGS_BOOL_FIELD(SPE, 6);
REGS_BOOL_FIELD(LSBFIRST, 7);
REGS_BOOL_FIELD(SSI, 8);
REGS_BOOL_FIELD(SSM, 9);
};
class SR : Register16 {
public:
REGS_BOOL_FIELD(RXNE, 0);
REGS_BOOL_FIELD(TXE, 1);
};
class DR : public Register16 {
};
constexpr SPI(int i) : m_index(i) {}
constexpr operator int() const { return m_index; }
REGS_REGISTER_AT(CR1, 0x00);
REGS_REGISTER_AT(SR, 0x08);
REGS_REGISTER_AT(DR, 0x0C);
private:
constexpr uint32_t Base() const {
return ((uint32_t []){0x40013000, 0x40003800, 0x40003C00})[m_index-1];
};
int m_index;
};
#endif

View File

@@ -0,0 +1,50 @@
#ifndef REGS_USART_H
#define REGS_USART_H
#include "register.h"
class USART {
public:
class SR : Register32 {
public:
REGS_BOOL_FIELD(RXNE, 5);
REGS_BOOL_FIELD(TXE, 7);
};
class DR : Register32 {
public:
uint16_t get() volatile {
return (uint16_t)getBitRange(8, 0);
}
void set(uint16_t v) volatile {
setBitRange(8, 0, v);
}
};
class BRR : Register32 {
public:
REGS_FIELD(DIV_FRAC, uint8_t, 3, 0);
REGS_FIELD(DIV_MANTISSA, uint16_t, 15, 4);
};
class CR1 : Register32 {
public:
REGS_BOOL_FIELD(UE, 13);
REGS_BOOL_FIELD(TE, 3);
REGS_BOOL_FIELD(RE, 2);
};
constexpr USART(int i) : m_index(i) {}
constexpr operator int() const { return m_index; }
REGS_REGISTER_AT(SR, 0x00);
REGS_REGISTER_AT(DR, 0x04);
REGS_REGISTER_AT(BRR, 0x08);
REGS_REGISTER_AT(CR1, 0x0C);
private:
constexpr uint32_t Base() const {
return ((uint32_t []){0x40011000, 0x40004400, 0x40004800})[m_index-1];
};
int m_index;
};
#endif