[ion/device] Get N0101 working

This commit is contained in:
Romain Goyet
2019-02-05 17:07:42 +01:00
parent b699777491
commit 6dcdbede56
24 changed files with 536 additions and 11 deletions

View File

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

View File

@@ -0,0 +1,6 @@
#ifndef ION_DEVICE_N0100_REGS_CONFIG_FLASH_H
#define ION_DEVICE_N0100_REGS_CONFIG_FLASH_H
#define REGS_FLASH_CONFIG_ART 0
#endif

View File

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

View File

@@ -0,0 +1,179 @@
#include <drivers/board.h>
#include <drivers/cache.h>
#include <regs/regs.h>
#include <ion.h>
// Public Ion methods
const char * Ion::fccId() {
return "2ALWP-N0101";
}
// Private Ion::Device methods
namespace Ion {
namespace Device {
namespace Board {
void initL1Cache() {
Cache::enableICache();
Cache::enableDCache();
}
void initMPU() {
// Configure MPU settings for the FMC memory area
// This is needed for interfacing with the LCD
MPU.RNR()->setREGION(0x00);
MPU.RBAR()->setADDR(0x60000000);
MPU.RASR()->setSIZE(MPU::RASR::RegionSize::_32MB);
MPU.RASR()->setXN(true);
MPU.RASR()->setAP(MPU::RASR::AccessPermission::RW);
MPU.RASR()->setTEX(2);
MPU.RASR()->setS(0);
MPU.RASR()->setC(0);
MPU.RASR()->setB(0);
MPU.RASR()->setENABLE(true);
MPU.CTRL()->setPRIVDEFENA(true);
MPU.CTRL()->setENABLE(true);
}
void init() {
initFPU();
initL1Cache();
initMPU();
initClocks();
// Ensure right location of interrupt vectors
// The bootloader leaves its own after flashing
SYSCFG.MEMRMP()->setMEM_MODE(SYSCFG::MEMRMP::MemMode::MainFlashmemory);
CM4.VTOR()->setVTOR((void*) 0);
// Put all inputs as Analog Input, No pull-up nor pull-down
// Except for the SWD port (PB3, PA13, PA14)
GPIOA.MODER()->set(0xEBFFFFFF);
GPIOA.PUPDR()->set(0x24000000);
GPIOB.MODER()->set(0xFFFFFFBF);
GPIOB.PUPDR()->set(0x00000000);
for (int g=2; g<5; g++) {
GPIO(g).MODER()->set(0xFFFFFFFF); // All to "Analog"
GPIO(g).PUPDR()->set(0x00000000); // All to "None"
}
initPeripherals();
}
void initClocks() {
/* System clock
* Configure the CPU at 96 MHz, APB2 and USB at 48 MHz. */
/* After reset the Flash runs as fast as the CPU. When we clock the CPU faster
* the flash memory cannot follow and therefore flash memory accesses need to
* wait a little bit.
* The spec tells us that at 2.8V and over 210MHz the flash expects 7 WS. */
FLASH.ACR()->setLATENCY(7);
/* Enable prefetching flash instructions */
/* Fetching instructions increases slightly the power consumption but the
* increase is negligible compared to the screen consumption. */
FLASH.ACR()->setPRFTEN(true);
/* Enable the ART */
FLASH.ACR()->setARTEN(true);
/* After reset, the device is using the high-speed internal oscillator (HSI)
* as a clock source, which runs at a fixed 16 MHz frequency. The HSI is not
* accurate enough for reliable USB operation, so we need to use the external
* high-speed oscillator (HSE). */
// Enable the HSE and wait for it to be ready
RCC.CR()->setHSEON(true);
while(!RCC.CR()->getHSERDY()) {
}
/* Given the crystal used on our device, the HSE will oscillate at 25 MHz. By
* piping it through a phase-locked loop (PLL) we can derive other frequencies
* for use in different parts of the system. */
// Configure the PLL ratios and use HSE as a PLL input
RCC.PLLCFGR()->setPLLM(25);
RCC.PLLCFGR()->setPLLN(384);
RCC.PLLCFGR()->setPLLQ(8);
RCC.PLLCFGR()->setPLLSRC(RCC::PLLCFGR::PLLSRC::HSE);
// 192 MHz is too fast for APB1. Divide it by four to reach 48 MHz
RCC.CFGR()->setPPRE1(RCC::CFGR::APBPrescaler::AHBDividedBy4);
// 192 MHz is too fast for APB2. Divide it by two to reach 96 MHz
RCC.CFGR()->setPPRE2(RCC::CFGR::APBPrescaler::AHBDividedBy2);
/* If you want to considerably slow down the whole machine uniformely, which
* can be very useful to diagnose performance issues, just uncomment the line
* below. Note that even booting takes a few seconds, so don't be surprised
* if the screen is black for a short while upon booting. */
// RCC.CFGR()->setHPRE(RCC::CFGR::AHBPrescaler::SysClkDividedBy128);
// Enable the PLL and wait for it to be ready
RCC.CR()->setPLLON(true);
while(!RCC.CR()->getPLLRDY()) {
}
// Use the PLL output as a SYSCLK source
RCC.CFGR()->setSW(RCC::CFGR::SW::PLL);
while (RCC.CFGR()->getSWS() != RCC::CFGR::SW::PLL) {
}
// Now that we don't need use it anymore, turn the HSI off
RCC.CR()->setHSION(false);
// Peripheral clocks
// AHB1 bus
// Our peripherals are using GPIO A, B, C, D and E.
// We're not using the CRC nor DMA engines.
class RCC::AHB1ENR ahb1enr(0x00100000); // Reset value
ahb1enr.setGPIOAEN(true);
ahb1enr.setGPIOBEN(true);
ahb1enr.setGPIOCEN(true);
ahb1enr.setGPIODEN(true);
ahb1enr.setGPIOEEN(true);
ahb1enr.setDMA2EN(true);
RCC.AHB1ENR()->set(ahb1enr);
// AHB2 bus
RCC.AHB2ENR()->setOTGFSEN(true);
// AHB3 bus
RCC.AHB3ENR()->setFSMCEN(true);
// APB1 bus
// We're using TIM3 for the LEDs
RCC.APB1ENR()->setTIM3EN(true);
RCC.APB1ENR()->setPWREN(true);
// APB2 bus
class RCC::APB2ENR apb2enr(0); // Reset value
apb2enr.setADC1EN(true);
apb2enr.setSYSCFGEN(true);
RCC.APB2ENR()->set(apb2enr);
}
void shutdownClocks(bool keepLEDAwake) {
// APB2 bus
RCC.APB2ENR()->set(0); // Reset value
// APB1
class RCC::APB1ENR apb1enr(0); // Reset value
// AHB1 bus
class RCC::AHB1ENR ahb1enr(0x00100000); // Reset value
if (keepLEDAwake) {
apb1enr.setTIM3EN(true);
ahb1enr.setGPIOBEN(true);
}
RCC.APB1ENR()->set(apb1enr);
RCC.AHB1ENR()->set(ahb1enr);
RCC.AHB3ENR()->setFSMCEN(false);
}
}
}
}

