mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 00:37:25 +01:00
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:
5
apps/hwtest/lowlevel/Makefile
Normal file
5
apps/hwtest/lowlevel/Makefile
Normal file
@@ -0,0 +1,5 @@
|
||||
objs += $(addprefix apps/hwtest/lowlevel/,\
|
||||
lowlevel.o \
|
||||
)
|
||||
|
||||
apps/hwtest/lowlevel/lowlevel.o: SFLAGS += -O3
|
||||
195
apps/hwtest/lowlevel/lowlevel.cpp
Normal file
195
apps/hwtest/lowlevel/lowlevel.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
15
ion/include/ion/console.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
81
ion/src/device/console.cpp
Normal file
81
ion/src/device/console.cpp
Normal 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
31
ion/src/device/console.h
Normal 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
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
35
ion/src/device/regs/spi.h
Normal 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
|
||||
50
ion/src/device/regs/usart.h
Normal file
50
ion/src/device/regs/usart.h
Normal 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
|
||||
Reference in New Issue
Block a user