From 4b86c281410aa330a1640e4211ae9da476dd0f56 Mon Sep 17 00:00:00 2001 From: Jeff Ciesielski Date: Fri, 19 Oct 2012 16:27:17 -0700 Subject: [PATCH 01/22] stm32/f1/can: Add loopback/silent vars to init function. Also: add helper function for mailbox checking --- include/libopencm3/stm32/can.h | 5 +++-- lib/stm32/f1/can.c | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/include/libopencm3/stm32/can.h b/include/libopencm3/stm32/can.h index d6636d5f..f787df76 100644 --- a/include/libopencm3/stm32/can.h +++ b/include/libopencm3/stm32/can.h @@ -619,7 +619,8 @@ BEGIN_DECLS void can_reset(u32 canport); int can_init(u32 canport, bool ttcm, bool abom, bool awum, bool nart, - bool rflm, bool txfp, u32 sjw, u32 ts1, u32 ts2, u32 brp); + bool rflm, bool txfp, u32 sjw, u32 ts1, u32 ts2, u32 brp, + bool loopback, bool silent); void can_filter_init(u32 canport, u32 nr, bool scale_32bit, bool id_list_mode, u32 fr1, u32 fr2, u32 fifo, bool enable); @@ -640,7 +641,7 @@ void can_receive(u32 canport, u8 fifo, bool release, u32 *id, bool *ext, bool *rtr, u32 *fmi, u8 *length, u8 *data); void can_fifo_release(u32 canport, u8 fifo); - +bool can_available_mailbox(u32 canport); END_DECLS #endif diff --git a/lib/stm32/f1/can.c b/lib/stm32/f1/can.c index fc7e0e75..46d0e652 100644 --- a/lib/stm32/f1/can.c +++ b/lib/stm32/f1/can.c @@ -32,7 +32,8 @@ void can_reset(u32 canport) } int can_init(u32 canport, bool ttcm, bool abom, bool awum, bool nart, - bool rflm, bool txfp, u32 sjw, u32 ts1, u32 ts2, u32 brp) + bool rflm, bool txfp, u32 sjw, u32 ts1, u32 ts2, u32 brp, + bool loopback, bool silent) { u32 wait_ack = 0x00000000; u32 can_msr_inak_timeout = 0x0000FFFF; @@ -85,8 +86,19 @@ int can_init(u32 canport, bool ttcm, bool abom, bool awum, bool nart, else CAN_MCR(canport) &= ~CAN_MCR_TXFP; + if (silent) + CAN_BTR(canport) |= (CAN_BTR_SILM); + else + CAN_BTR(canport) &= !(CAN_BTR_SILM); + + if (loopback) + CAN_BTR(canport) |= (CAN_BTR_LBKM); + else + CAN_BTR(canport) &= !(CAN_BTR_LBKM); + + /* Set bit timings. */ - CAN_BTR(canport) = sjw | ts2 | ts1 | + CAN_BTR(canport) |= sjw | ts2 | ts1 | (u32)(CAN_BTR_BRP_MASK & (brp - 1)); /* Request initialization "leave". */ From a2fc8768c60acdc01b5907984c4f5b79575d4c47 Mon Sep 17 00:00:00 2001 From: Jeff Ciesielski Date: Fri, 19 Oct 2012 16:28:44 -0700 Subject: [PATCH 02/22] stm32/f1/dma: add clear_flag helper function --- include/libopencm3/stm32/f1/dma.h | 1 + lib/stm32/f1/dma.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/include/libopencm3/stm32/f1/dma.h b/include/libopencm3/stm32/f1/dma.h index b08803f7..8a2edbad 100644 --- a/include/libopencm3/stm32/f1/dma.h +++ b/include/libopencm3/stm32/f1/dma.h @@ -369,6 +369,7 @@ void dma_disable_channel(u32 dma, u8 channel); void dma_set_peripheral_address(u32 dma, u8 channel, u32 address); void dma_set_memory_address(u32 dma, u8 channel, u32 address); void dma_set_number_of_data(u32 dma, u8 channel, u16 number); +void dma_clear_flag(u32 dma, u32 flag); END_DECLS diff --git a/lib/stm32/f1/dma.c b/lib/stm32/f1/dma.c index 04cb8a10..2bc89265 100644 --- a/lib/stm32/f1/dma.c +++ b/lib/stm32/f1/dma.c @@ -363,5 +363,10 @@ void dma_set_number_of_data(u32 dma, u8 channel, u16 number) { DMA_CNDTR(dma, channel) = number; } + +void dma_clear_flag(u32 dma, u32 flag) +{ + DMA_ISR(dma) &= ~flag; +} /**@}*/ From be4ee41ef98bfd40480634812fee6cccd5cd7360 Mon Sep 17 00:00:00 2001 From: Jeff Ciesielski Date: Fri, 19 Oct 2012 16:29:20 -0700 Subject: [PATCH 03/22] stm32/f1/i2c: add a whole bevy of additional helper functions --- include/libopencm3/stm32/i2c.h | 16 +++++- lib/stm32/i2c.c | 93 +++++++++++++++++++++++++++++++++- 2 files changed, 107 insertions(+), 2 deletions(-) diff --git a/include/libopencm3/stm32/i2c.h b/include/libopencm3/stm32/i2c.h index a59c4202..05a4d168 100644 --- a/include/libopencm3/stm32/i2c.h +++ b/include/libopencm3/stm32/i2c.h @@ -322,7 +322,8 @@ LGPL License Terms @ref lgpl_license /* DUTY: Fast Mode Duty Cycle */ #define I2C_CCR_DUTY (1 << 14) - +#define I2C_CCR_DUTY_DIV2 0 +#define I2C_CCR_DUTY_16_DIV_9 1 /* Note: Bits [13:12] are reserved, and forced to 0 by hardware. */ /* @@ -359,6 +360,7 @@ void i2c_peripheral_enable(u32 i2c); void i2c_peripheral_disable(u32 i2c); void i2c_send_start(u32 i2c); void i2c_send_stop(u32 i2c); +void i2c_clear_stop(u32 i2c); void i2c_set_own_7bit_slave_address(u32 i2c, u8 slave); void i2c_set_own_10bit_slave_address(u32 i2c, u16 slave); void i2c_set_fast_mode(u32 i2c); @@ -368,6 +370,18 @@ void i2c_set_ccr(u32 i2c, u16 freq); void i2c_set_trise(u32 i2c, u16 trise); void i2c_send_7bit_address(u32 i2c, u8 slave, u8 readwrite); void i2c_send_data(u32 i2c, u8 data); +uint8_t i2c_get_data(u32 i2c); +void i2c_enable_interrupt(u32 i2c, u32 interrupt); +void i2c_disable_interrupt(u32 i2c, u32 interrupt); +void i2c_enable_ack(u32 i2c); +void i2c_disable_ack(u32 i2c); +void i2c_nack_next(u32 i2c); +void i2c_nack_current(u32 i2c); +void i2c_set_dutycycle(u32 i2c, u32 dutycycle); +void i2c_enable_dma(u32 i2c); +void i2c_disable_dma(u32 i2c); +void i2c_set_dma_last_transfer(u32 i2c); +void i2c_clear_dma_last_transfer(u32 i2c); END_DECLS diff --git a/lib/stm32/i2c.c b/lib/stm32/i2c.c index e1d3a095..e1e62533 100644 --- a/lib/stm32/i2c.c +++ b/lib/stm32/i2c.c @@ -124,6 +124,18 @@ void i2c_send_stop(u32 i2c) I2C_CR1(i2c) |= I2C_CR1_STOP; } +/*-----------------------------------------------------------------------------*/ +/** @brief I2C Clear Stop Flag. + +Clear the "Send Stop" flag in the I2C config register + +@param[in] i2c Unsigned int32. I2C register base address @ref i2c_reg_base. +*/ +void i2c_clear_stop(u32 i2c) +{ + I2C_CR1(i2c) &= ~I2C_CR1_STOP; +} + /*-----------------------------------------------------------------------------*/ /** @brief I2C Set the 7 bit Slave Address for the Peripheral. @@ -269,5 +281,84 @@ void i2c_send_data(u32 i2c, u8 data) I2C_DR(i2c) = data; } -/**@}*/ +/*-----------------------------------------------------------------------------*/ +/** @brief I2C Get Data. +@param[in] i2c Unsigned int32. I2C register base address @ref i2c_reg_base. +*/ +uint8_t i2c_get_data(u32 i2c) +{ + return (I2C_DR(i2c) & 0xff); +} + +/*-----------------------------------------------------------------------------*/ +/** @brief I2C Enable Interrupt + +@param[in] i2c Unsigned int32. I2C register base address @ref i2c_reg_base. +@param[in] interrupt Unsigned int32. Interrupt to enable. +*/ +void i2c_enable_interrupt(u32 i2c, u32 interrupt) +{ + I2C_CR2(i2c) |= interrupt; +} + +/*-----------------------------------------------------------------------------*/ +/** @brief I2C Disable Interrupt + +@param[in] i2c Unsigned int32. I2C register base address @ref i2c_reg_base. +@param[in] interrupt Unsigned int32. Interrupt to disable. +*/ +void i2c_disable_interrupt(u32 i2c, u32 interrupt) +{ + I2C_CR2(i2c) &= ~interrupt; +} + +void i2c_enable_ack(u32 i2c) +{ + I2C_CR1(i2c) |= I2C_CR1_ACK; +} + +void i2c_disable_ack(u32 i2c) +{ + I2C_CR1(i2c) &= ~I2C_CR1_ACK; +} + +void i2c_nack_next(u32 i2c) +{ + I2C_CR1(i2c) |= I2C_CR1_POS; +} + +void i2c_nack_current(u32 i2c) +{ + I2C_CR1(i2c) &= ~I2C_CR1_POS; +} + +void i2c_set_dutycycle(u32 i2c, u32 dutycycle) +{ + if (dutycycle == I2C_CCR_DUTY_DIV2) + I2C_CCR(i2c) &= ~I2C_CCR_DUTY; + else + I2C_CCR(i2c) |= I2C_CCR_DUTY; +} + +void i2c_enable_dma(u32 i2c) +{ + I2C_CR2(i2c) |= I2C_CR2_DMAEN; +} + +void i2c_disable_dma(u32 i2c) +{ + I2C_CR2(i2c) &= ~I2C_CR2_DMAEN; +} + +void i2c_set_dma_last_transfer(u32 i2c) +{ + I2C_CR2(i2c) |= I2C_CR2_LAST; +} + +void i2c_clear_dma_last_transfer(u32 i2c) +{ + I2C_CR2(i2c) &= ~I2C_CR2_LAST; +} + +/**@}*/ From 153e81bc96add8229e29e495e21adf9e8e9c8ddc Mon Sep 17 00:00:00 2001 From: Jeff Ciesielski Date: Fri, 19 Oct 2012 16:30:27 -0700 Subject: [PATCH 04/22] stm32/f1/vector: add ability to boot to DFU bootloader note: Only works on CL devices also: this could probably use a helper function --- lib/stm32/f1/vector.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/stm32/f1/vector.c b/lib/stm32/f1/vector.c index f496ae4b..d660774b 100644 --- a/lib/stm32/f1/vector.c +++ b/lib/stm32/f1/vector.c @@ -191,10 +191,19 @@ void (*const vector_table[]) (void) = { otg_fs_isr, /* Addr: 0x0000_014C */ }; +#include void reset_handler(void) { volatile unsigned *src, *dest; + uint32_t reset_str = *((uint32_t *)0x2000FFF0); + if (reset_str == 0xDEADBEEF) { + *((uint32_t *)0x2000FFF0) = 0x00; + asm("ldr r0, =0x1fffb000"); + asm("ldr sp, [r0, #0]"); + asm("ldr r0, [r0, #4]"); + asm("bx r0"); + } __asm__("MSR msp, %0" : : "r"(&_stack)); for (src = &_data_loadaddr, dest = &_data; dest < &_edata; src++, dest++) From 069a758f6c59faaed61da45c12573f831da9c4af Mon Sep 17 00:00:00 2001 From: Jeff Ciesielski Date: Fri, 19 Oct 2012 16:31:52 -0700 Subject: [PATCH 05/22] stm32/f1/rcc: add 25mhz in to 72mhz out clock tree setup function. also: added support for previously undefined PLLs --- include/libopencm3/stm32/f1/rcc.h | 24 ++++- lib/stm32/f1/rcc.c | 167 ++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+), 2 deletions(-) diff --git a/include/libopencm3/stm32/f1/rcc.h b/include/libopencm3/stm32/f1/rcc.h index 855e43bb..52b3469c 100644 --- a/include/libopencm3/stm32/f1/rcc.h +++ b/include/libopencm3/stm32/f1/rcc.h @@ -86,7 +86,7 @@ LGPL License Terms @ref lgpl_license #define RCC_CFGR_MCO_SYSCLK 0x4 #define RCC_CFGR_MCO_HSICLK 0x5 #define RCC_CFGR_MCO_HSECLK 0x6 -#define RCC_CFGR_RMCO_PLLCLK_DIV2 0x7 +#define RCC_CFGR_MCO_PLLCLK_DIV2 0x7 #define RCC_CFGR_MCO_PLL2CLK 0x8 /* (**) */ #define RCC_CFGR_MCO_PLL3CLK_DIV2 0x9 /* (**) */ #define RCC_CFGR_MCO_XT1 0xa /* (**) */ @@ -448,6 +448,24 @@ LGPL License Terms @ref lgpl_license #define RCC_CFGR2_PLL2MUL_PLL2_CLK_MUL16 0xe #define RCC_CFGR2_PLL2MUL_PLL2_CLK_MUL20 0xf +/* PREDIV: PREDIV division factor */ +#define RCC_CFGR2_PREDIV_NODIV 0x0 +#define RCC_CFGR2_PREDIV_DIV2 0x1 +#define RCC_CFGR2_PREDIV_DIV3 0x2 +#define RCC_CFGR2_PREDIV_DIV4 0x3 +#define RCC_CFGR2_PREDIV_DIV5 0x4 +#define RCC_CFGR2_PREDIV_DIV6 0x5 +#define RCC_CFGR2_PREDIV_DIV7 0x6 +#define RCC_CFGR2_PREDIV_DIV8 0x7 +#define RCC_CFGR2_PREDIV_DIV9 0x8 +#define RCC_CFGR2_PREDIV_DIV10 0x9 +#define RCC_CFGR2_PREDIV_DIV11 0xa +#define RCC_CFGR2_PREDIV_DIV12 0xb +#define RCC_CFGR2_PREDIV_DIV13 0xc +#define RCC_CFGR2_PREDIV_DIV14 0xd +#define RCC_CFGR2_PREDIV_DIV15 0xe +#define RCC_CFGR2_PREDIV_DIV16 0xf + /* PREDIV2: PREDIV2 division factor */ #define RCC_CFGR2_PREDIV2_NODIV 0x0 #define RCC_CFGR2_PREDIV2_DIV2 0x1 @@ -473,7 +491,7 @@ extern u32 rcc_ppre2_frequency; /* --- Function prototypes ------------------------------------------------- */ typedef enum { - PLL, HSE, HSI, LSE, LSI + PLL, PLL2, PLL3, HSE, HSI, LSE, LSI } osc_t; BEGIN_DECLS @@ -497,6 +515,7 @@ void rcc_peripheral_reset(volatile u32 *reg, u32 reset); void rcc_peripheral_clear_reset(volatile u32 *reg, u32 clear_reset); void rcc_set_sysclk_source(u32 clk); void rcc_set_pll_multiplication_factor(u32 mul); +void rcc_set_pll2_multiplication_factor(u32 mul); void rcc_set_pll_source(u32 pllsrc); void rcc_set_pllxtpre(u32 pllxtpre); void rcc_set_adcpre(u32 adcpre); @@ -512,6 +531,7 @@ void rcc_clock_setup_in_hse_8mhz_out_24mhz(void); void rcc_clock_setup_in_hse_8mhz_out_72mhz(void); void rcc_clock_setup_in_hse_12mhz_out_72mhz(void); void rcc_clock_setup_in_hse_16mhz_out_72mhz(void); +void rcc_clock_setup_in_hse_25mhz_out_72mhz(void); void rcc_backupdomain_reset(void); END_DECLS diff --git a/lib/stm32/f1/rcc.c b/lib/stm32/f1/rcc.c index ab3350b2..9cd86588 100644 --- a/lib/stm32/f1/rcc.c +++ b/lib/stm32/f1/rcc.c @@ -71,6 +71,12 @@ void rcc_osc_ready_int_clear(osc_t osc) case PLL: RCC_CIR |= RCC_CIR_PLLRDYC; break; + case PLL2: + RCC_CIR |= RCC_CIR_PLL2RDYC; + break; + case PLL3: + RCC_CIR |= RCC_CIR_PLL3RDYC; + break; case HSE: RCC_CIR |= RCC_CIR_HSERDYC; break; @@ -98,6 +104,12 @@ void rcc_osc_ready_int_enable(osc_t osc) case PLL: RCC_CIR |= RCC_CIR_PLLRDYIE; break; + case PLL2: + RCC_CIR |= RCC_CIR_PLL2RDYIE; + break; + case PLL3: + RCC_CIR |= RCC_CIR_PLL3RDYIE; + break; case HSE: RCC_CIR |= RCC_CIR_HSERDYIE; break; @@ -125,6 +137,12 @@ void rcc_osc_ready_int_disable(osc_t osc) case PLL: RCC_CIR &= ~RCC_CIR_PLLRDYIE; break; + case PLL2: + RCC_CIR &= ~RCC_CIR_PLL2RDYIE; + break; + case PLL3: + RCC_CIR &= ~RCC_CIR_PLL3RDYIE; + break; case HSE: RCC_CIR &= ~RCC_CIR_HSERDYIE; break; @@ -153,6 +171,12 @@ int rcc_osc_ready_int_flag(osc_t osc) case PLL: return ((RCC_CIR & RCC_CIR_PLLRDYF) != 0); break; + case PLL2: + return ((RCC_CIR & RCC_CIR_PLL2RDYF) != 0); + break; + case PLL3: + return ((RCC_CIR & RCC_CIR_PLL3RDYF) != 0); + break; case HSE: return ((RCC_CIR & RCC_CIR_HSERDYF) != 0); break; @@ -203,6 +227,12 @@ void rcc_wait_for_osc_ready(osc_t osc) case PLL: while ((RCC_CR & RCC_CR_PLLRDY) == 0); break; + case PLL2: + while ((RCC_CR & RCC_CR_PLL2RDY) == 0); + break; + case PLL3: + while ((RCC_CR & RCC_CR_PLL3RDY) == 0); + break; case HSE: while ((RCC_CR & RCC_CR_HSERDY) == 0); break; @@ -238,6 +268,12 @@ void rcc_osc_on(osc_t osc) case PLL: RCC_CR |= RCC_CR_PLLON; break; + case PLL2: + RCC_CR |= RCC_CR_PLL2ON; + break; + case PLL3: + RCC_CR |= RCC_CR_PLL3ON; + break; case HSE: RCC_CR |= RCC_CR_HSEON; break; @@ -273,6 +309,12 @@ void rcc_osc_off(osc_t osc) case PLL: RCC_CR &= ~RCC_CR_PLLON; break; + case PLL2: + RCC_CR &= ~RCC_CR_PLL2ON; + break; + case PLL3: + RCC_CR &= ~RCC_CR_PLL3ON; + break; case HSE: RCC_CR &= ~RCC_CR_HSEON; break; @@ -331,6 +373,8 @@ void rcc_osc_bypass_enable(osc_t osc) RCC_BDCR |= RCC_BDCR_LSEBYP; break; case PLL: + case PLL2: + case PLL3: case HSI: case LSI: /* Do nothing, only HSE/LSE allowed here. */ @@ -361,6 +405,8 @@ void rcc_osc_bypass_disable(osc_t osc) RCC_BDCR &= ~RCC_BDCR_LSEBYP; break; case PLL: + case PLL2: + case PLL3: case HSI: case LSI: /* Do nothing, only HSE/LSE allowed here. */ @@ -484,6 +530,40 @@ void rcc_set_pll_multiplication_factor(u32 mul) RCC_CFGR = (reg32 | (mul << 18)); } +/*-----------------------------------------------------------------------------*/ +/** @brief RCC Set the PLL2 Multiplication Factor. + +@note This only has effect when the PLL is disabled. + +@param[in] mul Unsigned int32. PLL multiplication factor @ref rcc_cfgr_pmf +*/ + +void rcc_set_pll2_multiplication_factor(u32 mul) +{ + u32 reg32; + + reg32 = RCC_CFGR2; + reg32 &= ~((1 << 11) | (1 << 10) | (1 << 9) | (1 << 8)); + RCC_CFGR2 = (reg32 | (mul << 8)); +} + +/*-----------------------------------------------------------------------------*/ +/** @brief RCC Set the PLL3 Multiplication Factor. + +@note This only has effect when the PLL is disabled. + +@param[in] mul Unsigned int32. PLL multiplication factor @ref rcc_cfgr_pmf +*/ + +void rcc_set_pll3_multiplication_factor(u32 mul) +{ + u32 reg32; + + reg32 = RCC_CFGR2; + reg32 &= ~((1 << 15) | (1 << 14) | (1 << 13) | (1 << 12)); + RCC_CFGR2 = (reg32 | (mul << 12)); +} + /*-----------------------------------------------------------------------------*/ /** @brief RCC Set the PLL Clock Source. @@ -602,6 +682,36 @@ void rcc_set_usbpre(u32 usbpre) RCC_CFGR = (reg32 | (usbpre << 22)); } +void rcc_set_prediv1(u32 prediv) +{ + u32 reg32; + reg32 = RCC_CFGR2; + reg32 &= ~(1 << 3) | (1 << 2) | (1 << 1) | (1 << 0); + RCC_CFGR2 |= (reg32 | prediv); +} + +void rcc_set_prediv2(u32 prediv) +{ + u32 reg32; + reg32 = RCC_CFGR2; + reg32 &= ~(1 << 7) | (1 << 6) | (1 << 5) | (1 << 4); + RCC_CFGR2 |= (reg32 | (prediv << 4)); +} + +void rcc_set_prediv1_source(u32 rccsrc) +{ + RCC_CFGR2 &= ~(1 << 16); + RCC_CFGR2 |= (rccsrc << 16); +} + +void rcc_set_mco(u32 mcosrc) +{ + u32 reg32; + reg32 = RCC_CFGR; + reg32 &= ~((1 << 27) | (1 << 26) | (1 << 25) | (1 << 24)); + RCC_CFGR |= (reg32 | (mcosrc << 24)); +} + /*-----------------------------------------------------------------------------*/ /** @brief RCC Get the System Clock Source. @@ -1030,6 +1140,63 @@ void rcc_clock_setup_in_hse_16mhz_out_72mhz(void) rcc_ppre2_frequency = 72000000; } +/*-----------------------------------------------------------------------------*/ +/** @brief RCC Set System Clock PLL at 72MHz from HSE at 25MHz + +*/ + +void rcc_clock_setup_in_hse_25mhz_out_72mhz(void) +{ + /* Enable external high-speed oscillator 25MHz. */ + rcc_osc_on(HSE); + rcc_wait_for_osc_ready(HSE); + rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_HSECLK); + + /* + * Sysclk runs with 72MHz -> 2 waitstates. + * 0WS from 0-24MHz + * 1WS from 24-48MHz + * 2WS from 48-72MHz + */ + flash_set_ws(FLASH_LATENCY_2WS); + + /* + * Set prescalers for AHB, ADC, ABP1, ABP2. + * Do this before touching the PLL (TODO: why?). + */ + rcc_set_hpre(RCC_CFGR_HPRE_SYSCLK_NODIV); /* Set. 72MHz Max. 72MHz */ + rcc_set_adcpre(RCC_CFGR_ADCPRE_PCLK2_DIV6); /* Set. 12MHz Max. 14MHz */ + rcc_set_ppre1(RCC_CFGR_PPRE1_HCLK_DIV2); /* Set. 36MHz Max. 36MHz */ + rcc_set_ppre2(RCC_CFGR_PPRE2_HCLK_NODIV); /* Set. 72MHz Max. 72MHz */ + + /* Set pll2 prediv and multiplier */ + rcc_set_prediv2(RCC_CFGR2_PREDIV2_DIV5); + rcc_set_pll2_multiplication_factor(RCC_CFGR2_PLL2MUL_PLL2_CLK_MUL8); + + /* Enable PLL2 oscillator and wait for it to stabilize */ + rcc_osc_on(PLL2); + rcc_wait_for_osc_ready(PLL2); + + /* Set pll1 prediv/multiplier, prediv1 src, and usb predivider */ + rcc_set_pllxtpre(RCC_CFGR_PLLXTPRE_HSE_CLK); + rcc_set_prediv1_source(RCC_CFGR2_PREDIV1SRC_PLL2_CLK); + rcc_set_prediv1(RCC_CFGR2_PREDIV_DIV5); + rcc_set_pll_multiplication_factor(RCC_CFGR_PLLMUL_PLL_CLK_MUL9); + rcc_set_pll_source(RCC_CFGR_PLLSRC_PREDIV1_CLK); + rcc_set_usbpre(RCC_CFGR_USBPRE_PLL_VCO_CLK_DIV3); + + /* enable PLL1 and wait for it to stabilize */ + rcc_osc_on(PLL); + rcc_wait_for_osc_ready(PLL); + + /* Select PLL as SYSCLK source. */ + rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_PLLCLK); + + /* Set the peripheral clock frequencies used */ + rcc_ppre1_frequency = 36000000; + rcc_ppre2_frequency = 72000000; +} + /*-----------------------------------------------------------------------------*/ /** @brief RCC Reset the backup domain From 527eeacbffc61f7bd2d4f4353db1c669065b99a1 Mon Sep 17 00:00:00 2001 From: Jeff Ciesielski Date: Fri, 19 Oct 2012 16:32:37 -0700 Subject: [PATCH 06/22] stm32/f1/rtc: add PLL2/3 to switch statement to avoid compiler warning --- lib/stm32/f1/rtc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/stm32/f1/rtc.c b/lib/stm32/f1/rtc.c index 08a49536..cfc5f5b6 100644 --- a/lib/stm32/f1/rtc.c +++ b/lib/stm32/f1/rtc.c @@ -67,6 +67,8 @@ void rtc_awake_from_off(osc_t clock_source) RCC_BDCR |= (1 << 9) | (1 << 8); break; case PLL: + case PLL2: + case PLL3: case HSI: /* Unusable clock source, here to prevent warnings. */ /* Turn off clock sources to RTC. */ From 32924fcc159c2e55b589ea7f54402c5ddc89097d Mon Sep 17 00:00:00 2001 From: Jeff Ciesielski Date: Fri, 19 Oct 2012 16:33:07 -0700 Subject: [PATCH 07/22] examples: fix lisa-m-1 can example to use new can init function --- examples/stm32/f1/lisa-m-1/can/can.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/stm32/f1/lisa-m-1/can/can.c b/examples/stm32/f1/lisa-m-1/can/can.c index 6201547d..26309058 100644 --- a/examples/stm32/f1/lisa-m-1/can/can.c +++ b/examples/stm32/f1/lisa-m-1/can/can.c @@ -135,7 +135,9 @@ void can_setup(void) CAN_BTR_SJW_1TQ, CAN_BTR_TS1_3TQ, CAN_BTR_TS2_4TQ, - 12)) /* BRP+1: Baud rate prescaler */ + 12, /* BRP+1: Baud rate prescaler */ + false, /* loopback mode */ + false)) /* silent mode */ { gpio_set(GPIOA, GPIO8); /* LED0 off */ gpio_set(GPIOB, GPIO4); /* LED1 off */ From 1cb373464cbd43e55fecbebcf9f8120fda36a6d0 Mon Sep 17 00:00:00 2001 From: Jeff Ciesielski Date: Fri, 19 Oct 2012 16:33:38 -0700 Subject: [PATCH 08/22] stm32/f1/gpio: use |= instead of = when setting up registers --- lib/stm32/f1/gpio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/stm32/f1/gpio.c b/lib/stm32/f1/gpio.c index 06020125..e513d29e 100644 --- a/lib/stm32/f1/gpio.c +++ b/lib/stm32/f1/gpio.c @@ -292,7 +292,7 @@ value cannot be ascertained from the hardware. */ void gpio_primary_remap(u8 swjdisable, u32 maps) { - AFIO_MAPR = swjdisable | (maps & 0x1FFFFF); + AFIO_MAPR |= swjdisable | (maps & 0x1FFFFF); } /*-----------------------------------------------------------------------------*/ @@ -310,7 +310,7 @@ The AFIO remapping feature is used only with the STM32F10x series. */ void gpio_secondary_remap(u32 maps) { - AFIO_MAPR2 = maps; + AFIO_MAPR2 |= maps; } /**@}*/ From 24a35126bfcf8b462cc167307c274106e7128a3f Mon Sep 17 00:00:00 2001 From: Jeff Ciesielski Date: Fri, 19 Oct 2012 23:05:37 -0700 Subject: [PATCH 09/22] stm32/f1/can: Replace mistaken logical operators with bitwise operators. also: remove unnecessary parenthesis --- lib/stm32/f1/can.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/stm32/f1/can.c b/lib/stm32/f1/can.c index 46d0e652..9bd23cb3 100644 --- a/lib/stm32/f1/can.c +++ b/lib/stm32/f1/can.c @@ -87,14 +87,14 @@ int can_init(u32 canport, bool ttcm, bool abom, bool awum, bool nart, CAN_MCR(canport) &= ~CAN_MCR_TXFP; if (silent) - CAN_BTR(canport) |= (CAN_BTR_SILM); + CAN_BTR(canport) |= CAN_BTR_SILM; else - CAN_BTR(canport) &= !(CAN_BTR_SILM); + CAN_BTR(canport) &= ~CAN_BTR_SILM; if (loopback) - CAN_BTR(canport) |= (CAN_BTR_LBKM); + CAN_BTR(canport) |= CAN_BTR_LBKM; else - CAN_BTR(canport) &= !(CAN_BTR_LBKM); + CAN_BTR(canport) &= ~CAN_BTR_LBKM; /* Set bit timings. */ From ffe392c11bdc7c45f61c652ecabd79fecbe1938a Mon Sep 17 00:00:00 2001 From: Jeff Ciesielski Date: Fri, 19 Oct 2012 23:16:08 -0700 Subject: [PATCH 10/22] stm32/f1/i2c: remove unnecessary parenthesis --- lib/stm32/i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stm32/i2c.c b/lib/stm32/i2c.c index e1e62533..e6869f6e 100644 --- a/lib/stm32/i2c.c +++ b/lib/stm32/i2c.c @@ -288,7 +288,7 @@ void i2c_send_data(u32 i2c, u8 data) */ uint8_t i2c_get_data(u32 i2c) { - return (I2C_DR(i2c) & 0xff); + return I2C_DR(i2c) & 0xff; } /*-----------------------------------------------------------------------------*/ From dd5e797d6187a48ed944a5f34630f200e9fc8649 Mon Sep 17 00:00:00 2001 From: Jeff Ciesielski Date: Mon, 22 Oct 2012 13:26:58 -0700 Subject: [PATCH 11/22] stm32/f1/can: Add function to detect if can system has an available mailbox --- lib/stm32/f1/can.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/stm32/f1/can.c b/lib/stm32/f1/can.c index 9bd23cb3..2131fd1e 100644 --- a/lib/stm32/f1/can.c +++ b/lib/stm32/f1/can.c @@ -313,3 +313,16 @@ void can_receive(u32 canport, u8 fifo, bool release, u32 *id, bool *ext, if (release) can_fifo_release(CAN1, 0); } + +bool can_available_mailbox(u32 canport) +{ + if ((CAN_TSR(canport) & CAN_TSR_TME0) == CAN_TSR_TME0) { + return true; + } else if ((CAN_TSR(canport) & CAN_TSR_TME1) == CAN_TSR_TME1) { + return true; + } else if ((CAN_TSR(canport) & CAN_TSR_TME2) == CAN_TSR_TME2) { + return true; + } else { + return false; + } +} From 94abf6e529a6ca3c9ed663113dedeef34e297661 Mon Sep 17 00:00:00 2001 From: Jeff Ciesielski Date: Mon, 22 Oct 2012 14:45:44 -0700 Subject: [PATCH 12/22] stm32/f1: Add user_reset_hook to reset_handler w/ jump_to_dfu preset This allows for pragramatic jumping to the DFU bootloader on CL devices, or could be used for things like CRC checking of the firmware before boot --- lib/stm32/f1/vector.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/stm32/f1/vector.c b/lib/stm32/f1/vector.c index d660774b..87fd6559 100644 --- a/lib/stm32/f1/vector.c +++ b/lib/stm32/f1/vector.c @@ -18,7 +18,8 @@ */ #define WEAK __attribute__ ((weak)) - +#define NAKED __attribute__((naked)) +#include /* Symbols exported by the linker script(s): */ extern unsigned _data_loadaddr, _data, _edata, _ebss, _stack; @@ -191,10 +192,10 @@ void (*const vector_table[]) (void) = { otg_fs_isr, /* Addr: 0x0000_014C */ }; -#include -void reset_handler(void) +void WEAK user_reset_hook(void); + +void handle_dfu_bootloader(void) { - volatile unsigned *src, *dest; uint32_t reset_str = *((uint32_t *)0x2000FFF0); if (reset_str == 0xDEADBEEF) { @@ -204,8 +205,17 @@ void reset_handler(void) asm("ldr r0, [r0, #4]"); asm("bx r0"); } + +} + +void NAKED reset_handler(void) +{ + volatile unsigned *src, *dest; + __asm__("MSR msp, %0" : : "r"(&_stack)); + user_reset_hook(); + for (src = &_data_loadaddr, dest = &_data; dest < &_edata; src++, dest++) *dest = *src; @@ -226,6 +236,7 @@ void null_handler(void) /* Do nothing. */ } +#pragma weak user_reset_hook = handle_dfu_bootloader #pragma weak nmi_handler = null_handler #pragma weak hard_fault_handler = blocking_handler #pragma weak mem_manage_handler = blocking_handler From f80bf1ccb107e2ad54d7d8ac586e9ffd09e8730e Mon Sep 17 00:00:00 2001 From: Jeff Ciesielski Date: Mon, 22 Oct 2012 15:48:22 -0700 Subject: [PATCH 13/22] stm32/i2c: Add DOXYGEN strings for i2c helper functions. Also: Define i2c_dutycycle group in i2c header --- include/libopencm3/stm32/i2c.h | 6 ++++ lib/stm32/i2c.c | 51 ++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/include/libopencm3/stm32/i2c.h b/include/libopencm3/stm32/i2c.h index 05a4d168..1b2dc0ee 100644 --- a/include/libopencm3/stm32/i2c.h +++ b/include/libopencm3/stm32/i2c.h @@ -321,9 +321,15 @@ LGPL License Terms @ref lgpl_license #define I2C_CCR_FS (1 << 15) /* DUTY: Fast Mode Duty Cycle */ +/** @defgroup i2c_duty_cycle I2C peripheral clock duty cycles +@ingroup i2c_defines + +@{*/ #define I2C_CCR_DUTY (1 << 14) #define I2C_CCR_DUTY_DIV2 0 #define I2C_CCR_DUTY_16_DIV_9 1 +/**@}*/ + /* Note: Bits [13:12] are reserved, and forced to 0 by hardware. */ /* diff --git a/lib/stm32/i2c.c b/lib/stm32/i2c.c index e6869f6e..a67beced 100644 --- a/lib/stm32/i2c.c +++ b/lib/stm32/i2c.c @@ -313,26 +313,57 @@ void i2c_disable_interrupt(u32 i2c, u32 interrupt) I2C_CR2(i2c) &= ~interrupt; } +/*-----------------------------------------------------------------------------*/ +/** @brief I2C Enable ACK + +Enables acking of own 7/10 bit address +@param[in] i2c Unsigned int32. I2C register base address @ref i2c_reg_base. +*/ void i2c_enable_ack(u32 i2c) { I2C_CR1(i2c) |= I2C_CR1_ACK; } +/*-----------------------------------------------------------------------------*/ +/** @brief I2C Disable ACK + +Disables acking of own 7/10 bit address +@param[in] i2c Unsigned int32. I2C register base address @ref i2c_reg_base. +*/ void i2c_disable_ack(u32 i2c) { I2C_CR1(i2c) &= ~I2C_CR1_ACK; } +/*-----------------------------------------------------------------------------*/ +/** @brief I2C NACK Next Byte + +Causes the I2C controller to NACK the reception of the next byte +@param[in] i2c Unsigned int32. I2C register base address @ref i2c_reg_base. +*/ void i2c_nack_next(u32 i2c) { I2C_CR1(i2c) |= I2C_CR1_POS; } +/*-----------------------------------------------------------------------------*/ +/** @brief I2C NACK Next Byte + +Causes the I2C controller to NACK the reception of the current byte + +@param[in] i2c Unsigned int32. I2C register base address @ref i2c_reg_base. +*/ void i2c_nack_current(u32 i2c) { I2C_CR1(i2c) &= ~I2C_CR1_POS; } +/*-----------------------------------------------------------------------------*/ +/** @brief I2C Set clock duty cycle + +@param[in] i2c Unsigned int32. I2C register base address @ref i2c_reg_base. +@param[in] dutycycle Unsigned int32. I2C duty cycle @ref i2c_duty_cycle. +*/ void i2c_set_dutycycle(u32 i2c, u32 dutycycle) { if (dutycycle == I2C_CCR_DUTY_DIV2) @@ -341,21 +372,41 @@ void i2c_set_dutycycle(u32 i2c, u32 dutycycle) I2C_CCR(i2c) |= I2C_CCR_DUTY; } +/*-----------------------------------------------------------------------------*/ +/** @brief I2C Enable DMA + +@param[in] i2c Unsigned int32. I2C register base address @ref i2c_reg_base. +*/ void i2c_enable_dma(u32 i2c) { I2C_CR2(i2c) |= I2C_CR2_DMAEN; } +/*-----------------------------------------------------------------------------*/ +/** @brief I2C Disable DMA + +@param[in] i2c Unsigned int32. I2C register base address @ref i2c_reg_base. +*/ void i2c_disable_dma(u32 i2c) { I2C_CR2(i2c) &= ~I2C_CR2_DMAEN; } +/*-----------------------------------------------------------------------------*/ +/** @brief I2C Set DMA last transfer + +@param[in] i2c Unsigned int32. I2C register base address @ref i2c_reg_base. +*/ void i2c_set_dma_last_transfer(u32 i2c) { I2C_CR2(i2c) |= I2C_CR2_LAST; } +/*-----------------------------------------------------------------------------*/ +/** @brief I2C Clear DMA last transfer + +@param[in] i2c Unsigned int32. I2C register base address @ref i2c_reg_base. +*/ void i2c_clear_dma_last_transfer(u32 i2c) { I2C_CR2(i2c) &= ~I2C_CR2_LAST; From 7e9b79aa55875b636e690ddf6f9ec2ac7aebf786 Mon Sep 17 00:00:00 2001 From: Jeff Ciesielski Date: Wed, 31 Oct 2012 17:01:56 -0700 Subject: [PATCH 14/22] stm32/can: add 'shift' defines for can timing bits Useful whe you want to return min/max values of timing parameters --- include/libopencm3/stm32/can.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/libopencm3/stm32/can.h b/include/libopencm3/stm32/can.h index f787df76..1919da45 100644 --- a/include/libopencm3/stm32/can.h +++ b/include/libopencm3/stm32/can.h @@ -417,6 +417,7 @@ #define CAN_BTR_SJW_3TQ (0x2 << 24) #define CAN_BTR_SJW_4TQ (0x3 << 24) #define CAN_BTR_SJW_MASK (0x3 << 24) +#define CAN_BTR_SJW_SHIFT 24 /* 23 Reserved, forced by hardware to 0 */ @@ -430,6 +431,7 @@ #define CAN_BTR_TS2_7TQ (0x6 << 20) #define CAN_BTR_TS2_8TQ (0x7 << 20) #define CAN_BTR_TS2_MASK (0x7 << 20) +#define CAN_BTR_TS2_SHIFT 20 /* TS1[3:0]: Time segment 1 */ #define CAN_BTR_TS1_1TQ (0x0 << 16) @@ -449,6 +451,7 @@ #define CAN_BTR_TS1_15TQ (0xE << 16) #define CAN_BTR_TS1_16TQ (0xF << 16) #define CAN_BTR_TS1_MASK (0xF << 16) +#define CAN_BTR_TS1_SHIFT 16 /* 15:10 Reserved, forced by hardware to 0 */ From fa1d5f8e43296b8dcc8522ca4d1a1a8d1313c0ef Mon Sep 17 00:00:00 2001 From: Jeff Ciesielski Date: Wed, 31 Oct 2012 17:03:26 -0700 Subject: [PATCH 15/22] stm32/f1/can: clean up can_available_mailbox ->fewer 'if' statements --- lib/stm32/f1/can.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/lib/stm32/f1/can.c b/lib/stm32/f1/can.c index 2131fd1e..96c3a2df 100644 --- a/lib/stm32/f1/can.c +++ b/lib/stm32/f1/can.c @@ -316,13 +316,5 @@ void can_receive(u32 canport, u8 fifo, bool release, u32 *id, bool *ext, bool can_available_mailbox(u32 canport) { - if ((CAN_TSR(canport) & CAN_TSR_TME0) == CAN_TSR_TME0) { - return true; - } else if ((CAN_TSR(canport) & CAN_TSR_TME1) == CAN_TSR_TME1) { - return true; - } else if ((CAN_TSR(canport) & CAN_TSR_TME2) == CAN_TSR_TME2) { - return true; - } else { - return false; - } + return CAN_TSR(canport) & (CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2); } From 6c41c808c35c73a52f72032e2869b4201c746ff2 Mon Sep 17 00:00:00 2001 From: Jeff Ciesielski Date: Thu, 1 Nov 2012 18:02:11 -0700 Subject: [PATCH 16/22] stm32/f1/can: Add clearing of can timing bits on init This corrects a bug introduced with the addition of allowing loopback and silent parameters in the init function. --- lib/stm32/f1/can.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/stm32/f1/can.c b/lib/stm32/f1/can.c index 96c3a2df..f04fd26c 100644 --- a/lib/stm32/f1/can.c +++ b/lib/stm32/f1/can.c @@ -55,6 +55,9 @@ int can_init(u32 canport, bool ttcm, bool abom, bool awum, bool nart, if ((CAN_MSR(canport) & CAN_MSR_INAK) != CAN_MSR_INAK) return 1; + /* clear can timing bits */ + CAN_BTR(canport) = 0; + /* Set the automatic bus-off management. */ if (ttcm) CAN_MCR(canport) |= CAN_MCR_TTCM; From e5cf92b9aee4ce70f000961309d4fb9a9222a7b5 Mon Sep 17 00:00:00 2001 From: Jeff Ciesielski Date: Fri, 2 Nov 2012 10:52:25 -0700 Subject: [PATCH 17/22] stm32/f1/rcc: expose method for selecting MCO source --- include/libopencm3/stm32/f1/rcc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/libopencm3/stm32/f1/rcc.h b/include/libopencm3/stm32/f1/rcc.h index 52b3469c..868ad9ce 100644 --- a/include/libopencm3/stm32/f1/rcc.h +++ b/include/libopencm3/stm32/f1/rcc.h @@ -507,6 +507,7 @@ void rcc_osc_on(osc_t osc); void rcc_osc_off(osc_t osc); void rcc_css_enable(void); void rcc_css_disable(void); +void rcc_set_mco(u32 mcosrc); void rcc_osc_bypass_enable(osc_t osc); void rcc_osc_bypass_disable(osc_t osc); void rcc_peripheral_enable_clock(volatile u32 *reg, u32 en); From 74405de4a5a1beab4023731de41c9f6e1c7f69a4 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 6 Nov 2012 11:03:12 -0800 Subject: [PATCH 18/22] Enable OTG clock during initialization Add a call to enable USB clock, otherwise the dive will not enumerate. --- examples/stm32/f1/other/usb_dfu/usbdfu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/stm32/f1/other/usb_dfu/usbdfu.c b/examples/stm32/f1/other/usb_dfu/usbdfu.c index 0211a47e..f30783f3 100644 --- a/examples/stm32/f1/other/usb_dfu/usbdfu.c +++ b/examples/stm32/f1/other/usb_dfu/usbdfu.c @@ -244,6 +244,8 @@ int main(void) AFIO_MAPR |= AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON; gpio_set_mode(GPIOA, GPIO_MODE_INPUT, 0, GPIO15); + rcc_peripheral_enable_clock(&RCC_AHBENR, RCC_AHBENR_OTGFSEN); + usbd_init(&stm32f107_usb_driver, &dev, &config, usb_strings); usbd_set_control_buffer_size(sizeof(usbd_control_buffer)); usbd_register_control_callback( From 7a5da60e2669c57d7b615aabe16ab851606f8bf1 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 6 Nov 2012 16:46:55 -0800 Subject: [PATCH 19/22] Change USB strings handling code This commit add an extra field to the _usbd_device, that allows to keep track of the number of USB strings which allows simplify boundaries checking code in usb_standard_get_descriptor. This commit also changes the index base for strings in usb_standard_get_descriptor which allows to get rid of necessity to have a dummy one-character string in a strings array. --- include/libopencm3/usb/usbd.h | 2 +- lib/usb/usb.c | 4 +++- lib/usb/usb_private.h | 1 + lib/usb/usb_standard.c | 22 +++++++++++----------- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/include/libopencm3/usb/usbd.h b/include/libopencm3/usb/usbd.h index e4b35785..9da83796 100644 --- a/include/libopencm3/usb/usbd.h +++ b/include/libopencm3/usb/usbd.h @@ -38,7 +38,7 @@ extern u8 usbd_control_buffer[]; extern int usbd_init(const usbd_driver *driver, const struct usb_device_descriptor *dev, const struct usb_config_descriptor *conf, - const char **strings); + const char **strings, int num_strings); extern void usbd_set_control_buffer_size(u16 size); extern void usbd_register_reset_callback(void (*callback)(void)); diff --git a/lib/usb/usb.c b/lib/usb/usb.c index 1ebb6ec5..d5ec9800 100644 --- a/lib/usb/usb.c +++ b/lib/usb/usb.c @@ -45,12 +45,14 @@ u8 usbd_control_buffer[128] __attribute__((weak)); */ int usbd_init(const usbd_driver *driver, const struct usb_device_descriptor *dev, - const struct usb_config_descriptor *conf, const char **strings) + const struct usb_config_descriptor *conf, + const char **strings, int num_strings) { _usbd_device.driver = driver; _usbd_device.desc = dev; _usbd_device.config = conf; _usbd_device.strings = strings; + _usbd_device.num_strings = num_strings; _usbd_device.ctrl_buf = usbd_control_buffer; _usbd_device.ctrl_buf_len = sizeof(usbd_control_buffer); diff --git a/lib/usb/usb_private.h b/lib/usb/usb_private.h index a1e5e4c8..238f14f3 100644 --- a/lib/usb/usb_private.h +++ b/lib/usb/usb_private.h @@ -29,6 +29,7 @@ extern struct _usbd_device { const struct usb_device_descriptor *desc; const struct usb_config_descriptor *config; const char **strings; + int num_strings; u8 *ctrl_buf; /**< Internal buffer used for control transfers */ u16 ctrl_buf_len; diff --git a/lib/usb/usb_standard.c b/lib/usb/usb_standard.c index 2d7c619e..1c8b952e 100644 --- a/lib/usb/usb_standard.c +++ b/lib/usb/usb_standard.c @@ -90,7 +90,7 @@ static u16 build_config_descriptor(u8 index, u8 *buf, u16 len) static int usb_standard_get_descriptor(struct usb_setup_data *req, u8 **buf, u16 *len) { - int i; + int i, index; struct usb_string_descriptor *sd; switch (req->wValue >> 8) { @@ -105,16 +105,20 @@ static int usb_standard_get_descriptor(struct usb_setup_data *req, case USB_DT_STRING: sd = (struct usb_string_descriptor *)_usbd_device.ctrl_buf; + /* Send sane Language ID descriptor... */ + if ((req->wValue & 0xff) == 0) + sd->wData[0] = 0x409; + + index = (req->wValue & 0xff) - 1; + if (!_usbd_device.strings) return 0; /* Device doesn't support strings. */ /* Check that string index is in range. */ - for (i = 0; i <= (req->wValue & 0xff); i++) - if (_usbd_device.strings[i] == NULL) - return 0; + if (index >= _usbd_device.num_strings) + return 0; - sd->bLength = strlen(_usbd_device.strings[req->wValue & 0xff]) - * 2 + 2; + sd->bLength = strlen(_usbd_device.strings[index]) * 2 + 2; sd->bDescriptorType = USB_DT_STRING; *buf = (u8 *)sd; @@ -122,11 +126,7 @@ static int usb_standard_get_descriptor(struct usb_setup_data *req, for (i = 0; i < (*len / 2) - 1; i++) sd->wData[i] = - _usbd_device.strings[req->wValue & 0xff][i]; - - /* Send sane Language ID descriptor... */ - if ((req->wValue & 0xff) == 0) - sd->wData[0] = 0x409; + _usbd_device.strings[index][i]; return 1; } From 12e178686331fd0a8e3564a9f0e77fece4a04617 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 6 Nov 2012 16:48:40 -0800 Subject: [PATCH 20/22] Add a desig_get_unique_id_as_string This commit adds desig_get_unique_id_as_string which is useful if one wants to use device ID as USB serial number(iSerialNumber), for example. --- include/libopencm3/stm32/f1/desig.h | 9 +++++++++ lib/stm32/f1/desig.c | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/include/libopencm3/stm32/f1/desig.h b/include/libopencm3/stm32/f1/desig.h index 74cfb353..6ceb6658 100644 --- a/include/libopencm3/stm32/f1/desig.h +++ b/include/libopencm3/stm32/f1/desig.h @@ -51,6 +51,15 @@ u16 desig_get_flash_size(void); */ void desig_get_unique_id(u32 result[]); +/** + * Read the full 96 bit unique identifier and return it as a + * zero-terminated string + * @param string memory region to write the result to + 8 @param string_len the size of string in bytes + */ +void desig_get_unique_id_as_string(char *string, + unsigned int string_len); + END_DECLS #endif diff --git a/lib/stm32/f1/desig.c b/lib/stm32/f1/desig.c index 7ae968e1..7f213fab 100644 --- a/lib/stm32/f1/desig.c +++ b/lib/stm32/f1/desig.c @@ -35,3 +35,25 @@ void desig_get_unique_id(u32 result[]) result[1] = bits63_32; result[2] = bits31_16 << 16 | bits15_0; } + +void desig_get_unique_id_as_string(char *string, + unsigned int string_len) +{ + int i, len; + u8 device_id[12]; + static const char chars[] = "0123456789ABCDEF"; + + desig_get_unique_id((u32 *)device_id); + + /* Each byte produces two characters */ + len = (2 * sizeof(device_id) < string_len) ? + 2 * sizeof(device_id) : string_len - 1; + + for (i = 0; i < len; i += 2) { + string[i] = chars[(device_id[i / 2] >> 0) & 0x0F]; + string[i + 1] = chars[(device_id[i / 2] >> 4) & 0x0F]; + } + + string[len] = '\0'; +} + From c5c4db01969608ac19bba290b9f6ec965ef01e5c Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 7 Nov 2012 10:33:51 -0800 Subject: [PATCH 21/22] Extend control hook framework This commits adds a new error code that can be return from a registered control callback: USBD_REQ_NEXT_CALLBACK. This return code signifies that the callback is done processing the data successfully, but user would like to have all matching callbacks down the callback chain to be executed too. This change allows for example to intercept standard requests like GET_DESCRIPTOR, do some small action upon receiving of one, but still have the standard callback executed and do it's job. This way user doesn't have to re-implement standard GET_DESCRIPTOR functionality if they want to intercept that request to do some small thing. --- include/libopencm3/usb/usbd.h | 7 +++++++ lib/usb/usb_control.c | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/include/libopencm3/usb/usbd.h b/include/libopencm3/usb/usbd.h index 9da83796..8f685557 100644 --- a/include/libopencm3/usb/usbd.h +++ b/include/libopencm3/usb/usbd.h @@ -24,6 +24,13 @@ BEGIN_DECLS + +enum usbd_request_return_codes { + USBD_REQ_NOTSUPP = 0, + USBD_REQ_HANDLED = 1, + USBD_REQ_NEXT_CALLBACK = 2, +}; + typedef struct _usbd_driver usbd_driver; extern const usbd_driver stm32f103_usb_driver; extern const usbd_driver stm32f107_usb_driver; diff --git a/lib/usb/usb_control.c b/lib/usb/usb_control.c index 3dd08578..b4ac57e9 100644 --- a/lib/usb/usb_control.c +++ b/lib/usb/usb_control.c @@ -102,7 +102,8 @@ static int usb_control_request_dispatch(struct usb_setup_data *req) result = cb[i].cb(req, &control_state.ctrl_buf, &control_state.ctrl_len, &control_state.complete); - if (result) + if (result == USBD_REQ_HANDLED || + result == USBD_REQ_NOTSUPP) return result; } } From 720e85f850064ad6ab3e79101b972e43d4e31ef6 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 7 Nov 2012 11:30:44 -0800 Subject: [PATCH 22/22] Further re-factor USB string handling code This commit refactors USB string code, making it, hopefully, less buggy and more easier to understand. It also removes "magic" bit manipulation and "magic" numbers; --- include/libopencm3/usb/usbstd.h | 3 ++ lib/usb/usb_standard.c | 70 ++++++++++++++++++++++----------- 2 files changed, 50 insertions(+), 23 deletions(-) diff --git a/include/libopencm3/usb/usbstd.h b/include/libopencm3/usb/usbstd.h index 8610fdbc..01fc7e3c 100644 --- a/include/libopencm3/usb/usbstd.h +++ b/include/libopencm3/usb/usbstd.h @@ -220,4 +220,7 @@ struct usb_iface_assoc_descriptor { #define USB_DT_INTERFACE_ASSOCIATION_SIZE \ sizeof(struct usb_iface_assoc_descriptor) +enum usb_language_id { + USB_LANGID_ENGLISH_US = 0x409, +}; #endif diff --git a/lib/usb/usb_standard.c b/lib/usb/usb_standard.c index 1c8b952e..08923d85 100644 --- a/lib/usb/usb_standard.c +++ b/lib/usb/usb_standard.c @@ -87,50 +87,74 @@ static u16 build_config_descriptor(u8 index, u8 *buf, u16 len) return total; } +static int usb_descriptor_type(u16 wValue) +{ + return wValue >> 8; +} + +static int usb_descriptor_index(u16 wValue) +{ + return wValue & 0xFF; +} + static int usb_standard_get_descriptor(struct usb_setup_data *req, u8 **buf, u16 *len) { - int i, index; + int i, array_idx, descr_idx; struct usb_string_descriptor *sd; - switch (req->wValue >> 8) { + descr_idx = usb_descriptor_index(req->wValue); + + switch (usb_descriptor_type(req->wValue)) { case USB_DT_DEVICE: *buf = (u8 *) _usbd_device.desc; *len = MIN(*len, _usbd_device.desc->bLength); - return 1; + return USBD_REQ_HANDLED; case USB_DT_CONFIGURATION: *buf = _usbd_device.ctrl_buf; - *len = build_config_descriptor(req->wValue & 0xff, *buf, *len); - return 1; + *len = build_config_descriptor(descr_idx, *buf, *len); + return USBD_REQ_HANDLED; case USB_DT_STRING: sd = (struct usb_string_descriptor *)_usbd_device.ctrl_buf; - /* Send sane Language ID descriptor... */ - if ((req->wValue & 0xff) == 0) - sd->wData[0] = 0x409; + if (descr_idx == 0) { + /* Send sane Language ID descriptor... */ + sd->wData[0] = USB_LANGID_ENGLISH_US; + sd->bLength = sizeof(sd->bLength) + sizeof(sd->bDescriptorType) + + sizeof(sd->wData[0]); - index = (req->wValue & 0xff) - 1; + *len = MIN(*len, sd->bLength); + } else { + array_idx = descr_idx - 1; - if (!_usbd_device.strings) - return 0; /* Device doesn't support strings. */ + if (!_usbd_device.strings) + return USBD_REQ_NOTSUPP; /* Device doesn't support strings. */ + /* Check that string index is in range. */ + if (array_idx >= _usbd_device.num_strings) + return USBD_REQ_NOTSUPP; - /* Check that string index is in range. */ - if (index >= _usbd_device.num_strings) - return 0; + /* Strings with Language ID differnet from + * USB_LANGID_ENGLISH_US are not supported */ + if (req->wIndex != USB_LANGID_ENGLISH_US) + return USBD_REQ_NOTSUPP; + + /* Ths string is returned as UTF16, hence the multiplication */ + sd->bLength = strlen(_usbd_device.strings[array_idx]) * 2 + + sizeof(sd->bLength) + sizeof(sd->bDescriptorType); + + *len = MIN(*len, sd->bLength); + + for (i = 0; i < (*len / 2) - 1; i++) + sd->wData[i] = + _usbd_device.strings[array_idx][i]; + } - sd->bLength = strlen(_usbd_device.strings[index]) * 2 + 2; sd->bDescriptorType = USB_DT_STRING; - *buf = (u8 *)sd; - *len = MIN(*len, sd->bLength); - for (i = 0; i < (*len / 2) - 1; i++) - sd->wData[i] = - _usbd_device.strings[index][i]; - - return 1; + return USBD_REQ_HANDLED; } - return 0; + return USBD_REQ_NOTSUPP; } static int usb_standard_set_address(struct usb_setup_data *req, u8 **buf,