View File

@@ -1,6 +1,8 @@
#ifndef ION_DEVICE_CACHE_H
#define ION_DEVICE_CACHE_H
#include <regs/regs.h>
namespace Ion {
namespace Device {
namespace Cache {

View File

@@ -0,0 +1,23 @@
#ifndef ION_DEVICE_N0101_CONFIG_BACKLIGHT_H
#define ION_DEVICE_N0101_CONFIG_BACKLIGHT_H
#include <regs/regs.h>
/* Pin | Role | Mode | Function
* -----+-------------------+-----------------------+----------
* PE0 | Backlight Enable | Output |
*/
namespace Ion {
namespace Device {
namespace Backlight {
namespace Config {
constexpr static GPIOPin BacklightPin = GPIOPin(GPIOE, 0);
}
}
}
}
#endif

View File

@@ -0,0 +1,28 @@
#ifndef ION_DEVICE_N0101_CONFIG_BATTERY_H
#define ION_DEVICE_N0101_CONFIG_BATTERY_H
#include <regs/regs.h>
/* Pin | Role | Mode | Function
* -----+-------------------+-----------------------+----------
* PE3 | BAT_CHRG | Input, pulled up | Low = charging, high = full
* PB1 | VBAT_SNS | Analog | ADC1_1
*/
namespace Ion {
namespace Device {
namespace Battery {
namespace Config {
constexpr static GPIOPin ChargingPin = GPIOPin(GPIOE, 3);
constexpr static GPIOPin ADCPin = GPIOPin(GPIOB, 1);
constexpr uint8_t ADCChannel = 9;
constexpr float ADCReferenceVoltage = 2.8f;
constexpr float ADCDividerBridgeRatio = 2.0f;
}
}
}
}
#endif

View File

@@ -0,0 +1,21 @@
#ifndef ION_DEVICE_N0101_CONFIG_CONSOLE_H
#define ION_DEVICE_N0101_CONFIG_CONSOLE_H
#include <regs/regs.h>
namespace Ion {
namespace Device {
namespace Console {
namespace Config {
constexpr static USART Port = USART(6);
constexpr static GPIOPin RxPin = GPIOPin(GPIOC, 7);
constexpr static GPIOPin TxPin = GPIOPin(GPIOC, 6);
constexpr static GPIO::AFR::AlternateFunction AlternateFunction = GPIO::AFR::AlternateFunction::AF8;
}
}
}
}
#endif

View File

@@ -0,0 +1,32 @@
#ifndef ION_DEVICE_N0101_CONFIG_DISPLAY_H
#define ION_DEVICE_N0101_CONFIG_DISPLAY_H
#include <regs/regs.h>
namespace Ion {
namespace Device {
namespace Display {
namespace Config {
constexpr static GPIOPin FSMCPins[] = {
GPIOPin(GPIOD, 0), GPIOPin(GPIOD, 1), GPIOPin(GPIOD, 4), GPIOPin(GPIOD, 5),
GPIOPin(GPIOD, 7), GPIOPin(GPIOD, 8), GPIOPin(GPIOD, 9), GPIOPin(GPIOD, 10),
GPIOPin(GPIOD, 11), GPIOPin(GPIOD, 14), GPIOPin(GPIOD, 15), GPIOPin(GPIOE, 7),
GPIOPin(GPIOE, 8), GPIOPin(GPIOE, 9), GPIOPin(GPIOE, 10), GPIOPin(GPIOE, 11),
GPIOPin(GPIOE, 12), GPIOPin(GPIOE, 13), GPIOPin(GPIOE, 14), GPIOPin(GPIOE, 15),
};
constexpr static GPIOPin PowerPin = GPIOPin(GPIOC, 8);
constexpr static GPIOPin ResetPin = GPIOPin(GPIOE, 1);
constexpr static GPIOPin ExtendedCommandPin = GPIOPin(GPIOD, 6);
constexpr static GPIOPin TearingEffectPin = GPIOPin(GPIOB, 11);
constexpr static DMA DMAEngine = DMA2;
constexpr static int DMAStream = 0;
}
}
}
}
#endif

View File

@@ -0,0 +1,35 @@
#ifndef ION_DEVICE_N0101_CONFIG_EXTERNAL_FLASH_H
#define ION_DEVICE_N0101_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 AFGPIOPin Pins[] = {
AFGPIOPin(GPIOB, 2, GPIO::AFR::AlternateFunction::AF10, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::High),
AFGPIOPin(GPIOB, 6, GPIO::AFR::AlternateFunction::AF9, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::High),
AFGPIOPin(GPIOC, 9, GPIO::AFR::AlternateFunction::AF10, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::High),
AFGPIOPin(GPIOD, 12, GPIO::AFR::AlternateFunction::AF10, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::High),
AFGPIOPin(GPIOD, 13, GPIO::AFR::AlternateFunction::AF10, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::High),
AFGPIOPin(GPIOE, 2, GPIO::AFR::AlternateFunction::AF10, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::High),
};
}
}
}
}
#endif

