diff options
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/adc.c | 68 | ||||
-rw-r--r-- | src/lib/adc.h | 67 | ||||
-rw-r--r-- | src/lib/exti.c | 250 | ||||
-rw-r--r-- | src/lib/exti.h | 143 | ||||
-rw-r--r-- | src/lib/gpio.c | 14 | ||||
-rw-r--r-- | src/lib/gpio.h | 100 | ||||
-rw-r--r-- | src/lib/stm32f10x_conf.h | 132 | ||||
-rw-r--r-- | src/lib/stm32f10x_it.c | 252 | ||||
-rw-r--r-- | src/lib/syscalls.c | 156 | ||||
-rw-r--r-- | src/lib/systick.c | 30 | ||||
-rw-r--r-- | src/lib/systick.h | 37 | ||||
-rw-r--r-- | src/lib/timers.c | 114 | ||||
-rw-r--r-- | src/lib/timers.h | 118 | ||||
-rw-r--r-- | src/lib/usart.c | 44 | ||||
-rw-r--r-- | src/lib/usart.h | 131 | ||||
-rw-r--r-- | src/lib/util.cpp | 47 | ||||
-rw-r--r-- | src/lib/util.h | 57 |
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 + |