aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/adc.c68
-rw-r--r--src/lib/adc.h67
-rw-r--r--src/lib/exti.c250
-rw-r--r--src/lib/exti.h143
-rw-r--r--src/lib/gpio.c14
-rw-r--r--src/lib/gpio.h100
-rw-r--r--src/lib/stm32f10x_conf.h132
-rw-r--r--src/lib/stm32f10x_it.c252
-rw-r--r--src/lib/syscalls.c156
-rw-r--r--src/lib/systick.c30
-rw-r--r--src/lib/systick.h37
-rw-r--r--src/lib/timers.c114
-rw-r--r--src/lib/timers.h118
-rw-r--r--src/lib/usart.c44
-rw-r--r--src/lib/usart.h131
-rw-r--r--src/lib/util.cpp47
-rw-r--r--src/lib/util.h57
17 files changed, 1760 insertions, 0 deletions
diff --git a/src/lib/adc.c b/src/lib/adc.c
new file mode 100644
index 0000000..bcdf874
--- /dev/null
+++ b/src/lib/adc.c
@@ -0,0 +1,68 @@
+#include "stm32f10x_rcc.h"
+#include "adc.h"
+#include <stdio.h>
+#include <inttypes.h>
+
+/* The ADC input clock is generated from PCLK2/APB2 divided by a prescaler
+ * and it must not exceed 14MHz.
+ *
+ * ADC1 and ADC2 are clocked by APB2
+ *
+ * 1) Power on by setting ADON in ADC_CR2
+ * Conversion starts when ADON is set for a second time after some
+ * time t > t_stab.
+ *
+ * Up to 16 selected conversion must be selected in ADC_SQRx
+ *
+ * Single conversion mode:
+ * Set the ADON bit in the ADC_CR2 register
+ * Once the conversion is complete:
+ * Converted data is stored in ADC_DR
+ * EOC flag is set
+ * Interrupt is generated if EOCIE is set
+ *
+ * Calibration:
+ * Calibration is started by setting the CAL bit in the ADC_CR2 register.
+ * Once calibration is over, the CAL bit is reset by hardware and normal
+ * conversion can be performed. Calibrate at power-on.
+ *
+ * ALIGN in ADC_CR2 selects the alignment of data
+ *
+ * IMPORTANT: maximum external impedance must be below 0.4kOhms for 1.5
+ * sample conversion time.
+ *
+ * At 55.5 cycles/sample, the external input impedance < 50kOhms*/
+
+void adc_init(void) {
+ /* PCLK2 is the APB2 clock */
+ RCC_ADCCLKConfig(RCC_PCLK2_Div6);
+
+ /* Enable ADC1 clock so that we can talk to it */
+ RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
+
+ /* Put everything back to power-on defaults */
+ RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1, ENABLE);
+ RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1, DISABLE);
+
+ ADC_CR1 = 0;
+ ADC_CR2 = CR2_EXTSEL_SWSTART | CR2_EXTTRIG; // Software triggers conversions
+ ADC_SQR1 = 0;
+
+ /* Up the sample conversion time to 55.5 cycles/sec, see note above */
+ ADC_SMPR1 = 0xB6DB6D;
+ ADC_SMPR2 = 0x2DB6DB6D;
+
+ /* Enable the ADC */
+ CR2_ADON_BIT = 1;
+
+ /* Reset the calibration registers and then perform a reset */
+ CR2_RSTCAL_BIT = 1;
+ while(CR2_RSTCAL_BIT)
+ ;
+
+ CR2_CAL_BIT = 1;
+ while(CR2_CAL_BIT)
+ ;
+}
+
+
diff --git a/src/lib/adc.h b/src/lib/adc.h
new file mode 100644
index 0000000..41f975e
--- /dev/null
+++ b/src/lib/adc.h
@@ -0,0 +1,67 @@
+#ifndef _ADC_H_
+#define _ADC_H_
+#include <inttypes.h>
+#include "util.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+/* Notes:
+ * The maximum input impedance on each channel MUST be below .4kohms,
+ * or face the wrath of incorrect readings...
+ * This can be changed at the expense of sample time... see datasheet
+ *
+ * Need to up the sample time if otherwise... see datasheet */
+
+/* We'll only use ADC1 for now... */
+#define ADC_BASE 0x40012400
+#define ADC_SR *(volatile uint32_t*)(ADC_BASE + 0)
+#define ADC_CR1 *(volatile uint32_t*)(ADC_BASE + 0x4)
+#define ADC_CR2 *(volatile uint32_t*)(ADC_BASE + 0x8)
+#define ADC_SMPR1 *(volatile uint32_t*)(ADC_BASE + 0xC)
+#define ADC_SMPR2 *(volatile uint32_t*)(ADC_BASE + 0x10)
+#define ADC_SQR1 *(volatile uint32_t*)(ADC_BASE + 0x2C)
+#define ADC_SQR3 *(volatile uint32_t*)(ADC_BASE + 0x34)
+#define ADC_DR *(volatile uint32_t*)(ADC_BASE + 0x4C)
+
+#define CR2_EXTSEL_SWSTART (0xE << 16)
+#define CR2_RSTCAL (BIT(3))
+#define CR2_EXTTRIG (BIT(20))
+
+/* Bit banded bits */
+#define CR2_ADON_BIT *(volatile uint32_t*)(BITBAND_PERI(ADC_BASE+0x8, 0))
+#define CR2_CAL_BIT *(volatile uint32_t*)(BITBAND_PERI(ADC_BASE+0x8, 2))
+#define CR2_RSTCAL_BIT *(volatile uint32_t*)(BITBAND_PERI(ADC_BASE+0x8, 3))
+#define CR2_SWSTART_BIT *(volatile uint32_t*)(BITBAND_PERI(ADC_BASE+0x8 + 2, 6))
+#define SR_EOC_BIT *(volatile uint32_t*)(BITBAND_PERI(ADC_BASE+0, 1))
+
+#define NR_ANALOG_PINS 16
+
+/* Initialize ADC1 to do one-shot conversions */
+void adc_init(void);
+
+/* Perform a single conversion on ADC[0-16],
+ * PRECONDITIONS:
+ * adc initialized */
+static inline int adc_read(int channel) {
+ /* Set channel */
+ ADC_SQR3 = channel;
+
+ /* Start the conversion */
+ CR2_SWSTART_BIT = 1;
+
+ /* Wait for it to finish */
+ while(SR_EOC_BIT == 0)
+ ;
+
+ return ADC_DR;
+}
+
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+#endif
+
diff --git a/src/lib/exti.c b/src/lib/exti.c
new file mode 100644
index 0000000..8e46bb1
--- /dev/null
+++ b/src/lib/exti.c
@@ -0,0 +1,250 @@
+#include "exti.h"
+#include "util.h"
+
+volatile static voidFuncPtr exti_handlers[NR_EXTI_CHANNELS];
+
+static inline void clear_pending(int bit) {
+ REG_SET(EXTI_PR, BIT(bit));
+ /* If the pending bit is cleared as the last instruction in an ISR,
+ * it won't actually be cleared in time and the ISR will fire again.
+ * Insert a 2-cycle buffer to allow it to take effect. */
+ asm volatile("nop");
+ asm volatile("nop");
+}
+
+/* For EXTI0 through EXTI4, only one handler
+ * is associated with each channel, so we
+ * don't have to keep track of which channel
+ * we came from */
+void EXTI0_IRQHandler(void) {
+ ASSERT(exti_handlers[EXTI0]);
+ if (exti_handlers[EXTI0]) {
+ exti_handlers[EXTI0]();
+ }
+
+ /* Clear pending bit*/
+ clear_pending(EXTI0);
+}
+
+void EXTI1_IRQHandler(void) {
+ ASSERT(exti_handlers[EXTI1]);
+ /* Call registered handler */
+ if (exti_handlers[EXTI1]) {
+ exti_handlers[EXTI1]();
+ }
+
+ /* Clear pending bit*/
+ clear_pending(EXTI1);
+}
+
+void EXTI2_IRQHandler(void) {
+ ASSERT(exti_handlers[EXTI2]);
+ /* Call registered handler */
+ if (exti_handlers[EXTI2]) {
+ exti_handlers[EXTI2]();
+ }
+
+ /* Clear pending bit*/
+ clear_pending(EXTI2);
+}
+
+void EXTI3_IRQHandler(void) {
+ ASSERT(exti_handlers[EXTI3]);
+ /* Call registered handler */
+ if (exti_handlers[EXTI3]) {
+ exti_handlers[EXTI3]();
+ }
+
+ /* Clear pending bit*/
+ clear_pending(EXTI3);
+}
+
+void EXTI4_IRQHandler(void) {
+ ASSERT(exti_handlers[EXTI4]);
+ /* Call registered handler */
+ if (exti_handlers[EXTI4]) {
+ exti_handlers[EXTI4]();
+ }
+
+ /* Clear pending bit*/
+ clear_pending(EXTI4);
+}
+
+void EXTI9_5_IRQHandler(void) {
+ /* Figure out which channel it came from */
+ uint32_t pending;
+ uint32_t i;
+ pending = REG_GET(EXTI_PR);
+ pending = GET_BITS(pending, 5, 9);
+
+ /* Dispatch every handler if the pending bit is set */
+ for (i = 0; i < 5; i++) {
+ if (pending & 0x1) {
+ exti_handlers[EXTI5 + i]();
+ clear_pending(EXTI5 + i);
+ }
+ pending >>= 1;
+ }
+}
+
+void EXTI15_10_IRQHandler(void) {
+ /* Figure out which channel it came from */
+ uint32_t pending;
+ uint32_t i;
+ pending = REG_GET(EXTI_PR);
+ pending = GET_BITS(pending, 10, 15);
+
+ /* Dispatch every handler if the pending bit is set */
+ for (i = 0; i < 6; i++) {
+ if (pending & 0x1) {
+ exti_handlers[EXTI10 + i]();
+ clear_pending(EXTI10 + i);
+ }
+ pending >>= 1;
+ }
+}
+
+
+void exti_attach_interrupt(uint8_t channel, uint8_t port, voidFuncPtr handler, uint8_t mode) {
+ ASSERT(channel < NR_EXTI_CHANNELS);
+ ASSERT(port < NR_EXTI_PORTS);
+ ASSERT(mode < NR_EXTI_MODES);
+ ASSERT(EXTI0 == 0);
+
+ /* Note: All of the following code assumes that EXTI0 = 0 */
+
+ /* Map port to the correct EXTI channel */
+ switch (channel) {
+ case EXTI0:
+ case EXTI1:
+ case EXTI2:
+ case EXTI3:
+ REG_SET_MASK(AFIO_EXTICR1, BIT_MASK_SHIFT(port, channel*4));
+ break;
+
+ case EXTI4:
+ case EXTI5:
+ case EXTI6:
+ case EXTI7:
+ REG_SET_MASK(AFIO_EXTICR2, BIT_MASK_SHIFT(port, (channel-4)*4));
+ break;
+
+ case EXTI8:
+ case EXTI9:
+ case EXTI10:
+ case EXTI11:
+ REG_SET_MASK(AFIO_EXTICR3, BIT_MASK_SHIFT(port, (channel-8)*4));
+ break;
+
+ case EXTI12:
+ case EXTI13:
+ case EXTI14:
+ case EXTI15:
+ REG_SET_MASK(AFIO_EXTICR4, BIT_MASK_SHIFT(port, (channel-12)*4));
+ break;
+ }
+
+ /* Unmask appropriate interrupt line */
+ REG_SET_BIT(EXTI_IMR, channel);
+
+ /* Set trigger mode */
+ switch (mode) {
+ case EXTI_RISING:
+ REG_SET_BIT(EXTI_RTSR, channel);
+ break;
+
+ case EXTI_FALLING:
+ REG_SET_BIT(EXTI_FTSR, channel);
+ break;
+
+ case EXTI_RISING_FALLING:
+ REG_SET_BIT(EXTI_RTSR, channel);
+ REG_SET_BIT(EXTI_FTSR, channel);
+ break;
+ }
+
+ /* Configure the enable interrupt bits for the NVIC */
+ switch (channel) {
+ case EXTI0:
+ case EXTI1:
+ case EXTI2:
+ case EXTI3:
+ case EXTI4:
+ REG_SET(NVIC_ISER0, BIT(channel + 6));
+ break;
+
+ /* EXTI5-9 map to the same isr */
+ case EXTI5:
+ case EXTI6:
+ case EXTI7:
+ case EXTI8:
+ case EXTI9:
+ REG_SET(NVIC_ISER0, BIT(23));
+ break;
+
+ /* EXTI10-15 map to the same isr */
+ case EXTI10:
+ case EXTI11:
+ case EXTI12:
+ case EXTI13:
+ case EXTI14:
+ case EXTI15:
+ REG_SET(NVIC_ISER1, BIT(8));
+ break;
+ }
+
+ /* Register the handler */
+ exti_handlers[channel] = handler;
+}
+
+
+void exti_detach_interrupt(uint8_t channel) {
+ ASSERT(channel < NR_EXTI_CHANNELS);
+ ASSERT(EXTI0 == 0);
+ /* Is this interrupt actually on? */
+ ASSERT((REG_GET(EXTI_IMR) >> channel) & 0x01);
+
+ /* Clear EXTI_IMR line */
+ REG_CLEAR_BIT(EXTI_IMR, channel);
+
+ /* Clear triggers */
+ REG_CLEAR_BIT(EXTI_FTSR, channel);
+ REG_CLEAR_BIT(EXTI_RTSR, channel);
+
+ /* Turn off the associated interrupt */
+ switch (channel) {
+ case EXTI0:
+ case EXTI1:
+ case EXTI2:
+ case EXTI3:
+ case EXTI4:
+ REG_SET(NVIC_ICER0, BIT(channel + 6));
+ break;
+ case EXTI5:
+ case EXTI6:
+ case EXTI7:
+ case EXTI8:
+ case EXTI9:
+ /* Are there any other channels enabled?
+ * If so, don't disable the interrupt handler */
+ if (GET_BITS(REG_GET(EXTI_IMR), 5, 9) == 0) {
+ REG_SET(NVIC_ICER0, BIT(23));
+ }
+ break;
+ case EXTI10:
+ case EXTI11:
+ case EXTI12:
+ case EXTI13:
+ case EXTI14:
+ case EXTI15:
+ /* Are there any other channels enabled?
+ * If so, don't disable the interrupt handler */
+ if (GET_BITS(REG_GET(EXTI_IMR), 10, 15) == 0) {
+ REG_SET(NVIC_ICER1, BIT(8));
+ }
+ break;
+ }
+
+ /* Clear handler function pointer */
+ exti_handlers[channel] = 0;
+}
diff --git a/src/lib/exti.h b/src/lib/exti.h
new file mode 100644
index 0000000..ccae3a4
--- /dev/null
+++ b/src/lib/exti.h
@@ -0,0 +1,143 @@
+#ifndef _EXTI_H_
+#define _EXTI_H_
+
+#include <inttypes.h>
+/* Notes:
+ *
+ * To generate the interrupt, the interrupt line should be configured and
+ * enabled. This is done by programming the two trigger registers with the
+ * desired edge detection and by enabling the interrupt request by writing a
+ * '1' to the corresponding bit in the interrupt mask register. When the
+ * selected edge occurs on the external interrupt line, an interrupt request is
+ * generated. The pending bit corresponding to the interrupt line is also set.
+ * This request is reset by writing a '1' in the pending register.
+ *
+ * Hardware interrupt selection:
+ * To configure the 20 lines as interrupt sources, use the following procedure:
+ * 1) Configure AFIO_EXTIICR[y] to select the source input for EXTIx external
+ * interrupt
+ * 2) Configure the mask bits of the 20 interrupt lines (EXTI_IMR)
+ * 3) Configure the trigger selection bits of the interrupt lines (EXTI_RTSR and EXTI_FTSR)
+ * 4) Configure the enable and mask bits that control the NVIC_IRQ channel mapped to the External
+ * Interrupt Controller (EXTI) so that an inerrupt coming from one of the 20 lines
+ * can be correctly acknowledged.
+ *
+ * AFIO clock must be on.
+ *
+ * RM0008, page 107: "PD0, PD1 cannot be used for external interrupt/event generation
+ * on 36, 48, 64-bin packages."
+ *
+ * ----------------------------------------------------------------------------
+ * Pin to EXTI Line Mappings:
+ * EXTI0 EXTI1 EXTI2 EXTI3 EXTI4
+ * ----------------------------------------------------------------------------
+ * D2/PA0 D3/PA1 D1/PA2 D0/A6/PA3 D10/A10/PA4
+ * D26/EXT7/PB0 D27/EXT8/PB1 D16/A2/PC2 D17/A3/PC3 D18/A4/PC4
+ * D14/A0/PC0 D15/PC1 D25/EXT5/PD2
+ *
+ * EXTI5 EXTI6 EXTI7 EXTI8 EXTI9
+ * ----------------------------------------------------------------------------
+ * D13/A13/PA5 D12/A12/PA6 D11/A11/PA7 D6/PA8 D7/PA9
+ * D4/PB5 D5/PB6 D9/PB7 D38/PB8 D23/EXT4/PB9
+ * D19/A5/PC5 D34/EXTI15/PC6 D35/EXT16/PC7 D36/PC8 D37/EXT18/PC9
+ *
+ * EXTI10 EXTI11 EXTI12 EXTI13 EXTI14
+ * ----------------------------------------------------------------------------
+ * D8/PA10 D29/EXT10/PB11 D30/EXTI1/PB12 D31/EXTI12/PB13 D32/EXT13/PB14
+ * D28/PB10 D20/EXTI1/PC13 D21/EXT2/PC14
+ * D25/PC10
+ *
+ * EXTI15
+ * ----------------------------------------------------------------------------
+ * D33/EXTI14/PB15
+ * D22/EXT3/PC15
+ *
+ *
+ * The 16 EXTI interrupts are mapped to 7 interrupt handlers.
+ *
+ * EXTI Lines to Interrupt Mapping:
+ * EXTI0 -> EXTI0
+ * EXTI1 -> EXTI1
+ * EXTI2 -> EXTI2
+ * EXTI3 -> EXTI3
+ * EXTI4 -> EXTI4
+ * EXTI[5-9] -> EXT9_5
+ * EXTI[10-15] -> EXT15_10
+ *
+ *
+ * */
+
+#define NR_EXTI_CHANNELS 16
+#define NR_EXTI_PORTS 4
+#define NR_EXTI_MODES 3
+
+#define EXTI_IMR 0x40010400 // Interrupt mask register
+#define EXTI_EMR (EXTI_IMR + 0x04) // Event mask register
+#define EXTI_RTSR (EXTI_IMR + 0x08) // Rising trigger selection register
+#define EXTI_FTSR (EXTI_IMR + 0x0C) // Falling trigger selection register
+#define EXTI_SWIER (EXTI_IMR + 0x10) // Software interrupt event register
+#define EXTI_PR (EXTI_IMR + 0x14) // Pending register
+
+#define AFIO_EVCR 0x40010000
+#define AFIO_EXTICR1 (AFIO_EVCR + 0x08)
+#define AFIO_EXTICR2 (AFIO_EVCR + 0x0C)
+#define AFIO_EXTICR3 (AFIO_EVCR + 0x10)
+#define AFIO_EXTICR4 (AFIO_EVCR + 0x14)
+
+#define NVIC_EXTI1_OFFSET (NVIC_ISER0 + 0x07)
+#define NVIC_EXTI9_5_OFFSET (NVIC_ISER0 + 0x17)
+
+/* NVIC Interrupt Enable registers */
+#define NVIC_ISER0 0xE000E100
+#define NVIC_ISER1 0xE000E104
+#define NVIC_ISER2 0xE000E108
+#define NVIC_ISER3 0xE000E10C
+
+/* NVIC Interrupt Clear registers */
+#define NVIC_ICER0 0xE000E180
+#define NVIC_ICER1 0xE000E184
+#define NVIC_ICER2 0xE000E188
+#define NVIC_ICER3 0xE000E18C
+
+#define EXTI_RISING 0
+#define EXTI_FALLING 1
+#define EXTI_RISING_FALLING 2
+
+#define EXTI0 0
+#define EXTI1 1
+#define EXTI2 2
+#define EXTI3 3
+#define EXTI4 4
+#define EXTI5 5
+#define EXTI6 6
+#define EXTI7 7
+#define EXTI8 8
+#define EXTI9 9
+#define EXTI10 10
+#define EXTI11 11
+#define EXTI12 12
+#define EXTI13 13
+#define EXTI14 14
+#define EXTI15 15
+
+#define EXTI_CONFIG_PORTA 0
+#define EXTI_CONFIG_PORTB 1
+#define EXTI_CONFIG_PORTC 2
+#define EXTI_CONFIG_PORTD 3
+
+typedef void (*voidFuncPtr)(void);
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+void exti_attach_interrupt(uint8_t, uint8_t, voidFuncPtr, uint8_t);
+void exti_detach_interrupt(uint8_t);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+
+#endif
+
diff --git a/src/lib/gpio.c b/src/lib/gpio.c
new file mode 100644
index 0000000..db511c4
--- /dev/null
+++ b/src/lib/gpio.c
@@ -0,0 +1,14 @@
+#include "wiring.h"
+#include "stm32f10x_rcc.h"
+#include "gpio.h"
+#include "util.h"
+
+void gpio_init(void) {
+ /* Turn on clocks for GPIO */
+ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |
+ RCC_APB2Periph_GPIOB |
+ RCC_APB2Periph_GPIOC |
+ RCC_APB2Periph_AFIO,
+ ENABLE);
+}
+
diff --git a/src/lib/gpio.h b/src/lib/gpio.h
new file mode 100644
index 0000000..7620f96
--- /dev/null
+++ b/src/lib/gpio.h
@@ -0,0 +1,100 @@
+#ifndef _GPIO_H
+#define _GPIO_H
+
+#include <inttypes.h>
+#include "util.h"
+
+
+/* Each of the GPIO port bits can be in the following modes (STM32 138/995):
+ * Input floating
+ * Input pull-up
+ * Input pull-down
+ * Analog Input
+ * Output open-drain
+ * Output push-pull
+ * Alternate function push-pull
+ * Alternate function open-drain
+ *
+ * - After reset, the alternate functions are not active and IO prts
+ * are set to Input Floating mode */
+
+#define _GPIOA_BASE (GPIO_Port*)0x40010800
+#define _GPIOB_BASE (GPIO_Port*)0x40010C00
+#define _GPIOC_BASE (GPIO_Port*)0x40011000
+#define _GPIOD_BASE (GPIO_Port*)0x40011400
+
+/* Pin modes are set by [CNFx[1:0] : MODEx[1:0]] */
+#define GPIO_SPEED_50MHZ (0x3) // Max output speed 50 MHz
+#define GPIO_MODE_OUTPUT_PP ((0x00 << 2) | GPIO_SPEED_50MHZ)
+#define GPIO_MODE_OUTPUT_OD ((0x01 << 2) | GPIO_SPEED_50MHZ)
+
+#define GPIO_MODE_AF_OUTPUT_PP ((0x02 << 2) | GPIO_SPEED_50MHZ)
+#define GPIO_MODE_AF_OUTPUT_OD ((0x03 << 2) | GPIO_SPEED_50MHZ)
+
+/* Note: mode bits must be set to zero for input mode */
+#define GPIO_MODE_INPUT_ANALOG (0x00 << 2)
+#define GPIO_MODE_INPUT_FLOATING (0x01 << 2)
+#define GPIO_MODE_INPUT_PD (0x02 << 2)
+#define GPIO_MODE_INPUT_PU (0x02 << 2)
+
+#define INPUT_ANALOG GPIO_MODE_INPUT_ANALOG
+#define INPUT_DIGITAL GPIO_MODE_INPUT_FLOATING
+#define INPUT_FLOATING GPIO_MODE_INPUT_FLOATING
+#define INPUT_PULLDOWN GPIO_MODE_INPUT_PD
+#define INPUT_PULLUP GPIO_MODE_INPUT_PU
+#define INPUT GPIO_MODE_INPUT_FLOATING
+#define OUTPUT GPIO_MODE_OUTPUT_PP
+
+typedef struct {
+ volatile uint32_t CRL; // Port configuration register low
+ volatile uint32_t CRH; // Port configuration register high
+ volatile uint32_t IDR; // Port input data register
+ volatile uint32_t ODR; // Port output data register
+ volatile uint32_t BSRR; // Port bit set/reset register
+ volatile uint32_t BRR; // Port bit reset register
+ volatile uint32_t LCKR; // Port configuration lock register
+} GPIO_Port;
+
+typedef volatile uint32_t* GPIOReg;
+
+#define POS_MASK(shift) (~(0xF << shift))
+#define POS(val) (val << 2)
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+void gpio_init(void);
+
+static inline void gpio_set_mode(GPIO_Port* port, uint8_t gpio_pin, uint8_t mode) {
+ uint32_t tmp;
+ uint32_t shift = POS(gpio_pin % 8);
+ GPIOReg CR;
+
+ ASSERT(port);
+ ASSERT(gpio_pin < 16);
+
+ CR = (gpio_pin < 8) ? &(port->CRL) : &(port->CRH);
+
+ tmp = *CR;
+ tmp &= POS_MASK(shift);
+ tmp |= mode << shift;
+
+ *CR = tmp;
+}
+
+static inline void gpio_write_bit(GPIO_Port *port, uint8_t gpio_pin, uint8_t val) {
+ if (val){
+ port->BSRR = BIT(gpio_pin);
+ } else {
+ port->BRR = BIT(gpio_pin);
+ }
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+
+#endif
+
diff --git a/src/lib/stm32f10x_conf.h b/src/lib/stm32f10x_conf.h
new file mode 100644
index 0000000..d523f1a
--- /dev/null
+++ b/src/lib/stm32f10x_conf.h
@@ -0,0 +1,132 @@
+/******************** (C) COPYRIGHT 2008 STMicroelectronics ********************
+* File Name : stm32f10x_conf.h
+* Author : MCD Application Team
+* Version : V2.0.2
+* Date : 07/11/2008
+* Description : Library configuration file.
+********************************************************************************
+* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME.
+* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
+* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
+* CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
+* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+*******************************************************************************/
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32F10x_CONF_H
+#define __STM32F10x_CONF_H
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32f10x_type.h"
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+/* Uncomment the line below to compile the library in DEBUG mode, this will expanse
+ the "assert_param" macro in the firmware library code (see "Exported macro"
+ section below) */
+/* #define DEBUG 1*/
+
+/* Comment the line below to disable the specific peripheral inclusion */
+/************************************* ADC ************************************/
+#define _ADC
+#define _ADC1
+#define _ADC2
+#define _ADC3
+//#define _BKP
+//#define _CAN
+//#define _CRC
+//#define _DAC
+//#define _DBGMCU
+//#define _DMA
+//#define _DMA1_Channel1
+//#define _DMA1_Channel2
+//#define _DMA1_Channel3
+//#define _DMA1_Channel4
+//#define _DMA1_Channel5
+//#define _DMA1_Channel6
+//#define _DMA1_Channel7
+//#define _DMA2_Channel1
+//#define _DMA2_Channel2
+//#define _DMA2_Channel3
+//#define _DMA2_Channel4
+//#define _DMA2_Channel5
+#define _EXTI
+#define _FLASH
+
+/* Uncomment the line below to enable FLASH program/erase/protections functions,
+ otherwise only FLASH configuration (latency, prefetch, half cycle) functions
+ are enabled */
+/* #define _FLASH_PROG */
+
+#if 0
+#define _FSMC
+#define _GPIO
+#define _GPIOA
+#define _GPIOB
+#define _GPIOC
+#define _GPIOD
+#define _GPIOE
+#define _GPIOF
+#define _GPIOG
+#define _I2C
+#define _I2C1
+#define _I2C2
+#define _IWDG
+#define _PWR
+#define _RTC
+#define _SDIO
+#define _SPI
+#define _SPI1
+#define _SPI2
+#define _SPI3
+#define _SysTick
+#define _TIM
+#define _TIM1
+#define _TIM2
+#define _TIM3
+#define _TIM4
+#define _TIM5
+#define _TIM6
+#define _TIM7
+#define _TIM8
+#define _USART
+#define _USART1
+#define _USART3
+#define _UART4
+#define _UART5
+#endif
+#define _USART2
+#define _NVIC
+#define _RCC
+#define _AFIO
+
+/************************************* WWDG ***********************************/
+#define _WWDG
+
+/* In the following line adjust the value of External High Speed oscillator (HSE)
+ used in your application */
+#define HSE_Value ((u32)8000000) /* Value of the External oscillator in Hz*/
+
+/* Exported macro ------------------------------------------------------------*/
+#ifdef DEBUG
+/*******************************************************************************
+* Macro Name : assert_param
+* Description : The assert_param macro is used for function's parameters check.
+* It is used only if the library is compiled in DEBUG mode.
+* Input : - expr: If expr is false, it calls assert_failed function
+* which reports the name of the source file and the source
+* line number of the call that failed.
+* If expr is true, it returns no value.
+* Return : None
+*******************************************************************************/
+ #define assert_param(expr) ((expr) ? (void)0 : assert_failed((u8 *)__FILE__, __LINE__))
+/* Exported functions ------------------------------------------------------- */
+ void assert_failed(u8* file, u32 line);
+#else
+ #define assert_param(expr) ((void)0)
+#endif /* DEBUG */
+
+#endif /* __STM32F10x_CONF_H */
+
+/******************* (C) COPYRIGHT 2008 STMicroelectronics *****END OF FILE****/
diff --git a/src/lib/stm32f10x_it.c b/src/lib/stm32f10x_it.c
new file mode 100644
index 0000000..70d3269
--- /dev/null
+++ b/src/lib/stm32f10x_it.c
@@ -0,0 +1,252 @@
+void NMIException(void) {
+}
+void HardFaultException(void) {
+ while (1) {
+ }
+}
+
+void MemManageException(void) {
+ while (1) {
+ }
+}
+
+void BusFaultException(void) {
+ while (1) {
+ }
+}
+
+void UsageFaultException(void) {
+ while (1) {
+ }
+}
+
+#if 0
+void DebugMonitor(void) {
+}
+
+void SVCHandler(void) {
+}
+
+void PendSVC(void) {
+}
+
+void WWDG_IRQHandler(void) {
+}
+
+void PVD_IRQHandler(void) {
+}
+
+void TAMPER_IRQHandler(void) {
+}
+
+void RTC_IRQHandler(void) {
+}
+
+void FLASH_IRQHandler(void) {
+}
+
+void RCC_IRQHandler(void) {
+}
+
+void DMA1_Channel1_IRQHandler(void) {
+}
+
+void DMA1_Channel2_IRQHandler(void) {
+}
+
+void DMA1_Channel3_IRQHandler(void) {
+}
+
+void DMA1_Channel4_IRQHandler(void) {
+}
+
+void DMA1_Channel5_IRQHandler(void) {
+}
+
+void DMA1_Channel6_IRQHandler(void) {
+}
+
+void DMA1_Channel7_IRQHandler(void) {
+}
+
+void ADC1_2_IRQHandler(void) {
+}
+
+void USB_HP_CAN_TX_IRQHandler(void) {
+}
+
+void USB_LP_CAN_RX0_IRQHandler(void) {
+}
+
+void CAN_RX1_IRQHandler(void) {
+}
+
+void CAN_SCE_IRQHandler(void) {
+}
+
+void TIM1_BRK_IRQHandler(void) {
+}
+
+void TIM1_UP_IRQHandler(void) {
+ while(1) {
+ }
+}
+
+void TIM1_TRG_COM_IRQHandler(void) {
+}
+
+void TIM1_CC_IRQHandler(void) {
+ while(1)
+ ;
+}
+
+void TIM2_IRQHandler(void) {
+}
+
+void TIM3_IRQHandler(void) {
+}
+
+void TIM4_IRQHandler(void) {
+}
+
+void I2C1_EV_IRQHandler(void) {
+}
+
+void I2C1_ER_IRQHandler(void) {
+}
+
+void I2C2_EV_IRQHandler(void) {
+}
+
+void I2C2_ER_IRQHandler(void) {
+}
+
+void SPI1_IRQHandler(void) {
+}
+
+void SPI2_IRQHandler(void) {
+}
+
+void USART1_IRQHandler(void) {
+#if 0
+ if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
+ {
+ /* Read one byte from the receive data register */
+ RxBuffer[RxCounter++] = (USART_ReceiveData(USART1) & 0x7F);
+
+ if(RxCounter == NbrOfDataToRead)
+ {
+ /* Disable the USART Receive interrupt */
+ USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
+ }
+ }
+
+ if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)
+ {
+ /* Write one byte to the transmit data register */
+ USART_SendData(USART1, TxBuffer[TxCounter++]);
+
+ if(TxCounter == NbrOfDataToTransfer)
+ {
+ /* Disable the USART1 Transmit interrupt */
+ USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
+ }
+ }
+#endif
+}
+
+void USART2_IRQHandler(void) {
+#if 0
+ if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
+ {
+ /* Echo the character back */
+ RxBuffer[0] = (USART_ReceiveData(USART2) & 0x7F);
+ USART_SendData(USART2, RxBuffer[0]);
+ // /* Read one byte from the receive data register */
+ // RxBuffer[RxCounter++] = (USART_ReceiveData(USART2) & 0x7F);
+
+ // if(RxCounter == NbrOfDataToRead)
+ // {
+ // /* Disable the USART Receive interrupt */
+ // USART_ITConfig(USART2, USART_IT_RXNE, DISABLE);
+ // }
+ }
+
+#endif
+
+#if 0
+ if(USART_GetITStatus(USART2, USART_IT_TXE) != RESET)
+ {
+ /* Write one byte to the transmit data register */
+ USART_SendData(USART2, 'Y');
+ TxCounter++;
+// USART_SendData(USART2, TxBuffer[TxCounter++]);
+
+ if(TxCounter == NbrOfDataToTransfer) {
+ /* Disable the USART1 Transmit interrupt */
+ USART_ITConfig(USART2, USART_IT_TXE, DISABLE);
+ }
+ }
+#endif
+}
+
+void USART3_IRQHandler(void) {
+}
+
+void RTCAlarm_IRQHandler(void) {
+}
+
+void USBWakeUp_IRQHandler(void) {
+}
+
+void TIM8_BRK_IRQHandler(void) {
+}
+
+void TIM8_UP_IRQHandler(void) {
+}
+
+void TIM8_TRG_COM_IRQHandler(void) {
+}
+
+void TIM8_CC_IRQHandler(void) {
+}
+
+void ADC3_IRQHandler(void) {
+}
+
+void FSMC_IRQHandler(void) {
+}
+
+void SDIO_IRQHandler(void) {
+}
+
+void TIM5_IRQHandler(void) {
+}
+
+void SPI3_IRQHandler(void) {
+}
+
+void UART4_IRQHandler(void) {
+}
+
+void UART5_IRQHandler(void) {
+}
+
+void TIM6_IRQHandler(void) {
+}
+
+void TIM7_IRQHandler(void) {
+}
+
+void DMA2_Channel1_IRQHandler(void) {
+}
+
+void DMA2_Channel2_IRQHandler(void) {
+}
+
+void DMA2_Channel3_IRQHandler(void) {
+}
+
+void DMA2_Channel4_5_IRQHandler(void) {
+}
+#endif
diff --git a/src/lib/syscalls.c b/src/lib/syscalls.c
new file mode 100644
index 0000000..6095bd3
--- /dev/null
+++ b/src/lib/syscalls.c
@@ -0,0 +1,156 @@
+#include <sys/stat.h>
+#include "stm32f10x_usart.h"
+
+/* _end is set in the linker command file */
+extern caddr_t _end;
+
+/* just in case, most boards have at least some memory */
+#ifndef RAMSIZE
+# define RAMSIZE (caddr_t)0x50000
+#endif
+
+#define STACK_TOP 0x20000800
+
+void uart_send(const char*str);
+
+/*
+ * sbrk -- changes heap size size. Get nbytes more
+ * RAM. We just increment a pointer in what's
+ * left of memory on the board.
+ */
+caddr_t
+_sbrk(nbytes)
+int nbytes;
+{
+ static caddr_t heap_ptr = NULL;
+ caddr_t base;
+
+ if (heap_ptr == NULL) {
+ heap_ptr = (caddr_t)&_end;
+ }
+
+ if ((STACK_TOP - (unsigned int)heap_ptr) >= 0) {
+ base = heap_ptr;
+ heap_ptr += nbytes;
+ return (base);
+ } else {
+ uart_send("heap full!\r\n");
+ return ((caddr_t)-1);
+ }
+}
+
+int _open(const char *path, int flags, ...)
+{
+ return 1;
+}
+
+int _close(int fd)
+{
+ return 0;
+}
+
+int _fstat(int fd, struct stat *st)
+{
+ st->st_mode = S_IFCHR;
+ return 0;
+}
+
+int _isatty(int fd)
+{
+ return 1;
+}
+
+int isatty(int fd)
+{
+ return 1;
+}
+
+int _lseek(int fd, off_t pos, int whence)
+{
+ return -1;
+}
+
+unsigned char getch(void)
+{
+ while (!(USART2->SR & USART_FLAG_RXNE));
+ return USART2->DR;
+}
+
+
+int _read(int fd, char *buf, size_t cnt)
+{
+ *buf = getch();
+
+ return 1;
+}
+
+void putch(unsigned char c)
+{
+ if (c == '\n') putch('\r');
+
+ while (!(USART2->SR & USART_FLAG_TXE));
+ USART2->DR = c;
+}
+
+void cgets(char *s, int bufsize)
+{
+ char *p;
+ int c;
+ int i;
+
+ for (i = 0; i < bufsize; i++) {
+ *(s+i) = 0;
+ }
+// memset(s, 0, bufsize);
+
+ p = s;
+
+ for (p = s; p < s + bufsize-1;)
+ {
+ c = getch();
+ switch (c)
+ {
+ case '\r' :
+ case '\n' :
+ putch('\r');
+ putch('\n');
+ *p = '\n';
+ return;
+
+ case '\b' :
+ if (p > s)
+ {
+ *p-- = 0;
+ putch('\b');
+ putch(' ');
+ putch('\b');
+ }
+ break;
+
+ default :
+ putch(c);
+ *p++ = c;
+ break;
+ }
+ }
+ return;
+}
+
+int _write(int fd, const char *buf, size_t cnt)
+{
+ int i;
+// uart_send("_write\r\n");
+
+ for (i = 0; i < cnt; i++)
+ putch(buf[i]);
+
+ return cnt;
+}
+
+/* Override fgets() in newlib with a version that does line editing */
+char *fgets(char *s, int bufsize, void *f)
+{
+// uart_send("fgets\r\n");
+ cgets(s, bufsize);
+ return s;
+}
diff --git a/src/lib/systick.c b/src/lib/systick.c
new file mode 100644
index 0000000..f328ae0
--- /dev/null
+++ b/src/lib/systick.c
@@ -0,0 +1,30 @@
+#include "systick.h"
+
+#define MILLIS_INC 1
+
+volatile uint32_t systick_timer_overflow_count = 0;
+volatile uint32_t systick_timer_millis = 0;
+static uint8_t systick_timer_fract = 0;
+
+void systick_init(void) {
+ /* Set the reload counter to tick every 1ms */
+ SYSTICK_RELOAD = MAPLE_RELOAD_VAL;
+
+ /* Clock the system timer with the core clock
+ * and turn it on, interrrupt every 1ms to keep track of millis()*/
+ SYSTICK_CSR = SYSTICK_SRC_HCLK |
+ SYSTICK_ENABLE |
+ SYSTICK_TICKINT;
+}
+
+void SysTickHandler(void)
+{
+ uint32_t m = systick_timer_millis;
+ uint8_t f = systick_timer_fract;
+
+ m += MILLIS_INC;
+ systick_timer_millis = m;
+ systick_timer_overflow_count++;
+}
+
+
diff --git a/src/lib/systick.h b/src/lib/systick.h
new file mode 100644
index 0000000..42d33d0
--- /dev/null
+++ b/src/lib/systick.h
@@ -0,0 +1,37 @@
+#ifndef _SYSTICK_H_
+#define _SYSTICK_H_
+#include <inttypes.h>
+#include "util.h"
+
+/* To the ARM technical manual... there's nearly nothing on the systick
+ * timer in the stm32 manual */
+
+#define SYSTICK_CSR *(volatile int*)0xE000E010 // Control and status register
+#define SYSTICK_RELOAD *(volatile int*)0xE000E014 // Reload value register
+#define SYSTICK_CNT *(volatile int*)0xE000E018 // Current value register
+#define SYSTICK_CALIB *(volatile int*)0xE000E01C // Calibration value register
+
+#define SYSTICK_SRC_HCLK BIT(2) // Use core clock
+#define SYSTICK_TICKINT BIT(1) // Interrupt on systick countdown
+#define SYSTICK_ENABLE BIT(0) // Turn on the counter
+
+/* We use the systick timer to tick once
+ * every millisecond */
+#define MAPLE_RELOAD_VAL 72000
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+void systick_init(void);
+
+static inline uint32_t systick_get_count(void) {
+ return (uint32_t)SYSTICK_CNT;
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
+
diff --git a/src/lib/timers.c b/src/lib/timers.c
new file mode 100644
index 0000000..401b267
--- /dev/null
+++ b/src/lib/timers.c
@@ -0,0 +1,114 @@
+#include "stm32f10x_rcc.h"
+#include "wiring.h"
+#include "timers.h"
+#include "util.h"
+
+typedef struct {
+ volatile uint16_t CR1;
+ uint16_t RESERVED0;
+ volatile uint16_t CR2;
+ uint16_t RESERVED1;
+ volatile uint16_t SMCR;
+ uint16_t RESERVED2;
+ volatile uint16_t DIER;
+ uint16_t RESERVED3;
+ volatile uint16_t SR;
+ uint16_t RESERVED4;
+ volatile uint16_t EGR;
+ uint16_t RESERVED5;
+ volatile uint16_t CCMR1;
+ uint16_t RESERVED6;
+ volatile uint16_t CCMR2;
+ uint16_t RESERVED7;
+ volatile uint16_t CCER;
+ uint16_t RESERVED8;
+ volatile uint16_t CNT;
+ uint16_t RESERVED9;
+ volatile uint16_t PSC;
+ uint16_t RESERVED10;
+ volatile uint16_t ARR;
+ uint16_t RESERVED11;
+ volatile uint16_t RCR;
+ uint16_t RESERVED12;
+ volatile uint16_t CCR1;
+ uint16_t RESERVED13;
+ volatile uint16_t CCR2;
+ uint16_t RESERVED14;
+ volatile uint16_t CCR3;
+ uint16_t RESERVED15;
+ volatile uint16_t CCR4;
+ uint16_t RESERVED16;
+ volatile uint16_t BDTR; // Not used in general purpose timers
+ uint16_t RESERVED17; // Not used in general purpose timers
+ volatile uint16_t DCR;
+ uint16_t RESERVED18;
+ volatile uint16_t DMAR;
+ uint16_t RESERVED19;
+} Timer;
+
+void timer_init(uint8_t timer_num, uint16_t prescale) {
+ Timer *timer;
+ uint32_t is_advanced = 0;
+
+ ASSERT(timer_num > 0 && timer_num <= 4);
+
+ switch(timer_num) {
+ case 1:
+ timer = (Timer*)TIMER1_BASE;
+ RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
+ is_advanced = 1;
+ break;
+ case 2:
+ timer = (Timer*)TIMER2_BASE;
+ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
+ break;
+ case 3:
+ timer = (Timer*)TIMER3_BASE;
+ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
+ break;
+ case 4:
+ timer = (Timer*)TIMER4_BASE;
+ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
+ break;
+ }
+
+ timer->CR1 = ARPE; // No clock division
+ // Do not buffer auto-reload preload
+ // Edge aligned
+ // Upcounter
+ // Do not stop counter at update event
+ // Update events enabled (etc, see bits [1:2])
+ // Counter disabled for now
+
+ timer->PSC = prescale; // Prescaling by prescale (duh)
+ timer->ARR = 0xFFFF; // Max reload cont
+
+ /* initialize all the channels to 50% duty cycle,
+ * TODO: none of them actually get output unless the gpio pin
+ * is set, this will probably consume a bit more power but
+ * we'll worry about that later. */
+ timer->CCR1 = 0x8FFF; // PWM start value
+ timer->CCMR1 |= 0x68; // PWM mode 1, enable preload register.
+ timer->CCER |= 0x001; // enable ch
+
+ timer->CCR2 = 0x8FFF; // PWM start value
+ timer->CCMR1 |= (0x68 << 8);// PWM mode 1, enable preload register.
+ timer->CCER |= 0x010; // enable ch
+
+ timer->CCR3 = 0x8FFF; // PWM start value
+ timer->CCMR2 |= 0x68; // PWM mode 1, enable preload register.
+ timer->CCER |= 0x100; // enable ch
+
+ timer->CCR4 = 0x8FFF; // PWM start value
+ timer->CCMR2 |= (0x68 << 8);// PWM mode 1, enable preload register.
+ timer->CCER |= 0x1000; // enable ch
+
+ /* Advanced timer? */
+ if (is_advanced) {
+ timer->BDTR = 0x8000; // moe enable
+ }
+
+ timer->DIER = 0; // disable update interrupt
+ timer->EGR = 1; // Initialize update event and shadow registers
+ timer->CR1 |= 1; // Enable timer
+}
diff --git a/src/lib/timers.h b/src/lib/timers.h
new file mode 100644
index 0000000..27e5999
--- /dev/null
+++ b/src/lib/timers.h
@@ -0,0 +1,118 @@
+/* Note to self:
+ * The timer clock frequencies are automatically fixed by hardware.
+ * There are two cases:
+ * 1. if the APB prescaler is 1, the timer clock frequencies are
+ * set to the same frequency as that of the APB domain to which
+ * the timers are connected.
+ * 2. otherwise, they are set to twice (x2) the frequency of the
+ * APB domain to which the timers are connected.
+ * See stm32 manual, 77/995
+ *
+ * hence, 72 mhz timers
+ * */
+
+/* Maple Timer channels:
+ * Timer Maple Pin STM32 Pin Type
+ * TIM1_CH1 D6 PA8 Advanced
+ * TIM1_CH2 D7 PA9 Advanced
+ * TIM1_CH3 D8 PA10 Advanced
+ *
+ * TIM2_CH1 D2 PA0
+ * TIM2_CH2 D3 PA1
+ * TIM2_CH3 D1 PA2
+ * TIM2_CH4 D0 PA3
+ *
+ * TIM3_CH1 D12 PA6
+ * TIM3_CH2 D11 PA7
+ * TIM3_CH3 EXT7 PB0
+ * TIM3_CH4 EXT8 PB1
+ *
+ * TIM4_CH1 EXT5 PB6
+ * TIM4_CH1 EXT9 PB7
+ * TIM4_CH1 EXT15 PB8
+ * TIM4_CH1 EXT4 PB9
+ *
+ * Not supported:
+ * TIM1_CH4 USBDM, not available PA11 Advanced
+ * TIM1_CH1_N EXT12 PB13
+ * TIM1_CH2_N EXT13 PB14
+ * TIM1_CH3_N EXT14 PB15
+ * */
+
+/* I don't like the Arduino API for dealing with pin modes.
+ * How about...
+ *
+ * pinMode(digitalPin, PWM);
+ * pwmWrite(digitalPin) */
+
+
+#ifndef _TIMERS_H_
+#define _TIMERS_H_
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+typedef volatile uint32_t* TimerCCR;
+
+#define TIMER1_BASE 0x40012C00
+#define TIMER2_BASE 0x40000000
+#define TIMER3_BASE 0x40000400
+#define TIMER4_BASE 0x40000800
+
+#define ARPE BIT(7) // Auto-reload preload enable
+#define NOT_A_TIMER 0
+
+#define TIMER1_CH1_CCR (TimerCCR)(TIMER1_BASE + 0x34)
+#define TIMER1_CH2_CCR (TimerCCR)(TIMER1_BASE + 0x38)
+#define TIMER1_CH3_CCR (TimerCCR)(TIMER1_BASE + 0x3C)
+#define TIMER1_CH4_CCR (TimerCCR)(TIMER1_BASE + 0x40)
+
+#define TIMER2_CH1_CCR (TimerCCR)(TIMER2_BASE + 0x34)
+#define TIMER2_CH2_CCR (TimerCCR)(TIMER2_BASE + 0x38)
+#define TIMER2_CH3_CCR (TimerCCR)(TIMER2_BASE + 0x3C)
+#define TIMER2_CH4_CCR (TimerCCR)(TIMER2_BASE + 0x40)
+
+#define TIMER3_CH1_CCR (TimerCCR)(TIMER3_BASE + 0x34)
+#define TIMER3_CH2_CCR (TimerCCR)(TIMER3_BASE + 0x38)
+#define TIMER3_CH3_CCR (TimerCCR)(TIMER3_BASE + 0x3C)
+#define TIMER3_CH4_CCR (TimerCCR)(TIMER3_BASE + 0x40)
+
+#define TIMER4_CH1_CCR (TimerCCR)(TIMER4_BASE + 0x34)
+#define TIMER4_CH2_CCR (TimerCCR)(TIMER4_BASE + 0x38)
+#define TIMER4_CH3_CCR (TimerCCR)(TIMER4_BASE + 0x3C)
+#define TIMER4_CH4_CCR (TimerCCR)(TIMER4_BASE + 0x40)
+
+
+/* Turn on timer with prescale as the divisor
+ * void timer_init(uint32_t timer, uint16_t prescale)
+ * timer -> {1-4}
+ * prescale -> {1-65535}
+ * */
+void timer_init(uint8_t, uint16_t);
+
+/* Turn on PWM with duty_cycle on the specified channel in timer.
+ * This function takes in a pointer to the corresponding CCR
+ * register for the pin cause it saves pwmWrite() a couple of
+ * cycles.
+ *
+ * void timer_pwm(uint8_t channel, uint8_t duty_cycle);
+ * channel -> {TIMERx_CHn_CCR}
+ * duty_cycle -> {0-65535}
+ *
+ * PRECONDITIONS:
+ * pin has been set to alternate function output
+ * timer has been initialized
+ */
+static inline void timer_pwm_write_ccr(TimerCCR CCR, uint16_t duty_cycle) {
+ *CCR = duty_cycle;
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+
+#endif
+
diff --git a/src/lib/usart.c b/src/lib/usart.c
new file mode 100644
index 0000000..c1a7f43
--- /dev/null
+++ b/src/lib/usart.c
@@ -0,0 +1,44 @@
+#include "stm32f10x_rcc.h"
+#include "usart.h"
+
+static inline void usart_putc(usart_port *port, uint8_t ch) {
+ port->DR = ch;
+
+ /* Wait till TXE = 1 */
+ while ((port->SR & USART_TXE) == 0)
+ ;
+}
+
+int32_t usart_init(uint8_t usart_num) {
+ ASSERT((usart_num < NR_USARTS) && (usart_num > 0));
+ usart_port *port;
+ uint32_t clk_speed;
+
+ switch (usart_num) {
+ case 1:
+ port = USART1_BASE;
+ clk_speed = USART1_CLK;
+ break;
+ case 2:
+ port = USART2_BASE;
+ clk_speed = USART2_CLK;
+ RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
+ break;
+ case 3:
+ port = USART3_BASE;
+ break;
+ default:
+ /* should never get here */
+ ASSERT(0);
+ }
+ uint32_t baud = 9600;
+ uint32_t usartdiv = clk_speed / baud;
+
+ /* Set baud rate */
+ port->BRR = BIT_MASK_SHIFT(B9600_MANTISSA, 4) | B9600_FRACTION;
+
+ /* Enable the USART and set 8n1 (M bit clear) enable transmitter*/
+ port->CR1 = USART_UE | USART_TE;
+
+ return 0;
+}
diff --git a/src/lib/usart.h b/src/lib/usart.h
new file mode 100644
index 0000000..30f7dc1
--- /dev/null
+++ b/src/lib/usart.h
@@ -0,0 +1,131 @@
+#ifndef _USART_H_
+#define _USART_H_
+#include <inttypes.h>
+#include <util.h>
+
+/* Transmit procedure:
+ * 1. Enable the USART by writing the UE bit in USART_CR1 register to 1.
+ *
+ * 2. Program the M bit in USART_CR1 to define the word length.
+ *
+ * 3. Program the number of stop bits in USART_CR2.
+ *
+ * 4. Select DMA enable (DMAT) in USART_CR3 if Multi buffer Communication is
+ * to take place. Configure the DMA register as explained in multibuffer
+ * communication.
+ *
+ * 5. Select the desired baud rate using the USART_BRR register.
+ *
+ * 6. Set the TE bit in USART_CR1 to send an idle frame as first transmission.
+ *
+ * 7. Write the data to send in the USART_DR register (this clears the TXE
+ * bit). Repeat this for each data to be transmitted in case of single buffer.
+ *
+ * 8. After writing the last data into the USART_DR register, wait until TC=1.
+ * This indicates that the transmission of the last frame is complete. This is
+ * required for instance when the USART is disabled or enters the Halt mode to
+ * avoid corrupting the last transmission.
+ *
+ * Single byte communication
+ * Clearing the TXE bit is always performed by a write to the data register.
+ *
+ * The TXE bit is set by hardware and it indicates:
+ * ? The data has been moved from TDR to the shift register and the data transmission has
+ * started.
+ * ? The TDR register is empty.
+ * ? The next data can be written in the USART_DR register without overwriting the
+ * previous data.
+ * This flag generates an interrupt if the TXEIE bit is set.
+ *
+ * When a transmission is taking place, a write instruction to the USART_DR
+ * register stores the data in the TDR register and which is copied in the
+ * shift register at the end of the current transmission.
+ *
+ * When no transmission is taking place, a write instruction to the USART_DR
+ * register places the data directly in the shift register, the data
+ * transmission starts, and the TXE bit is immediately set.
+ *
+ * If a frame is transmitted (after the stop bit) and the TXE bit is set, the
+ * TC bit goes high. An interrupt is generated if the TCIE bit is set in the
+ * USART_CR1 register. After writing the last data into the USART_DR register,
+ * it is mandatory to wait for TC=1 before disabling the USART or causing the
+ * microcontroller to enter the low power mode (see Figure 241: TC/TXE behavior
+ * when transmitting).
+ *
+ * Clearing the TC bit is performed by the following software sequence:
+ * 1. A read from the USART_SR register
+ * 2. A write to the USART_DR register
+ *
+ *
+ * For now, use 8N1
+ *
+ * Baud rate is generated by programming the mantissa and fraction values of
+ * USARTDIV
+ *
+ * baud = fck / 16*USARTDIV
+ * Fck = PLK1 for USART2 and USART3 = 36MHz
+ * Fck = PCLK2 for USART1 = 72MHz
+ *
+ * Baud Actual USARTDIV_36MHZ Error USARTDIV_72MHZ Error
+ * 2400 2.400 937.5 0% 1875 0%
+ * 9600 9.600 234.375 0% 468.75 0%
+ * 19200 19.2 117.1875 0% 234.375 0%
+ * 57600 57.6 39.0625 0% 78.125 0.%
+ * 115200 115.384 19.5 0.15% 39.0625 0%
+ * 230400 230.769 9.75 0.16% 19.5 0.16%
+ * 460800 461.538 4.875 0.16% 9.75 0.16%
+ * 921600 923.076 2.4375 0.16% 4.875 0.16%
+ * 225000 2250 1 0% 2 0%
+ *
+ * USART_BRR[15:4] = mantissa
+ * USART_BRR[3:0] = fraction
+ * 111010
+ * 0110
+ * */
+#define NR_USARTS 0x3
+
+//#define USART1_BASE 0x40013800
+//#define USART2_BASE 0x40004400
+//#define USART3_BASE 0x40004800
+
+#define USART_UE BIT(13)
+#define USART_M BIT(12)
+#define USART_TE BIT(3)
+#define USART_TXE BIT(7)
+#define USART_STOP_BITS_1 BIT_MASK_SHIFT(0b0, 12)
+#define USART_STOP_BITS_05 BIT_MASK_SHIFT(0b01, 12)
+#define USART_STOP_BITS_2 BIT_MASK_SHIFT(0b02, 12)
+#define USART_STOP_BITS_15 BIT_MASK_SHIFT(0b02, 12)
+
+#define USART1_CLK 72000000UL
+#define USART2_CLK 36000000UL
+#define USART3_CLK 36000000UL
+
+#define B9600_MANTISSA 0xEA
+#define B9600_FRACTION 0x06
+
+
+typedef struct usart_port {
+ volatile uint32_t SR;
+ volatile uint32_t DR;
+ volatile uint32_t BRR;
+ volatile uint32_t CR1;
+ volatile uint32_t CR2;
+ volatile uint32_t CR3;
+ volatile uint32_t GTPR;
+} usart_port;
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+int32_t usart_init(uint8_t);
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+
+#endif
+
diff --git a/src/lib/util.cpp b/src/lib/util.cpp
new file mode 100644
index 0000000..4eb4fe0
--- /dev/null
+++ b/src/lib/util.cpp
@@ -0,0 +1,47 @@
+#include "wiring.h"
+#include "Serial.h"
+#include "util.h"
+#include "io.h"
+
+#define ERROR_PIN 13
+
+/* Required for C++ hackery */
+extern "C" void __cxa_pure_virtual(void) {
+ while(1)
+ ;
+}
+
+/* Error assert + fade */
+void _fail(const char* file, int line, const char* exp) {
+ int32_t slope = 1;
+ int32_t CC = 0x0000;
+ int32_t TOP_CNT = 0x02FF;
+ int32_t i = 0;
+
+ Serial1.print("ERROR: FAILED ASSERT(");
+ Serial1.print(exp);
+ Serial1.print("): ");
+ Serial1.print(file);
+ Serial1.print(":");
+ Serial1.println(line);
+
+ while (1) {
+ if (CC == TOP_CNT) {
+ slope = -1;
+ } else if (CC == 0) {
+ slope = 1;
+ }
+
+ if (i == TOP_CNT) {
+ CC += slope;
+ i = 0;
+ }
+
+ if (i < CC) {
+ digitalWrite(ERROR_PIN, HIGH);
+ } else {
+ digitalWrite(ERROR_PIN, LOW);
+ }
+ i++;
+ }
+}
diff --git a/src/lib/util.h b/src/lib/util.h
new file mode 100644
index 0000000..8701af1
--- /dev/null
+++ b/src/lib/util.h
@@ -0,0 +1,57 @@
+/* Generally "useful" utility procedures */
+#ifndef _UTIL_H_
+#define _UTIL_H_
+#include <inttypes.h>
+
+#define MAPLE_DEBUG 1
+
+#define BIT(shift) (1 << (shift))
+#define BIT_MASK_SHIFT(mask, shift) ((mask) << (shift))
+
+/* Return bits m to n of x */
+#define GET_BITS(x, m, n) ((((uint32_t)x) << (31 - (n))) >> ((31 - (n)) + (m)))
+
+/* Bit-banding macros */
+#define BITBAND_SRAM_REF 0x20000000
+#define BITBAND_SRAM_BASE 0x22000000
+#define BITBAND_SRAM(a,b) ((BITBAND_SRAM_BASE + (a-BITBAND_SRAM_REF)*32 + (b*4))) // Convert SRAM address
+#define BITBAND_PERI_REF 0x40000000
+#define BITBAND_PERI_BASE 0x42000000
+#define BITBAND_PERI(a,b) ((BITBAND_PERI_BASE + (a-BITBAND_PERI_REF)*32 + (b*4))) // Convert PERI address
+
+#define COUNTFLAG *((volatile unsigned char*) (BITBAND_PERI(SYSTICK_CSR,2)))
+
+#define REG_SET(reg, val) (*(volatile uint32_t*)(reg) = (val))
+#define REG_SET_BIT(reg, bit) (*(volatile uint32_t*)(reg) |= BIT(bit))
+#define REG_CLEAR_BIT(reg, bit) (*(volatile uint32_t*)(reg) &= ~BIT(bit))
+#define REG_SET_MASK(reg, mask) (*(volatile uint32_t*)(reg) |= (uint32_t)(mask))
+#define REG_CLEAR_MASK(reg, mask) (*(volatile uint32_t*)(reg) &= (uint32_t)~(mask))
+
+#define REG_GET(reg) *(volatile uint32_t*)(reg)
+
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+void _fail(const char*, int, const char*);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+
+/* Assert for sanity checks, undefine MAPLE_DEBUG to compile
+ * out these checks */
+#if MAPLE_DEBUG
+#define ASSERT(exp) \
+ if (exp) \
+ {} \
+ else \
+ _fail(__FILE__, __LINE__, #exp)
+#else
+#define ASSERT(exp) (void)((0))
+#endif
+
+#endif
+