View File

@@ -0,0 +1,43 @@
#ifndef ION_DEVICE_N0101_CONFIG_KEYBOARD_H
#define ION_DEVICE_N0101_CONFIG_KEYBOARD_H
#include <regs/regs.h>
/* Pin | Role | Mode
* -----+-------------------+--------------------
* PC0 | Keyboard column 1 | Input, pulled up
* PC1 | Keyboard column 2 | Input, pulled up
* PC2 | Keyboard column 3 | Input, pulled up
* PC3 | Keyboard column 4 | Input, pulled up
* PC4 | Keyboard column 5 | Input, pulled up
* PC5 | Keyboard column 6 | Input, pulled up
* PE0 | Keyboard row A | Output, open drain
* PE1 | Keyboard row B | Output, open drain
* PE2 | Keyboard row C | Output, open drain
* PE3 | Keyboard row D | Output, open drain
* PE4 | Keyboard row E | Output, open drain
* PE5 | Keyboard row F | Output, open drain
* PE6 | Keyboard row G | Output, open drain
* PE7 | Keyboard row H | Output, open drain
* PE8 | Keyboard row I | Output, open drain
*/
namespace Ion {
namespace Device {
namespace Keyboard {
namespace Config {
constexpr GPIO RowGPIO = GPIOA;
constexpr uint8_t numberOfRows = 9;
constexpr uint8_t RowPins[numberOfRows] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
constexpr GPIO ColumnGPIO = GPIOC;
constexpr uint8_t numberOfColumns = 6;
constexpr uint8_t ColumnPins[numberOfColumns] = {0, 1, 2, 3, 4, 5};
}
}
}
}
#endif

