[ion/device] Sort files

This commit is contained in:
Romain Goyet
2019-02-05 11:23:55 +01:00
parent 2e17db53d8
commit 7618318a90
141 changed files with 1172 additions and 1070 deletions

View File

@@ -1,4 +1,4 @@
TOOLCHAIN ?= arm-gcc-m4f
MODEL ?= n0100
USE_LIBA = 1
EXE = elf
EPSILON_BOOT_PROMPT = update
@@ -7,3 +7,5 @@ EPSILON_DEVICE_BENCH ?= 1
SFLAGS += -DEPSILON_DEVICE_BENCH=$(EPSILON_DEVICE_BENCH)
python/port/port.o: CXXFLAGS += -DMP_PORT_USE_STACK_SYMBOLS=1
include build/platform.device.$(MODEL).mak

View File

@@ -0,0 +1 @@
TOOLCHAIN ?= arm-gcc-m4f

View File

@@ -1,6 +1,6 @@
include ion/src/device/boot/Makefile
include ion/src/device/bench/Makefile
include ion/src/device/usb/Makefile
# Makefile belows should augment $(ion_device_objs)
include ion/src/device/shared/Makefile
include ion/src/device/$(MODEL)/Makefile
ion/src/shared/platform_info.o: SFLAGS += -DHEADER_SECTION="__attribute__((section(\".header\")))"
@@ -12,27 +12,10 @@ objs += $(addprefix ion/src/shared/, \
# If you need to profile execution, you can replace events_keyboard with
# events_replay.o and dummy/events_modifier.o
ION_DEVICE_SFLAGS = -Iion/src/device/$(MODEL) -Iion/src/device/shared
objs += $(addprefix ion/src/device/, \
backlight.o \
battery.o\
base64.o\
console.o \
device.o\
display.o\
events.o\
external_flash.o\
flash.o\
keyboard.o\
led.o\
power.o\
sd_card.o\
stack.o\
swd.o \
timing.o \
usb.o \
wakeup.o \
)
$(ion_device_objs): SFLAGS += $(ION_DEVICE_SFLAGS)
objs += $(ion_device_objs)
# When using the register.h C++ file in production mode, we expect the compiler
# to completely inline all bit manipulations. For some reason, if we build using

View File

@@ -1,2 +0,0 @@
objs += $(addprefix ion/src/device/boot/, isr.o rt0.o)
LDSCRIPT = ion/src/device/boot/flash.ld

View File

@@ -1,36 +0,0 @@
#ifndef ION_DEVICE_H
#define ION_DEVICE_H
namespace Ion {
namespace Device {
void init();
void shutdown();
void initFPU();
#if 0
void initMPU();
#endif
void initSysTick();
void shutdownSysTick();
void coreReset();
void jumpReset();
void initPeripherals();
void shutdownPeripherals(bool keepLEDAwake = false);
void initClocks();
void shutdownClocks(bool keepLEDAwake = false);
/* The serial number is 96 bits long. That's equal to 16 digits in base 64. We
* expose a convenient "copySerialNumber" routine which can be called without
* using a static variable (and therefore without a .bss section). This is used
* in the RAM'ed DFU bootloader. */
constexpr static int SerialNumberLength = 16;
void copySerialNumber(char * buffer);
}
}
#endif

View File

@@ -0,0 +1,5 @@
ion_device_objs += $(addprefix ion/src/device/n0100/drivers/, \
board.o \
)
LDSCRIPT ?= ion/src/device/n0100/flash.ld

View File

@@ -1,75 +1,9 @@
#include "device.h"
#include "regs/regs.h"
extern "C" {
#include <assert.h>
}
#include <drivers/board.h>
#include <regs/regs.h>
#include <ion.h>
#include "led.h"
#include "display.h"
#include "keyboard.h"
#include "battery.h"
#include "sd_card.h"
#include "backlight.h"
#include "console.h"
#include "swd.h"
#include "usb.h"
#include "bench/bench.h"
#include "base64.h"
#include "external_flash.h"
#define USE_SD_CARD 0
extern "C" {
extern const void * _stack_end;
}
// Public Ion methods
uint32_t Ion::crc32(const uint32_t * data, size_t length) {
bool initialCRCEngineState = RCC.AHB1ENR()->getCRCEN();
RCC.AHB1ENR()->setCRCEN(true);
CRC.CR()->setRESET(true);
const uint32_t * end = data + length;
while (data < end) {
CRC.DR()->set(*data++);
}
uint32_t result = CRC.DR()->get();
RCC.AHB1ENR()->setCRCEN(initialCRCEngineState);
return result;
}
uint32_t Ion::random() {
bool initialRNGEngineState = RCC.AHB2ENR()->getRNGEN();
RCC.AHB2ENR()->setRNGEN(true);
RNG.CR()->setRNGEN(true);
while (RNG.SR()->getDRDY() == 0) {
}
uint32_t result = RNG.DR()->get();
RNG.CR()->setRNGEN(false);
RCC.AHB2ENR()->setRNGEN(initialRNGEngineState);
return result;
}
void Ion::Device::copySerialNumber(char * buffer) {
const unsigned char * rawUniqueID = (const unsigned char *)0x1FFF7A10;
Base64::encode(rawUniqueID, 12, buffer);
buffer[SerialNumberLength] = 0;
}
const char * Ion::serialNumber() {
static char serialNumber[Device::SerialNumberLength + 1] = {0};
if (serialNumber[0] == 0) {
Device::copySerialNumber(serialNumber);
}
return serialNumber;
}
const char * Ion::fccId() {
return "2ALWP-N0100";
}
@@ -78,65 +12,7 @@ const char * Ion::fccId() {
namespace Ion {
namespace Device {
void initFPU() {
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0553a/BABDBFBJ.html
CM4.CPACR()->setAccess(10, CM4::CPACR::Access::Full);
CM4.CPACR()->setAccess(11, CM4::CPACR::Access::Full);
// FIXME: The pipeline should be flushed at this point
}
#if 0
void initMPU() {
/* Region 0 reprensents the last 128 bytes of the stack: accessing this
* memory means we are really likely to overflow the stack very soon. */
MPU.RNR()->setREGION(0x00);
MPU.RBAR()->setADDR(&_stack_end);
MPU.RASR()->setSIZE(MPU::RASR::RegionSize::Bytes128);
MPU.RASR()->setENABLE(true);
MPU.RASR()->setAP(0x000); // Forbid access
MPU.CTRL()->setPRIVDEFENA(true);
MPU.CTRL()->setENABLE(true);
}
#endif
void initSysTick() {
// CPU clock is 96 MHz, and systick clock source is divided by 8
// To get 1 ms systick overflow we need to reset it to
// 96 000 000 (Hz) / 8 / 1 000 (ms/s) - 1 (because the counter resets *after* counting to 0)
CM4.SYST_RVR()->setRELOAD(11999);
CM4.SYST_CVR()->setCURRENT(0);
CM4.SYST_CSR()->setCLKSOURCE(CM4::SYST_CSR::CLKSOURCE::AHB_DIV8);
CM4.SYST_CSR()->setTICKINT(true);
CM4.SYST_CSR()->setENABLE(true);
}
void shutdownSysTick() {
CM4.SYST_CSR()->setENABLE(false);
CM4.SYST_CSR()->setTICKINT(false);
}
void coreReset() {
// Perform a full core reset
CM4.AIRCR()->requestReset();
}
void jumpReset() {
uint32_t * stackPointerAddress = reinterpret_cast<uint32_t *>(0x08000000);
uint32_t * resetHandlerAddress = reinterpret_cast<uint32_t *>(0x08000004);
/* Jump to the reset service routine after having reset the stack pointer.
* Both addresses are fetched from the base of the Flash memory, just like a
* real reset would. These operations should be made at once, otherwise the C
* compiler might emit some instructions that modify the stack inbetween. */
asm volatile (
"msr MSP, %[stackPointer] ; bx %[resetHandler]"
: :
[stackPointer] "r" (*stackPointerAddress),
[resetHandler] "r" (*resetHandlerAddress)
);
}
namespace Board {
void init() {
initClocks();
@@ -158,7 +34,7 @@ void init() {
}
#if EPSILON_DEVICE_BENCH
bool consolePeerConnectedOnBoot = Ion::Console::Device::peerConnected();
bool consolePeerConnectedOnBoot = Console::peerConnected();
#endif
initPeripherals();
@@ -170,44 +46,6 @@ void init() {
#endif
}
void shutdown() {
shutdownPeripherals();
shutdownClocks();
}
void initPeripherals() {
Display::Device::init();
Backlight::Device::init();
Keyboard::Device::init();
LED::Device::init();
Battery::Device::init();
USB::Device::init();
#if USE_SD_CARD
SDCard::Device::init();
#endif
Console::Device::init();
SWD::Device::init();
initSysTick();
ExternalFlash::Device::init();
}
void shutdownPeripherals(bool keepLEDAwake) {
shutdownSysTick();
SWD::Device::shutdown();
Console::Device::shutdown();
#if USE_SD_CARD
SDCard::Device::shutdown();
#endif
USB::Device::shutdown();
Battery::Device::shutdown();
if (!keepLEDAwake) {
LED::Device::shutdown();
}
Keyboard::Device::shutdown();
Backlight::Device::shutdown();
Display::Device::shutdown();
}
void initClocks() {
/* System clock
* Configure the CPU at 96 MHz, APB2 and USB at 48 MHz. */
@@ -325,3 +163,4 @@ void shutdownClocks(bool keepLEDAwake) {
}
}
}

View File

@@ -1,28 +1,23 @@
#ifndef ION_DEVICE_BACKLIGHT_H
#define ION_DEVICE_BACKLIGHT_H
#ifndef ION_DEVICE_N0100_CONFIG_BACKLIGHT_H
#define ION_DEVICE_N0100_CONFIG_BACKLIGHT_H
#include <ion/backlight.h>
namespace Ion {
namespace Backlight {
namespace Device {
#include <regs/regs.h>
/* Pin | Role | Mode | Function
* -----+-------------------+-----------------------+----------
* PC6 | Backlight Enable | Output |
*/
void init();
void shutdown();
void suspend();
void resume();
void setLevel(uint8_t level);
uint8_t level();
namespace Ion {
namespace Device {
namespace Backlight {
namespace Config {
void sendPulses(int n);
constexpr static GPIOPin BacklightPin = GPIOPin(GPIOC, 6);
}
}
}
}
#endif

View File

@@ -1,11 +1,7 @@
#ifndef ION_DEVICE_BATTERY_H
#define ION_DEVICE_BATTERY_H
#ifndef ION_DEVICE_N0100_CONFIG_BATTERY_H
#define ION_DEVICE_N0100_CONFIG_BATTERY_H
#include "regs/regs.h"
namespace Ion {
namespace Battery {
namespace Device {
#include <regs/regs.h>
/* Pin | Role | Mode | Function
* -----+-------------------+-----------------------+----------
@@ -13,23 +9,20 @@ namespace Device {
* PA1 | VBAT_SNS | Analog | ADC1_1
*/
void init();
void shutdown();
void initGPIO();
void initADC();
namespace Ion {
namespace Device {
namespace Battery {
namespace Config {
constexpr GPIO ChargingGPIO = GPIOA;
constexpr uint8_t ChargingPin = 0;
constexpr GPIO ADCGPIO = GPIOA;
constexpr uint8_t ADCPin = 1;
constexpr static GPIOPin ChargingPin = GPIOPin(GPIOA, 0);
constexpr static GPIOPin ADCPin = GPIOPin(GPIOA, 1);
constexpr uint8_t ADCChannel = 1;
constexpr float ADCReferenceVoltage = 2.8f;
constexpr float ADCDividerBridgeRatio = 2.0f;
}
}
}
}
#endif

View File

@@ -1,12 +1,7 @@
#ifndef ION_DEVICE_CONSOLE_H
#define ION_DEVICE_CONSOLE_H
#ifndef ION_DEVICE_N0100_CONFIG_CONSOLE_H
#define ION_DEVICE_N0100_CONFIG_CONSOLE_H
#include <ion/console.h>
#include "regs/regs.h"
namespace Ion {
namespace Console {
namespace Device {
#include <regs/regs.h>
/* Pin | Role | Mode
* -----+-------------------+--------------------
@@ -14,11 +9,12 @@ namespace Device {
* PD8 | UART3 TX | Alternate Function
*/
void init();
void shutdown();
bool peerConnected();
namespace Ion {
namespace Device {
namespace Console {
namespace Config {
constexpr USART UARTPort = USART(3);
constexpr static USART UARTPort = USART(3);
constexpr static GPIOPin RxPin = GPIOPin(GPIOC, 11);
constexpr static GPIOPin TxPin = GPIOPin(GPIOD, 8);
constexpr static GPIOPin Pins[] = { RxPin, TxPin };
@@ -26,5 +22,6 @@ constexpr static GPIOPin Pins[] = { RxPin, TxPin };
}
}
}
}
#endif

View File

@@ -1,16 +1,7 @@
#ifndef ION_DEVICE_DISPLAY_H
#define ION_DEVICE_DISPLAY_H
#ifndef ION_DEVICE_N0100_CONFIG_DISPLAY_H
#define ION_DEVICE_N0100_CONFIG_DISPLAY_H
#include <kandinsky/rect.h>
#include <kandinsky/color.h>
extern "C" {
#include <stddef.h>
}
#include "regs/regs.h"
namespace Ion {
namespace Display {
namespace Device {
#include <regs/regs.h>
/* Pin | Role | Mode | Function | Note
* -----+-------------------+-----------------------+----------|------
@@ -38,44 +29,11 @@ namespace Device {
* PE15 | LCD D12 | Alternate Function 12 | FSMC_D12 |
*/
void init();
void shutdown();
void initDMA();
void initGPIO();
void shutdownGPIO();
void initFSMC();
void shutdownFSMC();
void initPanel();
void shutdownPanel();
enum class Orientation {
Landscape = 0,
Portrait = 1
};
void setDrawingArea(KDRect r, Orientation o);
void waitForPendingDMAUploadCompletion();
void pushPixels(const KDColor * pixels, size_t numberOfPixels);
void pushColor(KDColor color, size_t numberOfPixels);
void pullPixels(KDColor * pixels, size_t numberOfPixels);
enum class Command : uint16_t {
Nop = 0x00,
Reset = 0x01,
SleepIn = 0x10,
SleepOut = 0x11,
DisplayOff = 0x28,
DisplayOn = 0x29,
ColumnAddressSet = 0x2A,
PageAddressSet = 0x2B,
MemoryWrite = 0x2C,
MemoryRead = 0x2E,
TearingEffectLineOn = 0x35,
MemoryAccessControl = 0x36,
PixelFormatSet = 0x3A,
FrameRateControl = 0xC6
};
namespace Ion {
namespace Device {
namespace Display {
namespace Config {
constexpr static GPIOPin FSMCPins[] = {
GPIOPin(GPIOA, 2), GPIOPin(GPIOA, 3), GPIOPin(GPIOA, 4), GPIOPin(GPIOB, 12),
@@ -91,18 +49,10 @@ constexpr static GPIOPin ResetPin = GPIOPin(GPIOE, 9);
constexpr static GPIOPin ExtendedCommandPin = GPIOPin(GPIOB, 13);
constexpr static GPIOPin TearingEffectPin = GPIOPin(GPIOB, 10);
constexpr static int FSMCMemoryBank = 1;
constexpr static int FSMCDataCommandAddressBit = 16;
constexpr static uint32_t FSMCBaseAddress = 0x60000000;
constexpr static uint32_t FSMCBankAddress = FSMCBaseAddress + (FSMCMemoryBank-1)*0x04000000;
constexpr static DMA DMAEngine = DMA2;
constexpr static int DMAStream = 0;
static volatile Command * const CommandAddress = (Command *)(FSMCBankAddress);
static volatile uint16_t * const DataAddress = (uint16_t *)(FSMCBankAddress | (1<<(FSMCDataCommandAddressBit+1)));
}
}
}
}

View File

@@ -0,0 +1,31 @@
#ifndef ION_DEVICE_N0100_CONFIG_EXTERNAL_FLASH_H
#define ION_DEVICE_N0100_CONFIG_EXTERNAL_FLASH_H
#include <regs/regs.h>
/* Pin | Role | Mode | Function
* -----+----------------------+-----------------------+-----------------
* PB2 | QUADSPI CLK | Alternate Function 9 | QUADSPI_CLK
* PB6 | QUADSPI BK1_NCS | Alternate Function 10 | QUADSPI_BK1_NCS
* PC8 | QUADSPI BK1_IO2/WP | Alternate Function 9 | QUADSPI_BK1_IO2
* PC9 | QUADSPI BK1_IO0/SO | Alternate Function 9 | QUADSPI_BK1_IO0
* PD12 | QUADSPI BK1_IO1/SI | Alternate Function 9 | QUADSPI_BK1_IO1
* PD13 | QUADSPI BK1_IO3/HOLD | Alternate Function 9 | QUADSPI_BK1_IO3
*/
namespace Ion {
namespace Device {
namespace ExternalFlash {
namespace Config {
constexpr static GPIOPin QSPIPins[] = {
GPIOPin(GPIOB, 2), GPIOPin(GPIOB, 6), GPIOPin(GPIOC, 9), GPIOPin(GPIOD,12),
GPIOPin(GPIOC, 8), GPIOPin(GPIOD,13)
};
}
}
}
}
#endif

View File

@@ -1,13 +1,7 @@
#ifndef ION_DEVICE_KEYBOARD_H
#define ION_DEVICE_KEYBOARD_H
#ifndef ION_DEVICE_N0100_CONFIG_KEYBOARD_H
#define ION_DEVICE_N0100_CONFIG_KEYBOARD_H
#include <ion/keyboard.h>
#include <ion.h>
#include "regs/regs.h"
namespace Ion {
namespace Keyboard {
namespace Device {
#include <regs/regs.h>
/* Pin | Role | Mode
* -----+-------------------+--------------------
@@ -28,8 +22,10 @@ namespace Device {
* PE8 | Keyboard row I | Output, open drain
*/
void init();
void shutdown();
namespace Ion {
namespace Device {
namespace Keyboard {
namespace Config {
constexpr GPIO RowGPIO = GPIOE;
constexpr uint8_t numberOfRows = 9;
@@ -39,30 +35,7 @@ constexpr GPIO ColumnGPIO = GPIOC;
constexpr uint8_t numberOfColumns = 6;
constexpr uint8_t ColumnPins[numberOfColumns] = {0, 1, 2, 3, 4, 5};
inline uint8_t rowForKey(Key key) {
return (int)key/numberOfColumns;
}
inline uint8_t columnForKey(Key key) {
return (int)key%numberOfColumns;
}
inline void activateRow(uint8_t row) {
/* In open-drain mode, a 0 in the register drives the pin low, and a 1 lets
* the pin floating (Hi-Z). So we want to set the current row to zero and all
* the others to 1. */
uint16_t rowState = ~(1<<row);
// TODO: Assert pin numbers are sequentials and dynamically find 9 and 0
Device::RowGPIO.ODR()->setBitRange(9, 0, rowState);
// TODO: 100 us seems to work, but wasn't really calculated
Timing::usleep(100);
}
inline bool columnIsActive(uint8_t column) {
return !(Device::ColumnGPIO.IDR()->getBitRange(column,column));
}
}
}
}

View File

@@ -1,11 +1,7 @@
#ifndef ION_DEVICE_LED_H
#define ION_DEVICE_LED_H
#ifndef ION_DEVICE_N0100_CONFIG_LED_H
#define ION_DEVICE_N0100_CONFIG_LED_H
#include "regs/regs.h"
namespace Ion {
namespace LED {
namespace Device {
#include <regs/regs.h>
/* Pin | Role | Mode | Function
* -----+-------------------+-----------------------+----------
@@ -14,33 +10,16 @@ namespace Device {
* PC7 | LED red | Alternate Function 2 | TIM3_CH2
*/
enum class Mode {
PWM,
Blink
};
enum class Color {
Red,
Green,
Blue
};
void init();
void shutdown();
void setPeriodAndDutyCycles(Mode mode, float dutyCycleRed, float dutyCycleGreen, float dutyCycleBlue, uint16_t period = 0);
void initGPIO();
void shutdownGPIO();
void initTimer();
void shutdownTimer();
namespace Ion {
namespace Device {
namespace LED {
namespace Config {
constexpr static GPIOPin RGBPins[] = {
GPIOPin(GPIOC, 7), GPIOPin(GPIOB, 1), GPIOPin(GPIOB, 0)
};
constexpr uint16_t PWMPeriod = 40000;
}
}
}
}

View File

@@ -1,11 +1,7 @@
#ifndef ION_DEVICE_SWD_H
#define ION_DEVICE_SWD_H
#ifndef ION_DEVICE_N0100_CONFIG_SWD_H
#define ION_DEVICE_N0100_CONFIG_SWD_H
#include "regs/regs.h"
namespace Ion {
namespace SWD {
namespace Device {
#include <regs/regs.h>
/* Pin | Role | Mode
* -----+-------------------+---------------------
@@ -14,8 +10,10 @@ namespace Device {
* PB3 | SWO | Alternate Function 0
*/
void init();
void shutdown();
namespace Ion {
namespace Device {
namespace SWD {
namespace Config {
constexpr static GPIOPin Pins[] = {
GPIOPin(GPIOA, 13), GPIOPin(GPIOA, 14), GPIOPin(GPIOB, 3)
@@ -24,5 +22,6 @@ constexpr static GPIOPin Pins[] = {
}
}
}
}
#endif

View File

@@ -0,0 +1,24 @@
#ifndef ION_DEVICE_N0100_CONFIG_TIMING_H
#define ION_DEVICE_N0100_CONFIG_TIMING_H
#include <regs/regs.h>
namespace Ion {
namespace Device {
namespace Timing {
namespace Config {
constexpr static int LoopsPerMillisecond = 8852;
constexpr static int LoopsPerMicrosecond = 9;
// CPU clock is 96 MHz, and systick clock source is divided by 8
// To get 1 ms systick overflow we need to reset it to
// 96 000 000 (Hz) / 8 / 1 000 (ms/s) - 1 (because the counter resets *after* counting to 0)
constexpr static int SysTickPerMillisecond = 12000;
}
}
}
}
#endif

View File

@@ -0,0 +1,20 @@
#ifndef ION_DEVICE_N0100_CONFIG_USB_H
#define ION_DEVICE_N0100_CONFIG_USB_H
#include <regs/regs.h>
namespace Ion {
namespace Device {
namespace USB {
namespace Config {
constexpr static GPIOPin VbusPin = GPIOPin(GPIOA, 9);
constexpr static GPIOPin DmPin = GPIOPin(GPIOA, 11);
constexpr static GPIOPin DpPin = GPIOPin(GPIOA, 12);
}
}
}
}
#endif

View File

@@ -1,95 +0,0 @@
#include "sd_card.h"
#include "regs/regs.h"
extern "C" {
#include <assert.h>
}
namespace Ion {
namespace SDCard {
namespace Device {
void init() {
initGPIO();
initCard();
}
void initGPIO() {
// Configure GPIOs to use AF
GPIOA.MODER()->setMode(8, GPIO::MODER::Mode::AlternateFunction);
GPIOB.MODER()->setMode(4, GPIO::MODER::Mode::AlternateFunction);
GPIOB.MODER()->setMode(5, GPIO::MODER::Mode::AlternateFunction);
GPIOB.MODER()->setMode(15, GPIO::MODER::Mode::AlternateFunction);
GPIOC.MODER()->setMode(10, GPIO::MODER::Mode::AlternateFunction);
GPIOD.MODER()->setMode(2, GPIO::MODER::Mode::AlternateFunction);
// More precisely, AF12 which correspond to SDIO alternate functions
GPIOA.AFR()->setAlternateFunction(8, GPIO::AFR::AlternateFunction::AF12);
GPIOB.AFR()->setAlternateFunction(4, GPIO::AFR::AlternateFunction::AF12);
GPIOB.AFR()->setAlternateFunction(5, GPIO::AFR::AlternateFunction::AF12);
GPIOB.AFR()->setAlternateFunction(15, GPIO::AFR::AlternateFunction::AF12);
GPIOC.AFR()->setAlternateFunction(10, GPIO::AFR::AlternateFunction::AF12);
GPIOD.AFR()->setAlternateFunction(2, GPIO::AFR::AlternateFunction::AF12);
}
void initCard() {
// Power on
SDIO.POWER()->setPWRCTRL(SDIO::POWER::PWRCTRL::On);
while (SDIO.POWER()->getPWRCTRL() != SDIO::POWER::PWRCTRL::On) {
}
// Clock set
SDIO.CLKCR()->setCLKDIV(254);
SDIO.CLKCR()->setCLKEN(true);
sendCommand(0, 0);
// CMD8 : 0b0001 = 2.7 - 3.6V
// 0xB7 = Pattern to see back in response
sendCommand(8, 0x1B7);
assert(SDIO.RESP(1)->get() == 0x1B7);
}
void sendCommand(uint32_t cmd, uint32_t arg) {
class SDIO::ICR icr(0);
icr.setCCRCFAILC(true);
icr.setCTIMEOUTC(true);
icr.setCMDRENDC(true);
icr.setCMDSENTC(true);
SDIO.ICR()->set(icr);
SDIO.ARG()->set(arg);
SDIO::CMD::WAITRESP responseType = SDIO::CMD::WAITRESP::Short;
switch (cmd) {
case 0:
responseType = SDIO::CMD::WAITRESP::None;
break;
case 2:
case 9:
case 10:
responseType = SDIO::CMD::WAITRESP::Long;
default:
break;
}
class SDIO::CMD command(0);
command.setCMDINDEX(cmd);
command.setCPSMEN(true);
command.setWAITRESP(responseType);
SDIO.CMD()->set(command);
if (responseType == SDIO::CMD::WAITRESP::None) {
// Wait for timeout or command sent
while (!SDIO.STA()->getCTIMEOUT() && !SDIO.STA()->getCMDSENT()) {
}
} else {
while (!SDIO.STA()->getCTIMEOUT() && !SDIO.STA()->getCMDREND() && !SDIO.STA()->getCCRCFAIL()) {
}
}
}
}
}
}

View File

@@ -1,33 +0,0 @@
#ifndef ION_DEVICE_SD_CARD_H
#define ION_DEVICE_SD_CARD_H
extern "C" {
#include <stdint.h>
}
namespace Ion {
namespace SDCard {
namespace Device {
/* Pin | Role | Mode | Function
* -----+-------------------+-----------------------+----------
* PA8 | SDIO D1 | Alternate Function 12 | SDIO_D1
* PB4 | SDIO D0 | Alternate Function 12 | SDIO_D0
* PB5 | SDIO D3 | Alternate Function 12 | SDIO_D3
* PB15 | SDIO CLK | Alternate Function 12 | SDIO_CK
* PC10 | SDIO D2 | Alternate Function 12 | SDIO_D2
* PD2 | SDIO CMD | Alternate Function 12 | SDIO_CMD
*/
void init();
void initGPIO();
void initCard();
void sendCommand(uint32_t cmd, uint32_t arg);
}
}
}
#endif

View File

@@ -0,0 +1,3 @@
include ion/src/device/shared/boot/Makefile
include ion/src/device/shared/usb/Makefile
include ion/src/device/shared/drivers/Makefile

View File

@@ -0,0 +1,4 @@
objs += $(addprefix ion/src/device/shared/boot/, \
isr.o \
rt0.o \
)

View File

@@ -2,9 +2,9 @@
#include <stdint.h>
#include <string.h>
#include <ion.h>
#include "../device.h"
#include "../timing.h"
#include "../console.h"
#include "../drivers/board.h"
#include "../drivers/reset.h"
#include "../drivers/timing.h"
typedef void (*cxx_constructor)();
@@ -23,7 +23,7 @@ void abort() {
while (1) {
}
#else
Ion::Device::coreReset();
Ion::Device::Reset::core();
#endif
}
@@ -59,7 +59,8 @@ void start() {
/* Initialize the FPU as early as possible.
* For example, static C++ objects are very likely to manipulate float values */
Ion::Device::initFPU();
// FIXME//TODO//Ion::Device::initFPU();
#warning BOO
#if 0
Ion::Device::initMPU();
@@ -85,7 +86,7 @@ void start() {
}
#endif
Ion::Device::init();
Ion::Device::Board::init();
non_inlined_ion_main();
@@ -93,5 +94,7 @@ void start() {
}
void __attribute__((interrupt)) isr_systick() {
Ion::Timing::Device::MillisElapsed++;
#warning BOO
// FIXME: TODO//
//Ion::Timing::Device::MillisElapsed++;
}

View File

@@ -0,0 +1,22 @@
ion_device_objs += $(addprefix ion/src/device/shared/drivers/, \
backlight.o \
battery.o \
base64.o \
board.o \
console.o \
crc32.o \
display.o \
events.o \
external_flash.o \
flash.o \
keyboard.o \
led.o \
power.o\
random.o\
reset.o \
serial_number.o \
swd.o \
timing.o \
usb.o \
wakeup.o \
)

View File

@@ -1,6 +1,7 @@
#include <ion.h>
#include "regs/regs.h"
#include "backlight.h"
#include <ion/backlight.h>
#include <ion/timing.h>
#include <drivers/config/backlight.h>
/* This driver controls the RT9365 LED driver.
* This chip allows the brightness to be set to 16 different values. It starts
@@ -13,12 +14,14 @@
namespace Ion {
namespace Backlight {
using namespace Ion::Device::Backlight;
void setBrightness(uint8_t b) {
Device::setLevel(b >> 4);
setLevel(b >> 4);
}
uint8_t brightness() {
return Device::level() << 4;
return level() << 4;
}
}
@@ -27,29 +30,29 @@ uint8_t brightness() {
// Private Ion::Backlight::Device methods
namespace Ion {
namespace Backlight {
namespace Device {
namespace Backlight {
static uint8_t sLevel;
void init() {
GPIOC.MODER()->setMode(6, GPIO::MODER::Mode::Output);
Config::BacklightPin.group().MODER()->setMode(Config::BacklightPin.pin(), GPIO::MODER::Mode::Output);
sLevel = 0xF;
resume();
}
void shutdown() {
GPIOC.MODER()->setMode(6, GPIO::MODER::Mode::Analog);
GPIOC.PUPDR()->setPull(6, GPIO::PUPDR::Pull::None);
Config::BacklightPin.group().MODER()->setMode(Config::BacklightPin.pin(), GPIO::MODER::Mode::Analog);
Config::BacklightPin.group().PUPDR()->setPull(Config::BacklightPin.pin(), GPIO::PUPDR::Pull::None);
}
void suspend() {
GPIOC.ODR()->set(6, false);
Config::BacklightPin.group().ODR()->set(Config::BacklightPin.pin(), false);
Timing::msleep(3); // Might not need to be blocking
}
void resume() {
GPIOC.ODR()->set(6, true);
Config::BacklightPin.group().ODR()->set(Config::BacklightPin.pin(), true);
Timing::usleep(50);
uint8_t level = sLevel;
sLevel = 0xF;
@@ -73,9 +76,9 @@ uint8_t level() {
void sendPulses(int n) {
for (int i=0; i<n; i++) {
GPIOC.ODR()->set(6, false);
Config::BacklightPin.group().ODR()->set(Config::BacklightPin.pin(), false);
Timing::usleep(20);
GPIOC.ODR()->set(6, true);
Config::BacklightPin.group().ODR()->set(Config::BacklightPin.pin(), true);
Timing::usleep(20);
}
}

View File

@@ -0,0 +1,24 @@
#ifndef ION_DEVICE_SHARED_BACKLIGHT_H
#define ION_DEVICE_SHARED_BACKLIGHT_H
#include <stdint.h>
namespace Ion {
namespace Device {
namespace Backlight {
void init();
void shutdown();
void suspend();
void resume();
uint8_t level();
void setLevel(uint8_t level);
void sendPulses(int n);
}
}
}
#endif

View File

@@ -1,6 +1,6 @@
#include <ion/battery.h>
#include "battery.h"
#include "regs/regs.h"
#include <ion/battery.h>
#include <drivers/config/battery.h>
/* To measure the battery voltage, we're using the internal ADC. The ADC works
* by comparing the input voltage to a reference voltage. The only fixed voltage
@@ -10,11 +10,14 @@
* To avoid draining the battery, we're using an high-impedence voltage divider,
* so we need to be careful when sampling the ADC. See AN2834 for more info. */
namespace Ion {
namespace Battery {
using namespace Ion::Device::Battery;
bool isCharging() {
return !Device::ChargingGPIO.IDR()->get(Device::ChargingPin);
return !Config::ChargingPin.group().IDR()->get(Config::ChargingPin.pin());
}
Charge level() {
@@ -37,22 +40,22 @@ float voltage() {
uint16_t value = ADC.DR()->get();
// The ADC is 12 bits by default
return Device::ADCDividerBridgeRatio*(Device::ADCReferenceVoltage * value)/0xFFF;
return Config::ADCDividerBridgeRatio*(Config::ADCReferenceVoltage * value)/0xFFF;
}
}
}
namespace Ion {
namespace Battery {
namespace Device {
namespace Battery {
void init() {
initGPIO();
/* The BAT_SNS pin is connected to Vbat through a divider bridge. It therefore
* has a voltage of Vbat/2. We'll measure this using ADC channel 0. */
ADCGPIO.MODER()->setMode(ADCPin, GPIO::MODER::Mode::Analog);
Config::ADCPin.group().MODER()->setMode(Config::ADCPin.pin(), GPIO::MODER::Mode::Analog);
// Step 2 - Enable the ADC
RCC.APB2ENR()->setADC1EN(true);
@@ -60,8 +63,8 @@ void init() {
// 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
ADC.SQR3()->setSQ1(Config::ADCChannel);
ADC.SMPR()->setSamplingTime(Config::ADCChannel, ADC::SMPR::SamplingTime::Cycles480); // Use the max sampling time
}
void initGPIO() {
@@ -70,13 +73,13 @@ void initGPIO() {
* open-drain output. Open-drain output are either connected to ground or left
* floating. To interact with such an output, our input must therefore be
* pulled up. */
ChargingGPIO.MODER()->setMode(ChargingPin, GPIO::MODER::Mode::Input);
ChargingGPIO.PUPDR()->setPull(ChargingPin, GPIO::PUPDR::Pull::Up);
Config::ChargingPin.group().MODER()->setMode(Config::ChargingPin.pin(), GPIO::MODER::Mode::Input);
Config::ChargingPin.group().PUPDR()->setPull(Config::ChargingPin.pin(), GPIO::PUPDR::Pull::Up);
}
void shutdown() {
ChargingGPIO.MODER()->setMode(ChargingPin, GPIO::MODER::Mode::Analog);
ChargingGPIO.PUPDR()->setPull(ChargingPin, GPIO::PUPDR::Pull::None);
Config::ChargingPin.group().MODER()->setMode(Config::ChargingPin.pin(), GPIO::MODER::Mode::Analog);
Config::ChargingPin.group().PUPDR()->setPull(Config::ChargingPin.pin(), GPIO::PUPDR::Pull::None);
// Disable the ADC
ADC.CR2()->setADON(false);

View File

@@ -0,0 +1,17 @@
#ifndef ION_DEVICE_SHARED_BATTERY_H
#define ION_DEVICE_SHARED_BATTERY_H
namespace Ion {
namespace Device {
namespace Battery {
void init();
void shutdown();
void initGPIO();
void initADC();
}
}
}
#endif

View File

@@ -0,0 +1,59 @@
#include "board.h"
#include <drivers/board.h>
#include <drivers/backlight.h>
#include <drivers/battery.h>
#include <drivers/console.h>
#include <drivers/display.h>
#include <drivers/external_flash.h>
#include <drivers/keyboard.h>
#include <drivers/led.h>
#include <drivers/swd.h>
#include <drivers/timing.h>
#include <drivers/usb.h>
namespace Ion {
namespace Device {
namespace Board {
void shutdown() {
shutdownPeripherals();
shutdownClocks();
}
void initFPU() {
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0553a/BABDBFBJ.html
CM4.CPACR()->setAccess(10, CM4::CPACR::Access::Full);
CM4.CPACR()->setAccess(11, CM4::CPACR::Access::Full);
// FIXME: The pipeline should be flushed at this point
}
void initPeripherals() {
Display::init();
Backlight::init();
Keyboard::init();
LED::init();
Battery::init();
USB::init();
Console::init();
SWD::init();
Timing::init();
ExternalFlash::init();
}
void shutdownPeripherals(bool keepLEDAwake) {
Timing::shutdown();
SWD::shutdown();
Console::shutdown();
USB::shutdown();
Battery::shutdown();
if (!keepLEDAwake) {
LED::shutdown();
}
Keyboard::shutdown();
Backlight::shutdown();
Display::shutdown();
}
}
}
}

View File

@@ -0,0 +1,21 @@
#ifndef ION_DEVICE_SHARED_DRIVERS_BOARD_H
#define ION_DEVICE_SHARED_DRIVERS_BOARD_H
namespace Ion {
namespace Device {
namespace Board {
void init();
void shutdown();
void initClocks();
void shutdownClocks(bool keepLEDAwake = false);
void initPeripherals();
void shutdownPeripherals(bool keepLEDAwake = false);
}
}
}
#endif

View File

@@ -1,5 +1,7 @@
#include <ion.h>
#include "console.h"
#include <ion/console.h>
#include <ion/timing.h>
#include <drivers/config/console.h>
/* This file implements a serial console.
* We use a 115200 8N1 serial port */
@@ -7,36 +9,38 @@
namespace Ion {
namespace Console {
using namespace Ion::Device::Console;
char readChar() {
while (Device::UARTPort.SR()->getRXNE() == 0) {
while (Config::UARTPort.SR()->getRXNE() == 0) {
}
return (char)Device::UARTPort.DR()->get();
return (char)Config::UARTPort.DR()->get();
}
void writeChar(char c) {
while (Device::UARTPort.SR()->getTXE() == 0) {
while (Config::UARTPort.SR()->getTXE() == 0) {
}
Device::UARTPort.DR()->set(c);
Config::UARTPort.DR()->set(c);
}
}
}
namespace Ion {
namespace Console {
namespace Device {
namespace Console {
void init() {
RCC.APB1ENR()->setUSART3EN(true);
for(const GPIOPin & g : Pins) {
for(const GPIOPin & g : Config::Pins) {
g.group().MODER()->setMode(g.pin(), GPIO::MODER::Mode::AlternateFunction);
g.group().AFR()->setAlternateFunction(g.pin(), GPIO::AFR::AlternateFunction::AF7);
}
UARTPort.CR1()->setUE(true);
UARTPort.CR1()->setTE(true);
UARTPort.CR1()->setRE(true);
Config::UARTPort.CR1()->setUE(true);
Config::UARTPort.CR1()->setTE(true);
Config::UARTPort.CR1()->setRE(true);
/* We need to set the baud rate of the UART port.
* This is set relative to the APB1 clock, which runs at 48 MHz.
@@ -49,28 +53,27 @@ void init() {
* DIV_MANTISSA = 26
* DIV_FRAC = 16*0.0416667 = 1
*/
UARTPort.BRR()->setDIV_MANTISSA(26);
UARTPort.BRR()->setDIV_FRAC(1);
Config::UARTPort.BRR()->setDIV_MANTISSA(26);
Config::UARTPort.BRR()->setDIV_FRAC(1);
}
void shutdown() {
for(const GPIOPin & g : Pins) {
for(const GPIOPin & g : Config::Pins) {
g.group().MODER()->setMode(g.pin(), GPIO::MODER::Mode::Analog);
g.group().PUPDR()->setPull(g.pin(), GPIO::PUPDR::Pull::None);
}
}
bool peerConnected() {
RxPin.group().PUPDR()->setPull(RxPin.pin(), GPIO::PUPDR::Pull::Down);
RxPin.group().MODER()->setMode(RxPin.pin(), GPIO::MODER::Mode::Input);
Config::RxPin.group().PUPDR()->setPull(Config::RxPin.pin(), GPIO::PUPDR::Pull::Down);
Config::RxPin.group().MODER()->setMode(Config::RxPin.pin(), GPIO::MODER::Mode::Input);
Timing::msleep(1);
bool result = RxPin.group().IDR()->get(RxPin.pin());
RxPin.group().PUPDR()->setPull(RxPin.pin(), GPIO::PUPDR::Pull::None);
RxPin.group().MODER()->setMode(RxPin.pin(), GPIO::MODER::Mode::AlternateFunction);
bool result = Config::RxPin.group().IDR()->get(Config::RxPin.pin());
Config::RxPin.group().PUPDR()->setPull(Config::RxPin.pin(), GPIO::PUPDR::Pull::None);
Config::RxPin.group().MODER()->setMode(Config::RxPin.pin(), GPIO::MODER::Mode::AlternateFunction);
return result;
}
}
}
}

View File

@@ -0,0 +1,16 @@
#ifndef ION_DEVICE_SHARED_CONSOLE_H
#define ION_DEVICE_SHARED_CONSOLE_H
namespace Ion {
namespace Device {
namespace Console {
void init();
void shutdown();
bool peerConnected();
}
}
}
#endif

View File

@@ -0,0 +1,17 @@
#include <ion.h>
#include <regs/regs.h>
uint32_t Ion::crc32(const uint32_t * data, size_t length) {
bool initialCRCEngineState = RCC.AHB1ENR()->getCRCEN();
RCC.AHB1ENR()->setCRCEN(true);
CRC.CR()->setRESET(true);
const uint32_t * end = data + length;
while (data < end) {
CRC.DR()->set(*data++);
}
uint32_t result = CRC.DR()->get();
RCC.AHB1ENR()->setCRCEN(initialCRCEngineState);
return result;
}

View File

@@ -1,9 +1,8 @@
#include <ion.h>
#include "display.h"
#include "regs/regs.h"
extern "C" {
#include <ion/display.h>
#include <ion/timing.h>
#include <drivers/config/display.h>
#include <assert.h>
}
/* This driver interfaces with the ST7789V LCD controller.
* This chip keeps a whole frame in SRAM memory and feeds it to the LCD panel as
@@ -16,42 +15,42 @@ extern "C" {
#define USE_DMA (USE_DMA_FOR_PUSH_PIXELS|USE_DMA_FOR_PUSH_COLOR)
// Public Ion::Display methods
namespace Ion {
namespace Display {
using namespace Ion::Device::Display;
void pushRect(KDRect r, const KDColor * pixels) {
#if USE_DMA
Device::waitForPendingDMAUploadCompletion();
waitForPendingDMAUploadCompletion();
#endif
Device::setDrawingArea(r, Device::Orientation::Landscape);
Device::pushPixels(pixels, r.width()*r.height());
setDrawingArea(r, Orientation::Landscape);
pushPixels(pixels, r.width()*r.height());
}
void pushRectUniform(KDRect r, KDColor c) {
#if USE_DMA
Device::waitForPendingDMAUploadCompletion();
waitForPendingDMAUploadCompletion();
#endif
Device::setDrawingArea(r, Device::Orientation::Portrait);
Device::pushColor(c, r.width()*r.height());
setDrawingArea(r, Orientation::Portrait);
pushColor(c, r.width()*r.height());
}
void pullRect(KDRect r, KDColor * pixels) {
#if USE_DMA
Device::waitForPendingDMAUploadCompletion();
waitForPendingDMAUploadCompletion();
#endif
Device::setDrawingArea(r, Device::Orientation::Landscape);
Device::pullPixels(pixels, r.width()*r.height());
setDrawingArea(r, Orientation::Landscape);
pullPixels(pixels, r.width()*r.height());
}
void waitForVBlank() {
// We want to return as soon as the TE line is transitionning from "DOWN" to "UP"
while (Device::TearingEffectPin.group().IDR()->get(Device::TearingEffectPin.pin())) {
while (Config::TearingEffectPin.group().IDR()->get(Config::TearingEffectPin.pin())) {
// Loop while high, exit when low
// Wait for zero
}
while (!Device::TearingEffectPin.group().IDR()->get(Device::TearingEffectPin.pin())) {
while (!Config::TearingEffectPin.group().IDR()->get(Config::TearingEffectPin.pin())) {
// Loop while low, exit when high
}
}
@@ -59,11 +58,9 @@ void waitForVBlank() {
}
}
// Private Ion::Display::Device methods
namespace Ion {
namespace Display {
namespace Device {
namespace Display {
static inline void send_data(uint16_t d) {
*DataAddress = d;
@@ -107,82 +104,81 @@ void shutdown() {
#if USE_DMA
void initDMA() {
// Only DMA2 can perform memory-to-memory transfers
//assert(DMAEngine == DMA2);
//assert(Config::DMAEngine == DMA2);
/* In memory-to-memory transfers, the "peripheral" is the source and the
* "memory" is the destination. In other words, memory is copied from address
* DMA_SxPAR to address DMA_SxM0AR. */
DMAEngine.SCR(DMAStream)->setDIR(DMA::SCR::Direction::MemoryToMemory);
DMAEngine.SM0AR(DMAStream)->set((uint32_t)DataAddress);
DMAEngine.SCR(DMAStream)->setMSIZE(DMA::SCR::DataSize::HalfWord);
DMAEngine.SCR(DMAStream)->setPSIZE(DMA::SCR::DataSize::HalfWord);
DMAEngine.SCR(DMAStream)->setMBURST(DMA::SCR::Burst::Incremental4);
DMAEngine.SCR(DMAStream)->setPBURST(DMA::SCR::Burst::Incremental4);
DMAEngine.SCR(DMAStream)->setMINC(false);
Config::DMAEngine.SCR(Config::DMAStream)->setDIR(DMA::SCR::Direction::MemoryToMemory);
Config::DMAEngine.SM0AR(Config::DMAStream)->set((uint32_t)DataAddress);
Config::DMAEngine.SCR(Config::DMAStream)->setMSIZE(DMA::SCR::DataSize::HalfWord);
Config::DMAEngine.SCR(Config::DMAStream)->setPSIZE(DMA::SCR::DataSize::HalfWord);
Config::DMAEngine.SCR(Config::DMAStream)->setMBURST(DMA::SCR::Burst::Incremental4);
Config::DMAEngine.SCR(Config::DMAStream)->setPBURST(DMA::SCR::Burst::Incremental4);
Config::DMAEngine.SCR(Config::DMAStream)->setMINC(false);
}
void waitForPendingDMAUploadCompletion() {
// Loop until DMA engine available
while (DMAEngine.SCR(DMAStream)->getEN()) {
while (Config::DMAEngine.SCR(Config::DMAStream)->getEN()) {
}
}
static inline void startDMAUpload(const KDColor * src, bool incrementSrc, uint16_t length) {
// Reset interruption markers
DMAEngine.LIFCR()->set(0xF7D0F7D);
Config::DMAEngine.LIFCR()->set(0xF7D0F7D);
DMAEngine.SNDTR(DMAStream)->set(length);
DMAEngine.SPAR(DMAStream)->set((uint32_t)src);
DMAEngine.SCR(DMAStream)->setPINC(incrementSrc);
DMAEngine.SCR(DMAStream)->setEN(true);
Config::DMAEngine.SNDTR(Config::DMAStream)->set(length);
Config::DMAEngine.SPAR(Config::DMAStream)->set((uint32_t)src);
Config::DMAEngine.SCR(Config::DMAStream)->setPINC(incrementSrc);
Config::DMAEngine.SCR(Config::DMAStream)->setEN(true);
}
#endif
void initGPIO() {
// All the FSMC GPIO pins use the alternate function number 12
for(const GPIOPin & g : FSMCPins) {
for(const GPIOPin & g : Config::FSMCPins) {
g.group().MODER()->setMode(g.pin(), GPIO::MODER::Mode::AlternateFunction);
g.group().AFR()->setAlternateFunction(g.pin(), GPIO::AFR::AlternateFunction::AF12);
}
// Turn on the power
PowerPin.group().MODER()->setMode(PowerPin.pin(), GPIO::MODER::Mode::Output);
PowerPin.group().ODR()->set(PowerPin.pin(), true);
Config::PowerPin.group().MODER()->setMode(Config::PowerPin.pin(), GPIO::MODER::Mode::Output);
Config::PowerPin.group().ODR()->set(Config::PowerPin.pin(), true);
// Turn on the reset pin
ResetPin.group().MODER()->setMode(ResetPin.pin(), GPIO::MODER::Mode::Output);
ResetPin.group().ODR()->set(ResetPin.pin(), true);
Config::ResetPin.group().MODER()->setMode(Config::ResetPin.pin(), GPIO::MODER::Mode::Output);
Config::ResetPin.group().ODR()->set(Config::ResetPin.pin(), true);
// Turn on the extended command pin
ExtendedCommandPin.group().MODER()->setMode(ExtendedCommandPin.pin(), GPIO::MODER::Mode::Output);
ExtendedCommandPin.group().ODR()->set(ExtendedCommandPin.pin(), true);
Config::ExtendedCommandPin.group().MODER()->setMode(Config::ExtendedCommandPin.pin(), GPIO::MODER::Mode::Output);
Config::ExtendedCommandPin.group().ODR()->set(Config::ExtendedCommandPin.pin(), true);
// Turn on the Tearing Effect pin
TearingEffectPin.group().MODER()->setMode(TearingEffectPin.pin(), GPIO::MODER::Mode::Input);
TearingEffectPin.group().PUPDR()->setPull(TearingEffectPin.pin(), GPIO::PUPDR::Pull::None);
Config::TearingEffectPin.group().MODER()->setMode(Config::TearingEffectPin.pin(), GPIO::MODER::Mode::Input);
Config::TearingEffectPin.group().PUPDR()->setPull(Config::TearingEffectPin.pin(), GPIO::PUPDR::Pull::None);
Timing::msleep(120);
}
void shutdownGPIO() {
// All the FSMC GPIO pins use the alternate function number 12
for(const GPIOPin & g : FSMCPins) {
for(const GPIOPin & g : Config::FSMCPins) {
g.group().MODER()->setMode(g.pin(), GPIO::MODER::Mode::Analog);
g.group().PUPDR()->setPull(g.pin(), GPIO::PUPDR::Pull::None);
}
ResetPin.group().MODER()->setMode(ResetPin.pin(), GPIO::MODER::Mode::Analog);
ResetPin.group().PUPDR()->setPull(ResetPin.pin(), GPIO::PUPDR::Pull::None);
Config::ResetPin.group().MODER()->setMode(Config::ResetPin.pin(), GPIO::MODER::Mode::Analog);
Config::ResetPin.group().PUPDR()->setPull(Config::ResetPin.pin(), GPIO::PUPDR::Pull::None);
PowerPin.group().MODER()->setMode(PowerPin.pin(), GPIO::MODER::Mode::Analog);
PowerPin.group().PUPDR()->setPull(PowerPin.pin(), GPIO::PUPDR::Pull::None);
Config::PowerPin.group().MODER()->setMode(Config::PowerPin.pin(), GPIO::MODER::Mode::Analog);
Config::PowerPin.group().PUPDR()->setPull(Config::PowerPin.pin(), GPIO::PUPDR::Pull::None);
ExtendedCommandPin.group().MODER()->setMode(ExtendedCommandPin.pin(), GPIO::MODER::Mode::Analog);
ExtendedCommandPin.group().PUPDR()->setPull(ExtendedCommandPin.pin(), GPIO::PUPDR::Pull::None);
Config::ExtendedCommandPin.group().MODER()->setMode(Config::ExtendedCommandPin.pin(), GPIO::MODER::Mode::Analog);
Config::ExtendedCommandPin.group().PUPDR()->setPull(Config::ExtendedCommandPin.pin(), GPIO::PUPDR::Pull::None);
TearingEffectPin.group().MODER()->setMode(TearingEffectPin.pin(), GPIO::MODER::Mode::Analog);
Config::TearingEffectPin.group().MODER()->setMode(Config::TearingEffectPin.pin(), GPIO::MODER::Mode::Analog);
}
void initFSMC() {

View File

@@ -0,0 +1,67 @@
#ifndef ION_DEVICE_SHARED_DISPLAY_H
#define ION_DEVICE_SHARED_DISPLAY_H
#include <kandinsky/rect.h>
#include <kandinsky/color.h>
extern "C" {
#include <stddef.h>
}
namespace Ion {
namespace Device {
namespace Display {
void init();
void shutdown();
void initDMA();
void initGPIO();
void shutdownGPIO();
void initFSMC();
void shutdownFSMC();
void initPanel();
void shutdownPanel();
enum class Orientation {
Landscape = 0,
Portrait = 1
};
void setDrawingArea(KDRect r, Orientation o);
void waitForPendingDMAUploadCompletion();
void pushPixels(const KDColor * pixels, size_t numberOfPixels);
void pushColor(KDColor color, size_t numberOfPixels);
void pullPixels(KDColor * pixels, size_t numberOfPixels);
enum class Command : uint16_t {
Nop = 0x00,
Reset = 0x01,
SleepIn = 0x10,
SleepOut = 0x11,
DisplayOff = 0x28,
DisplayOn = 0x29,
ColumnAddressSet = 0x2A,
PageAddressSet = 0x2B,
MemoryWrite = 0x2C,
MemoryRead = 0x2E,
TearingEffectLineOn = 0x35,
MemoryAccessControl = 0x36,
PixelFormatSet = 0x3A,
FrameRateControl = 0xC6
};
constexpr static int FSMCMemoryBank = 1;
constexpr static int FSMCDataCommandAddressBit = 16;
constexpr static uint32_t FSMCBaseAddress = 0x60000000;
constexpr static uint32_t FSMCBankAddress = FSMCBaseAddress + (FSMCMemoryBank-1)*0x04000000;
static volatile Command * const CommandAddress = (Command *)(FSMCBankAddress);
static volatile uint16_t * const DataAddress = (uint16_t *)(FSMCBankAddress | (1<<(FSMCDataCommandAddressBit+1)));
}
}
}
#endif

View File

@@ -1,8 +1,9 @@
#include "external_flash.h"
#include <drivers/config/external_flash.h>
namespace Ion {
namespace ExternalFlash {
namespace Device {
namespace ExternalFlash {
/* The external flash and the Quad-SPI peripheral support
* several operating modes, corresponding to different numbers of signals
@@ -206,7 +207,7 @@ void init() {
}
void initGPIO() {
for(const GPIOPin & g : QSPIPins) {
for(const GPIOPin & g : Config::QSPIPins) {
g.group().OSPEEDR()->setOutputSpeed(g.pin(), GPIO::OSPEEDR::OutputSpeed::High);
g.group().MODER()->setMode(g.pin(), GPIO::MODER::Mode::AlternateFunction);
g.group().AFR()->setAlternateFunction(g.pin(), (g.pin() == 6 ? GPIO::AFR::AlternateFunction::AF10 : GPIO::AFR::AlternateFunction::AF9));

View File

@@ -1,9 +1,8 @@
#ifndef ION_DEVICE_EXTERNAL_FLASH_H
#define ION_DEVICE_EXTERNAL_FLASH_H
#ifndef ION_DEVICE_SHARED_EXTERNAL_FLASH_H
#define ION_DEVICE_SHARED_EXTERNAL_FLASH_H
#include <stdint.h>
#include <stddef.h>
#include "regs/regs.h"
#include <stdint.h>
// Quad-SPI on STM32 microcontroller
// https://www.st.com/resource/en/application_note/dm00227538.pdf
@@ -19,8 +18,8 @@
* 2^7 * 2 * 2^3 * 2^4 256B pages 0x....00 - 0x....FF */
namespace Ion {
namespace ExternalFlash {
namespace Device {
namespace ExternalFlash {
/* Pin | Role | Mode | Function
* -----+----------------------+-----------------------+-----------------
@@ -69,11 +68,6 @@ constexpr static uint8_t NumberOfAddressBitsInChip = 23;
constexpr static uint8_t NumberOfAddressBitsIn64KbyteBlock = 16;
constexpr static uint32_t FlashAddressSpaceSize = 1 << NumberOfAddressBitsInChip;
constexpr static GPIOPin QSPIPins[] = {
GPIOPin(GPIOB, 2), GPIOPin(GPIOB, 6), GPIOPin(GPIOC, 9), GPIOPin(GPIOD,12),
GPIOPin(GPIOC, 8), GPIOPin(GPIOD,13)
};
}
}
}

View File

@@ -2,8 +2,8 @@
#include <assert.h>
namespace Ion {
namespace Flash {
namespace Device {
namespace Flash {
static inline void wait() {
// Wait for pending Flash operations to complete

View File

@@ -1,12 +1,12 @@
#ifndef ION_DEVICE_FLASH_H
#define ION_DEVICE_FLASH_H
#ifndef ION_DEVICE_SHARED_FLASH_H
#define ION_DEVICE_SHARED_FLASH_H
#include <stddef.h>
#include "regs/flash.h"
#include <regs/regs.h>
namespace Ion {
namespace Flash {
namespace Device {
namespace Flash {
void MassErase();

View File

@@ -42,19 +42,19 @@
#include "keyboard.h"
// Public Ion::Keyboard methods
namespace Ion {
namespace Keyboard {
using namespace Ion::Device::Keyboard;
State scan() {
uint64_t state = 0;
for (uint8_t i=0; i<Device::numberOfRows; i++) {
Device::activateRow(Device::numberOfRows-1-i);
for (uint8_t i=0; i<Config::numberOfRows; i++) {
activateRow(Config::numberOfRows-1-i);
// TODO: Assert pin numbers are sequentials and dynamically find 8 and 0
uint8_t columns = Device::ColumnGPIO.IDR()->getBitRange(5,0);
uint8_t columns = Config::ColumnGPIO.IDR()->getBitRange(5,0);
/* The key is down if the input is brought low by the output. In other
* words, we want to return true if the input is low (false). So we need to
@@ -73,37 +73,35 @@ State scan() {
}
}
// Private Ion::Keyboard::Device methods
namespace Ion {
namespace Keyboard {
namespace Device {
namespace Keyboard {
void init() {
for (uint8_t i=0; i<numberOfRows; i++) {
uint8_t pin = RowPins[i];
RowGPIO.MODER()->setMode(pin, GPIO::MODER::Mode::Output);
RowGPIO.OTYPER()->setType(pin, GPIO::OTYPER::Type::OpenDrain);
for (uint8_t i=0; i<Config::numberOfRows; i++) {
uint8_t pin = Config::RowPins[i];
Config::RowGPIO.MODER()->setMode(pin, GPIO::MODER::Mode::Output);
Config::RowGPIO.OTYPER()->setType(pin, GPIO::OTYPER::Type::OpenDrain);
}
for (uint8_t i=0; i<numberOfColumns; i++) {
uint8_t pin = ColumnPins[i];
ColumnGPIO.MODER()->setMode(pin, GPIO::MODER::Mode::Input);
ColumnGPIO.PUPDR()->setPull(pin, GPIO::PUPDR::Pull::Up);
for (uint8_t i=0; i<Config::numberOfColumns; i++) {
uint8_t pin = Config::ColumnPins[i];
Config::ColumnGPIO.MODER()->setMode(pin, GPIO::MODER::Mode::Input);
Config::ColumnGPIO.PUPDR()->setPull(pin, GPIO::PUPDR::Pull::Up);
}
}
void shutdown() {
for (uint8_t i=0; i<numberOfRows; i++) {
uint8_t pin = RowPins[i];
RowGPIO.MODER()->setMode(pin, GPIO::MODER::Mode::Analog);
RowGPIO.PUPDR()->setPull(pin, GPIO::PUPDR::Pull::None);
for (uint8_t i=0; i<Config::numberOfRows; i++) {
uint8_t pin = Config::RowPins[i];
Config::RowGPIO.MODER()->setMode(pin, GPIO::MODER::Mode::Analog);
Config::RowGPIO.PUPDR()->setPull(pin, GPIO::PUPDR::Pull::None);
}
for (uint8_t i=0; i<numberOfColumns; i++) {
uint8_t pin = ColumnPins[i];
ColumnGPIO.MODER()->setMode(pin, GPIO::MODER::Mode::Analog);
ColumnGPIO.PUPDR()->setPull(pin, GPIO::PUPDR::Pull::None);
for (uint8_t i=0; i<Config::numberOfColumns; i++) {
uint8_t pin = Config::ColumnPins[i];
Config::ColumnGPIO.MODER()->setMode(pin, GPIO::MODER::Mode::Analog);
Config::ColumnGPIO.PUPDR()->setPull(pin, GPIO::PUPDR::Pull::None);
}
}

View File

@@ -0,0 +1,45 @@
#ifndef ION_DEVICE_SHARED_KEYBOARD_H
#define ION_DEVICE_SHARED_KEYBOARD_H
#include <ion/keyboard.h>
#include <ion/timing.h>
#include <drivers/config/keyboard.h>
namespace Ion {
namespace Device {
namespace Keyboard {
using namespace Ion::Keyboard;
void init();
void shutdown();
inline uint8_t rowForKey(Key key) {
return (int)key/Config::numberOfColumns;
}
inline uint8_t columnForKey(Key key) {
return (int)key%Config::numberOfColumns;
}
inline void activateRow(uint8_t row) {
/* In open-drain mode, a 0 in the register drives the pin low, and a 1 lets
* the pin floating (Hi-Z). So we want to set the current row to zero and all
* the others to 1. */
uint16_t rowState = ~(1<<row);
// TODO: Assert pin numbers are sequentials and dynamically find 9 and 0
Config::RowGPIO.ODR()->setBitRange(9, 0, rowState);
// TODO: 100 us seems to work, but wasn't really calculated
Timing::usleep(100);
}
inline bool columnIsActive(uint8_t column) {
return !(Config::ColumnGPIO.IDR()->getBitRange(column,column));
}
}
}
}
#endif

View File

@@ -1,18 +1,19 @@
#include <ion/led.h>
#include <ion/display.h>
#include "device.h"
#include "led.h"
#include "regs/regs.h"
// Public Ion::LED methods
#include <ion/led.h>
#include <drivers/config/led.h>
static KDColor sLedColor = KDColorBlack;
KDColor Ion::LED::getColor() {
namespace Ion {
namespace LED {
using namespace Ion::Device::LED;
KDColor getColor() {
return sLedColor;
}
void Ion::LED::setColor(KDColor c) {
void setColor(KDColor c) {
sLedColor = c;
/* Active all RGB colors */
@@ -22,10 +23,10 @@ void Ion::LED::setColor(KDColor c) {
/* Set the PWM duty cycles to display the right color */
constexpr float maxColorValue = (float)((1 << 8) -1);
Device::setPeriodAndDutyCycles(Device::Mode::PWM, c.red()/maxColorValue, c.green()/maxColorValue, c.blue()/maxColorValue);
setPeriodAndDutyCycles(Mode::PWM, c.red()/maxColorValue, c.green()/maxColorValue, c.blue()/maxColorValue);
}
void Ion::LED::setBlinking(uint16_t period, float dutyCycle) {
void setBlinking(uint16_t period, float dutyCycle) {
/* We want to use the PWM at a slow rate to display a seeable blink.
* Consequently, we do not use PWM to display the right color anymore but to
* blink. We cannot use the PWM to display the exact color so we 'project the
@@ -34,14 +35,15 @@ void Ion::LED::setBlinking(uint16_t period, float dutyCycle) {
TIM3.CCMR()->setOC4M(sLedColor.green() > 0 ? TIM<Register16>::CCMR::OCM::PWM1 : TIM<Register16>::CCMR::OCM::ForceInactive);
TIM3.CCMR()->setOC3M(sLedColor.blue() > 0 ? TIM<Register16>::CCMR::OCM::PWM1 : TIM<Register16>::CCMR::OCM::ForceInactive);
Device::setPeriodAndDutyCycles(Device::Mode::Blink, dutyCycle, dutyCycle, dutyCycle, period);
setPeriodAndDutyCycles(Mode::Blink, dutyCycle, dutyCycle, dutyCycle, period);
}
// Private Ion::Device::LED methods
}
}
namespace Ion {
namespace LED {
namespace Device {
namespace LED {
void init() {
initGPIO();
@@ -57,14 +59,14 @@ void initGPIO() {
/* RED_LED(PC7), GREEN_LED(PB1), and BLUE_LED(PB0) are driven using a timer,
* which is an alternate function. More precisely, we will use AF2, which maps
* PB0 to TIM3_CH2, PB1 to TIM3_CH4, and PC7 to TIM3_CH2. */
for(const GPIOPin & g : RGBPins) {
for(const GPIOPin & g : Config::RGBPins) {
g.group().MODER()->setMode(g.pin(), GPIO::MODER::Mode::AlternateFunction);
g.group().AFR()->setAlternateFunction(g.pin(), GPIO::AFR::AlternateFunction::AF2);
}
}
void shutdownGPIO() {
for(const GPIOPin & g : RGBPins) {
for(const GPIOPin & g : Config::RGBPins) {
g.group().MODER()->setMode(g.pin(), GPIO::MODER::Mode::Analog);
g.group().PUPDR()->setPull(g.pin(), GPIO::PUPDR::Pull::None);
}

View File

@@ -0,0 +1,36 @@
#ifndef ION_DEVICE_SHARED_LED_H
#define ION_DEVICE_SHARED_LED_H
#include <stdint.h>
namespace Ion {
namespace Device {
namespace LED {
enum class Mode {
PWM,
Blink
};
enum class Color {
Red,
Green,
Blue
};
void init();
void shutdown();
void setPeriodAndDutyCycles(Mode mode, float dutyCycleRed, float dutyCycleGreen, float dutyCycleBlue, uint16_t period = 0);
void initGPIO();
void shutdownGPIO();
void initTimer();
void shutdownTimer();
constexpr uint16_t PWMPeriod = 40000;
}
}
}
#endif

View File

@@ -1,14 +1,15 @@
#include <ion.h>
#include "battery.h"
#include "device.h"
#include "display.h"
#include "keyboard.h"
#include "led.h"
#include "usb.h"
#include "wakeup.h"
#include "regs/regs.h"
#include <ion/keyboard.h>
#include <ion/led.h>
#include <ion/usb.h>
#include <drivers/board.h>
#include <drivers/keyboard.h>
#include <drivers/wakeup.h>
#include <regs/regs.h>
void Ion::Power::suspend(bool checkIfPowerKeyReleased) {
namespace Ion {
namespace Power {
void suspend(bool checkIfPowerKeyReleased) {
bool isLEDActive = Ion::LED::getColor() != KDColorBlack;
if (checkIfPowerKeyReleased) {
/* Wait until power is released to avoid restarting just after suspending */
@@ -18,7 +19,7 @@ void Ion::Power::suspend(bool checkIfPowerKeyReleased) {
isPowerDown = scan.keyDown(Keyboard::Key::B2);
}
}
Device::shutdownPeripherals(isLEDActive);
Device::Board::shutdownPeripherals(isLEDActive);
PWR.CR()->setLPDS(true); // Turn the regulator off. Takes longer to wake up.
PWR.CR()->setFPDS(true); // Put the flash to sleep. Takes longer to wake up.
@@ -33,13 +34,13 @@ void Ion::Power::suspend(bool checkIfPowerKeyReleased) {
//Ion::LED::setCharging(Ion::USB::isPlugged(), Ion::Battery::isCharging());
#endif
WakeUp::Device::onPowerKeyDown();
WakeUp::Device::onUSBPlugging();
Device::WakeUp::onPowerKeyDown();
Device::WakeUp::onUSBPlugging();
#if EPSILON_LED_WHILE_CHARGING
WakeUp::Device::onChargingEvent();
Device::WakeUp::onChargingEvent();
#endif
Device::shutdownClocks(isLEDActive);
Device::Board::shutdownClocks(isLEDActive);
/* To enter sleep, we need to issue a WFE instruction, which waits for the
* event flag to be set and then clears it. However, the event flag might
@@ -51,11 +52,11 @@ void Ion::Power::suspend(bool checkIfPowerKeyReleased) {
asm("nop");
asm("wfe");
Device::initClocks();
Device::Board::initClocks();
Keyboard::Device::init();
Device::Keyboard::init();
Keyboard::State scan = Keyboard::scan();
Keyboard::Device::shutdown();
Device::Keyboard::shutdown();
Ion::Keyboard::State OnlyPowerKeyDown = Keyboard::State(Keyboard::Key::B2);
if (scan == OnlyPowerKeyDown || USB::isPlugged()) {
@@ -63,7 +64,10 @@ void Ion::Power::suspend(bool checkIfPowerKeyReleased) {
break;
}
}
Device::initClocks();
Device::Board::initClocks();
Device::initPeripherals();
Device::Board::initPeripherals();
}
}
}

View File

@@ -0,0 +1,18 @@
#include <ion.h>
#include <regs/regs.h>
uint32_t Ion::random() {
bool initialRNGEngineState = RCC.AHB2ENR()->getRNGEN();
RCC.AHB2ENR()->setRNGEN(true);
RNG.CR()->setRNGEN(true);
while (RNG.SR()->getDRDY() == 0) {
}
uint32_t result = RNG.DR()->get();
RNG.CR()->setRNGEN(false);
RCC.AHB2ENR()->setRNGEN(initialRNGEngineState);
return result;
}

View File

@@ -0,0 +1,32 @@
#include "reset.h"
#include <regs/regs.h>
namespace Ion {
namespace Device {
namespace Reset {
void core() {
// Perform a full core reset
CM4.AIRCR()->requestReset();
}
void jump() {
uint32_t * stackPointerAddress = reinterpret_cast<uint32_t *>(0x08000000);
uint32_t * resetHandlerAddress = reinterpret_cast<uint32_t *>(0x08000004);
/* Jump to the reset service routine after having reset the stack pointer.
* Both addresses are fetched from the base of the Flash memory, just like a
* real reset would. These operations should be made at once, otherwise the C
* compiler might emit some instructions that modify the stack inbetween. */
asm volatile (
"msr MSP, %[stackPointer] ; bx %[resetHandler]"
: :
[stackPointer] "r" (*stackPointerAddress),
[resetHandler] "r" (*resetHandlerAddress)
);
}
}
}
}

View File

@@ -0,0 +1,15 @@
#ifndef ION_DEVICE_SHARED_RESET_H
#define ION_DEVICE_SHARED_RESET_H
namespace Ion {
namespace Device {
namespace Reset {
void core();
void jump();
}
}
}
#endif

View File

@@ -0,0 +1,30 @@
#include "serial_number.h"
#include "base64.h"
namespace Ion {
using namespace Ion::Device::SerialNumber;
const char * serialNumber() {
static char serialNumber[Length + 1] = {0};
if (serialNumber[0] == 0) {
copy(serialNumber);
}
return serialNumber;
}
}
namespace Ion {
namespace Device {
namespace SerialNumber {
void copy(char * buffer) {
const unsigned char * rawUniqueID = (const unsigned char *)0x1FFF7A10;
Base64::encode(rawUniqueID, 12, buffer);
buffer[Length] = 0;
}
}
}
}

View File

@@ -0,0 +1,19 @@
#ifndef ION_DEVICE_SHARED_SERIAL_NUMBER_H
#define ION_DEVICE_SHARED_SERIAL_NUMBER_H
namespace Ion {
namespace Device {
namespace SerialNumber {
/* The serial number is 96 bits long. That's equal to 16 digits in base 64. We
* expose a convenient "copySerialNumber" routine which can be called without
* using a static variable (and therefore without a .bss section). This is used
* in the RAM'ed DFU bootloader. */
constexpr static int Length = 16;
void copy(char * buffer);
}
}
}
#endif

View File

@@ -1,19 +1,19 @@
#include "swd.h"
#include "regs/regs.h"
#include <drivers/config/swd.h>
namespace Ion {
namespace SWD {
namespace Device {
namespace SWD {
void init() {
for(const GPIOPin & g : Pins) {
for(const GPIOPin & g : Config::Pins) {
g.group().MODER()->setMode(g.pin(), GPIO::MODER::Mode::AlternateFunction);
g.group().AFR()->setAlternateFunction(g.pin(), GPIO::AFR::AlternateFunction::AF0);
}
}
void shutdown() {
for(const GPIOPin & g : Pins) {
for(const GPIOPin & g : Config::Pins) {
g.group().MODER()->setMode(g.pin(), GPIO::MODER::Mode::Analog);
g.group().PUPDR()->setPull(g.pin(), GPIO::PUPDR::Pull::None);
}

View File

@@ -0,0 +1,15 @@
#ifndef ION_DEVICE_SHARED_SWD_H
#define ION_DEVICE_SHARED_SWD_H
namespace Ion {
namespace Device {
namespace SWD {
void init();
void shutdown();
}
}
}
#endif

View File

@@ -0,0 +1,53 @@
#include "timing.h"
#include <ion/timing.h>
#include <drivers/config/timing.h>
namespace Ion {
namespace Timing {
using namespace Ion::Device::Timing;
/* TODO: The delay methods 'msleep' and 'usleep' are currently dependent on the
* optimizations chosen by the compiler. To prevent that and to gain in
* precision, we could use the controller cycle counter (Systick). */
void msleep(uint32_t ms) {
for (volatile uint32_t i=0; i<Config::LoopsPerMillisecond*ms; i++) {
__asm volatile("nop");
}
}
void usleep(uint32_t us) {
for (volatile uint32_t i=0; i<Config::LoopsPerMicrosecond*us; i++) {
__asm volatile("nop");
}
}
uint64_t millis() {
return MillisElapsed;
}
}
}
namespace Ion {
namespace Device {
namespace Timing {
volatile uint64_t MillisElapsed = 0;
void init() {
CM4.SYST_RVR()->setRELOAD(Config::SysTickPerMillisecond - 1); // Remove 1 because the counter resets *after* counting to 0
CM4.SYST_CVR()->setCURRENT(0);
CM4.SYST_CSR()->setCLKSOURCE(CM4::SYST_CSR::CLKSOURCE::AHB_DIV8);
CM4.SYST_CSR()->setTICKINT(true);
CM4.SYST_CSR()->setENABLE(true);
}
void shutdown() {
CM4.SYST_CSR()->setENABLE(false);
CM4.SYST_CSR()->setTICKINT(false);
}
}
}
}

View File

@@ -0,0 +1,19 @@
#ifndef ION_DEVICE_SHARED_TIMING_H
#define ION_DEVICE_SHARED_TIMING_H
#include <stdint.h>
namespace Ion {
namespace Device {
namespace Timing {
void init();
void shutdown();
extern volatile uint64_t MillisElapsed;
}
}
}
#endif

View File

@@ -1,16 +1,14 @@
#include <ion/usb.h>
#include "usb.h"
#include <ion/display.h>
#include "device.h"
#include "display.h"
#include "regs/regs.h"
#include <stdlib.h>
#include <ion/usb.h>
#include <drivers/config/usb.h>
namespace Ion {
namespace USB {
using namespace Ion::Device::USB;
bool isPlugged() {
return Device::VbusPin.group().IDR()->get(Device::VbusPin.pin());
return Config::VbusPin.group().IDR()->get(Config::VbusPin.pin());
}
bool isEnumerated() {
@@ -38,8 +36,8 @@ void disable() {
}
namespace Ion {
namespace USB {
namespace Device {
namespace USB {
void init() {
initGPIO();
@@ -69,22 +67,22 @@ void initGPIO() {
* plugged, the pin must be pulled down. */
// FIXME: Understand how the Vbus pin really works!
#if 0
VbusPin.group().MODER()->setMode(VbusPin.pin(), GPIO::MODER::Mode::Input);
VbusPin.group().PUPDR()->setPull(VbusPin.pin(), GPIO::PUPDR::Pull::Down);
Config::VbusPin.group().MODER()->setMode(Config::VbusPin.pin(), GPIO::MODER::Mode::Input);
Config::VbusPin.group().PUPDR()->setPull(Config::VbusPin.pin(), GPIO::PUPDR::Pull::Down);
#else
VbusPin.group().MODER()->setMode(VbusPin.pin(), GPIO::MODER::Mode::AlternateFunction);
VbusPin.group().AFR()->setAlternateFunction(VbusPin.pin(), GPIO::AFR::AlternateFunction::AF10);
Config::VbusPin.group().MODER()->setMode(Config::VbusPin.pin(), GPIO::MODER::Mode::AlternateFunction);
Config::VbusPin.group().AFR()->setAlternateFunction(Config::VbusPin.pin(), GPIO::AFR::AlternateFunction::AF10);
#endif
DmPin.group().MODER()->setMode(DmPin.pin(), GPIO::MODER::Mode::AlternateFunction);
DmPin.group().AFR()->setAlternateFunction(DmPin.pin(), GPIO::AFR::AlternateFunction::AF10);
Config::DmPin.group().MODER()->setMode(Config::DmPin.pin(), GPIO::MODER::Mode::AlternateFunction);
Config::DmPin.group().AFR()->setAlternateFunction(Config::DmPin.pin(), GPIO::AFR::AlternateFunction::AF10);
DpPin.group().MODER()->setMode(DpPin.pin(), GPIO::MODER::Mode::AlternateFunction);
DpPin.group().AFR()->setAlternateFunction(DpPin.pin(), GPIO::AFR::AlternateFunction::AF10);
Config::DpPin.group().MODER()->setMode(Config::DpPin.pin(), GPIO::MODER::Mode::AlternateFunction);
Config::DpPin.group().AFR()->setAlternateFunction(Config::DpPin.pin(), GPIO::AFR::AlternateFunction::AF10);
}
void shutdownGPIO() {
constexpr static GPIOPin USBPins[] = {DpPin, DmPin, VbusPin};
constexpr static GPIOPin USBPins[] = {Config::DpPin, Config::DmPin, Config::VbusPin};
for (const GPIOPin & g : USBPins) {
g.group().MODER()->setMode(g.pin(), GPIO::MODER::Mode::Analog);
g.group().PUPDR()->setPull(g.pin(), GPIO::PUPDR::Pull::None);

View File

@@ -0,0 +1,19 @@
#ifndef ION_DEVICE_SHARED_USB_H
#define ION_DEVICE_SHARED_USB_H
namespace Ion {
namespace Device {
namespace USB {
void init();
void shutdown();
void initGPIO();
void shutdownGPIO();
void initOTG();
void shutdownOTG();
}
}
}
#endif

View File

@@ -0,0 +1,72 @@
#include "wakeup.h"
#include <drivers/battery.h>
#include <drivers/keyboard.h>
#include <drivers/usb.h>
#include <drivers/config/battery.h>
#include <drivers/config/keyboard.h>
#include <drivers/config/usb.h>
#include <regs/regs.h>
namespace Ion {
namespace Device {
namespace WakeUp {
void onChargingEvent() {
Battery::initGPIO();
/* Warning: pins with the same number in different groups cannot be set as
* source input for EXTI at the same time. Here, EXTICR1 register is filled
* between position 0-3 (charging pin = 0) with
* 0000 (ChargingGPIO = group A). */
SYSCFG.EXTICR1()->setEXTI(Battery::Config::ChargingPin.pin(), Battery::Config::ChargingPin.group());
EXTI.EMR()->set(Battery::Config::ChargingPin.pin(), true);
/* We need to detect when the battery stops charging. We set the
* wake up event on the rising edge. */
EXTI.RTSR()->set(Battery::Config::ChargingPin.pin(), true);
}
void onUSBPlugging() {
USB::initGPIO();
/* Here, EXTICR3 register is filled between position 4-7 (Vbus pin = 9) with
* 0000 (Vbus GPIO = group A). */
SYSCFG.EXTICR3()->setEXTI(USB::Config::VbusPin.pin(), USB::Config::VbusPin.group());
EXTI.EMR()->set(USB::Config::VbusPin.pin(), true);
#if EPSILON_LED_WHILE_CHARGING
EXTI.FTSR()->set(USB::Config::VbusPin.pin(), true);
#endif
EXTI.RTSR()->set(USB::Config::VbusPin.pin(), true);
}
void onPowerKeyDown() {
Keyboard::Key key = Keyboard::Key::B2;
uint8_t rowPin = Keyboard::Config::RowPins[Keyboard::rowForKey(key)];
Keyboard::Config::RowGPIO.MODER()->setMode(rowPin, GPIO::MODER::Mode::Output);
Keyboard::Config::RowGPIO.OTYPER()->setType(rowPin, GPIO::OTYPER::Type::OpenDrain);
Keyboard::Config::RowGPIO.ODR()->set(rowPin, 0);
uint8_t column = Keyboard::columnForKey(key);
uint8_t columnPin = Keyboard::Config::ColumnPins[column];
Keyboard::Config::ColumnGPIO.MODER()->setMode(columnPin, GPIO::MODER::Mode::Input);
Keyboard::Config::ColumnGPIO.PUPDR()->setPull(columnPin, GPIO::PUPDR::Pull::Up);
/* Here, EXTICR1 register is filled between position 4-7 (column pin = 1) with
* 0010 (ColumnGPIO = group C). */
SYSCFG.EXTICR1()->setEXTI(columnPin, Keyboard::Config::ColumnGPIO);
EXTI.EMR()->set(columnPin, true);
/* When the key is pressed, it will go from 1 (because it's pulled up) to
* zero (because it's connected to the open-drain output. In other words,
* we're waiting for a falling edge. */
EXTI.FTSR()->set(columnPin, true);
}
}
}
}

View File

@@ -4,8 +4,8 @@
#include "regs/regs.h"
namespace Ion {
namespace WakeUp {
namespace Device {
namespace WakeUp {
/* All wakeup functions can be called together without overwriting the same
* register. All togethed, they will set SYSCFG and EXTi registers as follow:

View File

@@ -0,0 +1,73 @@
usb_objs += $(addprefix ion/src/device/shared/usb/, \
calculator.o \
dfu_interface.o\
)
usb_objs += $(addprefix ion/src/device/shared/usb/stack/, \
device.o\
endpoint0.o \
interface.o\
request_recipient.o\
setup_packet.o\
streamable.o\
)
usb_objs += $(addprefix ion/src/device/shared/usb/stack/descriptor/, \
bos_descriptor.o\
configuration_descriptor.o \
descriptor.o\
device_descriptor.o\
device_capability_descriptor.o\
dfu_functional_descriptor.o\
extended_compat_id_descriptor.o \
interface_descriptor.o\
language_id_string_descriptor.o \
microsoft_os_string_descriptor.o\
platform_device_capability_descriptor.o\
string_descriptor.o\
url_descriptor.o\
webusb_platform_descriptor.o\
)
$(usb_objs): SFLAGS += $(ION_DEVICE_SFLAGS)
EPSILON_USB_DFU_XIP ?= 0
ifeq ($(EPSILON_USB_DFU_XIP),1)
ion_device_objs += ion/src/device/shared/usb/dfu_xip.o
ion_device_objs += $(usb_objs)
else
dfu_objs += liba/src/assert.o
dfu_objs += liba/src/strlen.o
dfu_objs += liba/src/strlcpy.o
dfu_objs += liba/src/memset.o
dfu_objs += liba/src/memcpy.o
dfu_objs += libaxx/src/cxxabi/pure_virtual.o
dfu_objs += ion/src/device/shared/usb/boot.o
dfu_objs += $(addprefix ion/src/device/shared/drivers/, \
base64.o \
external_flash.o \
flash.o \
keyboard.o \
reset.o \
serial_number.o \
timing.o \
usb.o \
)
ion/src/device/shared/usb/dfu.elf: LDSCRIPT = ion/src/device/shared/usb/dfu.ld
ion/src/device/shared/usb/dfu.elf: $(usb_objs) $(dfu_objs)
ion/src/device/shared/usb/dfu.o: ion/src/device/shared/usb/dfu.bin
@echo "OBJCOPY $@"
$(Q) $(OBJCOPY) -I binary -O elf32-littlearm -B arm --rename-section .data=.rodata --redefine-sym _binary_ion_src_device_shared_usb_dfu_bin_start=_dfu_bootloader_flash_start --redefine-sym _binary_ion_src_device_shared_usb_dfu_bin_end=_dfu_bootloader_flash_end $< $@
ion_device_objs += ion/src/device/shared/usb/dfu.o
ion_device_objs += ion/src/device/shared/usb/dfu_relocated.o
products += $(usb_objs) $(addprefix ion/src/device/shared/usb/dfu, .elf .bin)
endif

View File

@@ -1,27 +1,27 @@
#include "calculator.h"
#include <ion/usb.h>
#include <ion/src/device/regs/regs.h>
#include <ion/src/device/device.h>
#include <ion/src/device/keyboard.h>
#include <drivers/keyboard.h>
#include <drivers/reset.h>
#include <drivers/serial_number.h>
namespace Ion {
namespace USB {
namespace Device {
namespace USB {
void Calculator::PollAndReset(bool exitWithKeyboard) {
char serialNumber[Ion::Device::SerialNumberLength+1];
Ion::Device::copySerialNumber(serialNumber);
char serialNumber[Ion::Device::SerialNumber::Length+1];
Ion::Device::SerialNumber::copy(serialNumber);
Calculator c(serialNumber);
/* Leave DFU mode if the Back key is pressed, the calculator unplugged or the
* USB core soft-disconnected. */
Ion::Keyboard::Key exitKey = Ion::Keyboard::Key::A6;
uint8_t exitKeyRow = Ion::Keyboard::Device::rowForKey(exitKey);
uint8_t exitKeyColumn = Ion::Keyboard::Device::columnForKey(exitKey);
uint8_t exitKeyRow = Ion::Device::Keyboard::rowForKey(exitKey);
uint8_t exitKeyColumn = Ion::Device::Keyboard::columnForKey(exitKey);
Ion::Keyboard::Device::activateRow(exitKeyRow);
Ion::Device::Keyboard::activateRow(exitKeyRow);
while (!(exitWithKeyboard && Ion::Keyboard::Device::columnIsActive(exitKeyColumn)) &&
while (!(exitWithKeyboard && Ion::Device::Keyboard::columnIsActive(exitKeyColumn)) &&
Ion::USB::isPlugged() &&
!c.isSoftDisconnected()) {
c.poll();
@@ -35,7 +35,7 @@ void Calculator::PollAndReset(bool exitWithKeyboard) {
* thing to do but would therefore result in the device entering the ROMed
* DFU bootloader, which we want to avoid. By performing a jump-reset, we
* will enter the newly flashed firmware. */
Ion::Device::jumpReset();
Ion::Device::Reset::jump();
}
}

View File

@@ -1,5 +1,5 @@
#ifndef ION_DEVICE_USB_CALCULATOR_H
#define ION_DEVICE_USB_CALCULATOR_H
#ifndef ION_DEVICE_SHARED_USB_CALCULATOR_H
#define ION_DEVICE_SHARED_USB_CALCULATOR_H
#include <stddef.h>
#include <assert.h>
@@ -19,8 +19,8 @@
#include "stack/descriptor/webusb_platform_descriptor.h"
namespace Ion {
namespace USB {
namespace Device {
namespace USB {
class Calculator : public Device {
public:

View File

@@ -1,11 +1,11 @@
#include "dfu_interface.h"
#include <string.h>
#include <ion/src/device/flash.h>
#include <ion/src/device/external_flash.h>
#include "../drivers/flash.h"
#include "../drivers/external_flash.h"
namespace Ion {
namespace USB {
namespace Device {
namespace USB {
static inline uint32_t min(uint32_t x, uint32_t y) { return (x<y ? x : y); }
@@ -174,7 +174,7 @@ void DFUInterface::eraseCommand(uint8_t * transferBuffer, uint16_t transferBuffe
if (transferBufferLength == 1) {
// Mass erase
m_erasePage = Flash::Device::NumberOfSectors + ExternalFlash::Device::NumberOfSectors;
m_erasePage = Flash::NumberOfSectors + ExternalFlash::NumberOfSectors;
return;
}
@@ -187,9 +187,9 @@ void DFUInterface::eraseCommand(uint8_t * transferBuffer, uint16_t transferBuffe
+ (transferBuffer[4] << 24);
if (eraseAddress >= k_flashStartAddress && eraseAddress <= k_flashEndAddress) {
m_erasePage = Flash::Device::SectorAtAddress(eraseAddress);
m_erasePage = Flash::SectorAtAddress(eraseAddress);
} else if (eraseAddress >= k_externalFlashStartAddress && eraseAddress <= k_externalFlashEndAddress) {
m_erasePage = Flash::Device::NumberOfSectors + ExternalFlash::Device::SectorAtAddress(eraseAddress - k_externalFlashStartAddress);
m_erasePage = Flash::NumberOfSectors + ExternalFlash::SectorAtAddress(eraseAddress - k_externalFlashStartAddress);
} else {
// Unrecognized sector
m_state = State::dfuERROR;
@@ -204,13 +204,13 @@ void DFUInterface::eraseMemoryIfNeeded() {
return;
}
if (m_erasePage == Flash::Device::NumberOfSectors + ExternalFlash::Device::NumberOfSectors) {
Flash::Device::MassErase();
ExternalFlash::Device::MassErase();
} else if (m_erasePage < Flash::Device::NumberOfSectors) {
Flash::Device::EraseSector(m_erasePage);
if (m_erasePage == Flash::NumberOfSectors + ExternalFlash::NumberOfSectors) {
Flash::MassErase();
ExternalFlash::MassErase();
} else if (m_erasePage < Flash::NumberOfSectors) {
Flash::EraseSector(m_erasePage);
} else {
ExternalFlash::Device::EraseSector(m_erasePage - Flash::Device::NumberOfSectors);
ExternalFlash::EraseSector(m_erasePage - Flash::NumberOfSectors);
}
/* Put an out of range value in m_erasePage to indicate that no erase is
@@ -223,13 +223,13 @@ void DFUInterface::eraseMemoryIfNeeded() {
void DFUInterface::writeOnMemory() {
if (m_writeAddress >= k_flashStartAddress && m_writeAddress <= k_flashEndAddress) {
// Write to the Flash memory
Flash::Device::WriteMemory(m_largeBuffer, reinterpret_cast<uint8_t *>(m_writeAddress), m_largeBufferLength);
Flash::WriteMemory(m_largeBuffer, reinterpret_cast<uint8_t *>(m_writeAddress), m_largeBufferLength);
} else if (m_writeAddress >= k_sramStartAddress && m_writeAddress <= k_sramEndAddress) {
// Write on SRAM
// FIXME We should check that we are not overriding the current instructions.
memcpy((void *)m_writeAddress, m_largeBuffer, m_largeBufferLength);
} else if (m_writeAddress >= k_externalFlashStartAddress && m_writeAddress <= k_externalFlashEndAddress) {
ExternalFlash::Device::WriteMemory(m_largeBuffer, reinterpret_cast<uint8_t *>(m_writeAddress) - k_externalFlashStartAddress, m_largeBufferLength);
ExternalFlash::WriteMemory(m_largeBuffer, reinterpret_cast<uint8_t *>(m_writeAddress) - k_externalFlashStartAddress, m_largeBufferLength);
} else {
// Invalid write address
m_largeBufferLength = 0;

View File

@@ -1,5 +1,5 @@
#ifndef ION_DEVICE_USB_DFU_INTERFACE_H
#define ION_DEVICE_USB_DFU_INTERFACE_H
#ifndef ION_DEVICE_SHARED_USB_DFU_INTERFACE_H
#define ION_DEVICE_SHARED_USB_DFU_INTERFACE_H
#include <assert.h>
#include <stddef.h>
@@ -10,8 +10,8 @@
#include "stack/streamable.h"
namespace Ion {
namespace USB {
namespace Device {
namespace USB {
class DFUInterface : public Interface {

View File

@@ -1,7 +1,7 @@
#include <ion/usb.h>
#include <string.h>
#include <assert.h>
#include <ion/src/device/device.h>
#include "../drivers/timing.h"
extern char _stack_end;
extern char _dfu_bootloader_flash_start;
@@ -51,7 +51,7 @@ void DFU() {
/* 4- Disable all interrupts
* The interrupt service routines live in the Flash and could be overwritten
* by garbage during a firmware upgrade opration, so we disable them. */
Device::shutdownSysTick();
Device::Timing::shutdown();
/* 5- Jump to DFU bootloader code. We made sure in the linker script that the
* first function we want to call is at the beginning of the DFU code. */
@@ -70,7 +70,7 @@ void DFU() {
dfu_bootloader_entry(true);
/* 5- Restore interrupts */
Device::initSysTick();
Device::Timing::init();
/* 6- That's all. The DFU bootloader on the stack is now dead code that will
* be overwritten when the stack grows. */

View File

@@ -1,11 +1,10 @@
#include "calculator.h"
#include "../device.h"
namespace Ion {
namespace USB {
void DFU() {
Ion::USB::Device::Calculator::PollAndReset(true);
Ion::Device::USB::Calculator::PollAndReset(true);
}
}

View File

@@ -1,8 +1,8 @@
#include "bos_descriptor.h"
namespace Ion {
namespace USB {
namespace Device {
namespace USB {
void BOSDescriptor::push(Channel * c) const {
Descriptor::push(c);

View File

@@ -1,12 +1,12 @@
#ifndef ION_DEVICE_USB_STACK_BOS_DESCRIPTOR_H
#define ION_DEVICE_USB_STACK_BOS_DESCRIPTOR_H
#ifndef ION_DEVICE_SHARED_USB_STACK_BOS_DESCRIPTOR_H
#define ION_DEVICE_SHARED_USB_STACK_BOS_DESCRIPTOR_H
#include "descriptor.h"
#include "device_capability_descriptor.h"
namespace Ion {
namespace USB {
namespace Device {
namespace USB {
class BOSDescriptor : public Descriptor {
public:

View File

@@ -1,8 +1,8 @@
#include "configuration_descriptor.h"
namespace Ion {
namespace USB {
namespace Device {
namespace USB {
void ConfigurationDescriptor::push(Channel * c) const {
Descriptor::push(c);

View File

@@ -1,12 +1,12 @@
#ifndef ION_DEVICE_USB_STACK_CONFIGURATION_DESCRIPTOR_H
#define ION_DEVICE_USB_STACK_CONFIGURATION_DESCRIPTOR_H
#ifndef ION_DEVICE_SHARED_USB_STACK_CONFIGURATION_DESCRIPTOR_H
#define ION_DEVICE_SHARED_USB_STACK_CONFIGURATION_DESCRIPTOR_H
#include "descriptor.h"
#include "interface_descriptor.h"
namespace Ion {
namespace USB {
namespace Device {
namespace USB {
class ConfigurationDescriptor : public Descriptor {
public:

Some files were not shown because too many files have changed in this diff Show More