diff options
| author | iperry <iperry@749a229e-a60e-11de-b98f-4500b42dc123> | 2009-12-17 02:37:07 +0000 | 
|---|---|---|
| committer | iperry <iperry@749a229e-a60e-11de-b98f-4500b42dc123> | 2009-12-17 02:37:07 +0000 | 
| commit | 32e57dac2e61e79b029593eb4d34d727bcc10678 (patch) | |
| tree | 98d7ff41993576bb150d13d5f63dc744f6812852 /src/lib | |
| download | librambutan-32e57dac2e61e79b029593eb4d34d727bcc10678.tar.gz librambutan-32e57dac2e61e79b029593eb4d34d727bcc10678.zip | |
Initial commit of library code, moved from leaftest repo
git-svn-id: https://leaflabs.googlecode.com/svn/trunk/library@69 749a229e-a60e-11de-b98f-4500b42dc123
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 + | 