View File

@@ -0,0 +1,22 @@
#ifndef ION_DEVICE_N0101_CONFIG_LED_H
#define ION_DEVICE_N0101_CONFIG_LED_H
#include <regs/regs.h>
namespace Ion {
namespace Device {
namespace LED {
namespace Config {
constexpr static AFGPIOPin RGBPins[] = {
AFGPIOPin(GPIOB, 4, GPIO::AFR::AlternateFunction::AF2, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::High),
AFGPIOPin(GPIOB, 5, GPIO::AFR::AlternateFunction::AF2, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::High),
AFGPIOPin(GPIOB, 0, GPIO::AFR::AlternateFunction::AF2, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::High),
};
}
}
}
}
#endif

View File

@@ -0,0 +1,22 @@
#ifndef ION_DEVICE_N0101_CONFIG_SWD_H
#define ION_DEVICE_N0101_CONFIG_SWD_H
#include <regs/regs.h>
namespace Ion {
namespace Device {
namespace SWD {
namespace Config {
constexpr static AFGPIOPin Pins[] = {
AFGPIOPin(GPIOA, 13, GPIO::AFR::AlternateFunction::AF0, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::High),
AFGPIOPin(GPIOA, 14, GPIO::AFR::AlternateFunction::AF0, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::High),
AFGPIOPin(GPIOB, 3, GPIO::AFR::AlternateFunction::AF0, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::High),
};
}
}
}
}
#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_N0101_CONFIG_USB_H
#define ION_DEVICE_N0101_CONFIG_USB_H
#include <regs/regs.h>
namespace Ion {
namespace Device {
namespace USB {
namespace Config {
constexpr static AFGPIOPin VbusPin = AFGPIOPin(GPIOA, 9, GPIO::AFR::AlternateFunction::AF10, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::High);
constexpr static AFGPIOPin DmPin = AFGPIOPin(GPIOA, 11, GPIO::AFR::AlternateFunction::AF10, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::High);
constexpr static AFGPIOPin DpPin = AFGPIOPin(GPIOA, 12, GPIO::AFR::AlternateFunction::AF10, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::High);
}
}
}
}
#endif

View File

@@ -0,0 +1,6 @@
#ifndef ION_DEVICE_N0101_REGS_CONFIG_FLASH_H
#define ION_DEVICE_N0101_REGS_CONFIG_FLASH_H
#define REGS_FLASH_CONFIG_ART 1
#endif

View File

@@ -59,12 +59,7 @@ void start() {
/* Initialize the FPU as early as possible.
* For example, static C++ objects are very likely to manipulate float values */
// FIXME//TODO//Ion::Device::initFPU();
#warning BOO
#if 0
Ion::Device::initMPU();
#endif
Ion::Device::Board::initFPU();
/* Call static C++ object constructors
* The C++ compiler creates an initialization function for each static object.

View File

@@ -8,6 +8,7 @@ namespace Board {
void init();
void shutdown();
void initFPU();
void initClocks();
void shutdownClocks(bool keepLEDAwake = false);

View File

@@ -31,6 +31,14 @@ static void close() {
FLASH.CR()->setLOCK(true);
// Purge Data and instruction cache
#if REGS_FLASH_CONFIG_ART
if (FLASH.ACR()->getARTEN()) {
FLASH.ACR()->setARTEN(false);
FLASH.ACR()->setARTRST(true);
FLASH.ACR()->setARTRST(false);
FLASH.ACR()->setARTEN(true);
}
#else
if (FLASH.ACR()->getDCEN()) {
FLASH.ACR()->setDCEN(false);
FLASH.ACR()->setDCRST(true);
@@ -43,6 +51,7 @@ static void close() {
FLASH.ACR()->setICRST(false);
FLASH.ACR()->setICEN(true);
}
#endif
}
// Compile-time log2

View File

@@ -40,6 +40,12 @@ public:
REGS_BOOL_FIELD(SLEEPDEEP, 2);
};
class CCR : public Register32 {
public:
REGS_BOOL_FIELD(IC, 17);
REGS_BOOL_FIELD(DC, 16);
};
class SYST_CSR : public Register32 {
public:
enum class CLKSOURCE : uint8_t {
@@ -62,6 +68,29 @@ public:
REGS_FIELD(CURRENT, uint32_t, 23, 0);
};
class ICIALLU : public Register32 {
public:
using Register32::Register32;
};
class CSSELR : public Register32 {
public:
REGS_BOOL_FIELD(IND, 0);
};
class CCSIDR : public Register32 {
public:
REGS_FIELD(ASSOCIATIVITY, uint16_t, 12, 3);
REGS_FIELD(NUMSETS, uint16_t, 27, 13);
};
class DCISW : public Register32 {
public:
DCISW() : Register32(0) {}
REGS_FIELD(SET, uint16_t, 13, 5);
REGS_FIELD(WAY, uint8_t, 31, 30);
};
constexpr CM4() {};
REGS_REGISTER_AT(SYST_CSR, 0x10);
REGS_REGISTER_AT(SYST_RVR, 0x14);
@@ -69,7 +98,12 @@ public:
REGS_REGISTER_AT(VTOR, 0xD08);
REGS_REGISTER_AT(AIRCR, 0xD0C);
REGS_REGISTER_AT(SCR, 0xD10);
REGS_REGISTER_AT(CCR, 0xD14);
REGS_REGISTER_AT(CCSIDR, 0xD80);
REGS_REGISTER_AT(CSSELR, 0xD84);
REGS_REGISTER_AT(CPACR, 0xD88);
REGS_REGISTER_AT(ICIALLU, 0xF50);
REGS_REGISTER_AT(DCISW, 0xF60);
private:
constexpr uint32_t Base() const {
return 0xE000E000;

View File

@@ -2,6 +2,7 @@
#define REGS_FLASH_H
#include "register.h"
#include <regs/config/flash.h>
class FLASH {
public:
@@ -9,10 +10,15 @@ public:
public:
REGS_FIELD(LATENCY, uint8_t, 3, 0);
REGS_BOOL_FIELD(PRFTEN, 8);
#if REGS_FLASH_CONFIG_ART
REGS_BOOL_FIELD(ARTEN, 9);
REGS_BOOL_FIELD(ARTRST, 9);
#else
REGS_BOOL_FIELD(ICEN, 9);
REGS_BOOL_FIELD(DCEN, 10);
REGS_BOOL_FIELD(ICRST, 11);
REGS_BOOL_FIELD(DCRST, 12);
#endif
};
class KEYR : public Register32 {

View File

@@ -26,7 +26,7 @@ public:
class RBAR : Register32 {
public:
void setADDR(void * address) volatile { assert(((uint32_t)address & 0b11111) == 0); setBitRange(31, 5, (uint32_t)address >> 5); }
void setADDR(uint32_t address) volatile { /* assert((address & 0b11111) == 0);*/ setBitRange(31, 5, address >> 5); }
REGS_BOOL_FIELD(VALID, 4);
REGS_FIELD(REGION, uint8_t, 3, 0);
};
@@ -34,11 +34,19 @@ public:
class RASR : Register32 {
public:
REGS_BOOL_FIELD(XN, 28);
REGS_FIELD(AP, uint8_t, 26, 24);
enum class AccessPermission : uint8_t {
NoAccess = 0,
PrivilegedRO = 5,
PrivilegedRW = 1,
PrivilegedRWUnprivilegedRO = 2,
RO = 6,
RW = 3
};
REGS_FIELD(AP, AccessPermission, 26, 24);
REGS_FIELD(TEX, uint8_t, 21, 19);
REGS_BOOL_FIELD(S, 18);
REGS_BOOL_FIELD(C, 17);
REGS_BOOL_FIELD(B, 16);
REGS_BOOL_FIELD(S, 18); // Shareable
REGS_BOOL_FIELD(C, 17); // Cacheable
REGS_BOOL_FIELD(B, 16); // Buffereable
REGS_FIELD(SRD, uint8_t, 15, 8);
enum class RegionSize : uint8_t {
Bytes32 = 0b00100,
@@ -46,6 +54,8 @@ public:
Bytes128 = 0b00110,
KyloBytes1 = 0b01001,
MegaBytes1 = 0b10011,
_1MB = 19,
_32MB = 24,
GigaBytes1 = 0b11101,
GigaBytes4 = 0b11111
};

View File

@@ -57,6 +57,7 @@ public:
AHBDividedBy16 = 7
};
void setPPRE1(APBPrescaler r) volatile { setBitRange(12, 10, (uint32_t)r); }
void setPPRE2(APBPrescaler r) volatile { setBitRange(15, 13, (uint32_t)r); }
};
class AHB1ENR : public Register32 {