2020-06-06 14:35:55 +02:00
/** global definitions and methods
2020-02-11 12:15:31 +01:00
* @ file
2016-10-23 17:42:27 +02:00
* @ author King Kévin < kingkevin @ cuvoodoo . info >
2020-06-06 14:35:55 +02:00
* @ copyright SPDX - License - Identifier : GPL - 3.0 - or - later
2020-02-11 12:15:31 +01:00
* @ date 2016 - 2020
2016-10-23 17:42:27 +02:00
*/
/* standard libraries */
# include <stdint.h> // standard integer types
# include <stdlib.h> // general utilities
2017-10-09 17:58:19 +02:00
# include <string.h> // memory utilities
2016-10-23 17:42:27 +02:00
/* STM32 (including CM3) libraries */
2017-08-02 13:44:16 +02:00
# include <libopencmsis/core_cm3.h> // Cortex M3 utilities
# include <libopencm3/cm3/nvic.h> // interrupt handler
# include <libopencm3/cm3/systick.h> // SysTick library
2016-10-23 17:42:27 +02:00
# include <libopencm3/stm32/rcc.h> // real-time control clock library
# include <libopencm3/stm32/gpio.h> // general purpose input output library
# include <libopencm3/stm32/exti.h> // external interrupt defines
2020-11-27 16:37:52 +01:00
# include <libopencm3/stm32/syscfg.h> // system definitions
# include <libopencm3/usb/dwc/otg_fs.h> // USB OTG utilities
2016-10-23 17:42:27 +02:00
# include "global.h" // common methods
volatile bool button_flag = false ;
2018-02-18 15:20:47 +01:00
volatile bool user_input_available = false ;
static volatile uint8_t user_input_buffer [ 64 ] = { 0 } ; /**< ring buffer for received data */
static volatile uint8_t user_input_i = 0 ; /**< current position of read received data */
static volatile uint8_t user_input_used = 0 ; /**< how much data has been received and not red */
2016-10-23 17:42:27 +02:00
2018-04-03 16:59:02 +02:00
static volatile uint32_t sleep_duration = 0 ; /**< sleep duration count down (in SysTick interrupts) */
2020-12-07 19:10:04 +01:00
uint8_t addu8_safe ( uint8_t a , uint8_t b )
{
if ( a > UINT8_MAX - b ) {
return UINT8_MAX ;
} else {
return a + b ;
}
}
uint16_t addu16_safe ( uint16_t a , uint16_t b )
{
if ( a > UINT16_MAX - b ) {
return UINT16_MAX ;
} else {
return a + b ;
}
}
uint32_t addu32_safe ( uint32_t a , uint32_t b )
{
if ( a > UINT32_MAX - b ) {
return UINT32_MAX ;
} else {
return a + b ;
}
}
int8_t adds8_safe ( int8_t a , int8_t b )
{
if ( b > 0 ) {
if ( a > INT8_MAX - b ) {
return INT8_MAX ;
} else {
return a + b ;
}
} else {
if ( a < INT8_MIN + b ) {
return INT8_MIN ;
} else {
return a + b ;
}
}
}
int16_t adds16_safe ( int16_t a , int16_t b )
{
if ( b > 0 ) {
if ( a > INT16_MAX - b ) {
return INT16_MAX ;
} else {
return a + b ;
}
} else {
if ( a < INT16_MIN + b ) {
return INT16_MIN ;
} else {
return a + b ;
}
}
}
int32_t adds32_safe ( int32_t a , int32_t b )
{
if ( b > 0 ) {
if ( a > INT32_MAX - b ) {
return INT32_MAX ;
} else {
return a + b ;
}
} else {
if ( a < INT32_MIN + b ) {
return INT32_MIN ;
} else {
return a + b ;
}
}
}
2016-10-23 17:42:27 +02:00
char * b2s ( uint64_t binary , uint8_t rjust )
{
2019-04-02 19:51:18 +02:00
static char string [ 64 + 1 ] = { 0 } ; // the string representation to return
uint8_t bit = LENGTH ( string ) - 1 ; // the index of the bit to print
2016-10-23 17:42:27 +02:00
string [ bit - - ] = ' \0 ' ; // terminate string
while ( binary ) {
if ( binary & 1 ) {
string [ bit - - ] = ' 1 ' ;
} else {
string [ bit - - ] = ' 0 ' ;
}
binary > > = 1 ;
}
2019-04-02 19:51:18 +02:00
while ( 64 - bit - 1 < rjust & & bit > 0 ) {
2016-10-23 17:42:27 +02:00
string [ bit - - ] = ' 0 ' ;
}
return string ;
}
/** switch on board LED */
2019-12-21 14:13:28 +01:00
inline void led_on ( void )
2016-10-23 17:42:27 +02:00
{
2019-12-21 14:13:28 +01:00
# if defined(LED_PIN)
2018-02-18 15:18:42 +01:00
# if defined(LED_ON) && LED_ON
2019-12-21 20:02:33 +01:00
gpio_set ( GPIO_PORT ( LED_PIN ) , GPIO_PIN ( LED_PIN ) ) ;
2018-02-18 15:18:42 +01:00
# else
2019-12-21 20:02:33 +01:00
gpio_clear ( GPIO_PORT ( LED_PIN ) , GPIO_PIN ( LED_PIN ) ) ;
2019-12-21 14:13:28 +01:00
# endif // LED_ON
# endif // LED_PIN
2016-10-23 17:42:27 +02:00
}
2018-02-18 15:18:42 +01:00
2016-10-23 17:42:27 +02:00
/** switch off board LED */
2019-12-21 14:13:28 +01:00
inline void led_off ( void )
2016-10-23 17:42:27 +02:00
{
2019-12-21 14:13:28 +01:00
# if defined(LED_PIN)
2018-02-18 15:18:42 +01:00
# if defined(LED_ON) && LED_ON
2019-12-21 20:02:33 +01:00
gpio_clear ( GPIO_PORT ( LED_PIN ) , GPIO_PIN ( LED_PIN ) ) ;
2018-02-18 15:18:42 +01:00
# else
2019-12-21 20:02:33 +01:00
gpio_set ( GPIO_PORT ( LED_PIN ) , GPIO_PIN ( LED_PIN ) ) ;
2019-12-21 14:13:28 +01:00
# endif // LED_ON
# endif // LED_PIN
2016-10-23 17:42:27 +02:00
}
2018-02-18 15:18:42 +01:00
2016-10-23 17:42:27 +02:00
/** toggle board LED */
2019-12-21 14:13:28 +01:00
inline void led_toggle ( void )
2016-10-23 17:42:27 +02:00
{
2019-12-21 14:13:28 +01:00
# if defined(LED_PIN)
2019-12-21 20:02:33 +01:00
gpio_toggle ( GPIO_PORT ( LED_PIN ) , GPIO_PIN ( LED_PIN ) ) ;
2019-12-21 14:13:28 +01:00
# endif // LED_PIN
2016-10-23 17:42:27 +02:00
}
2017-08-02 13:44:16 +02:00
void sleep_us ( uint32_t duration )
{
2020-12-10 23:52:48 +01:00
if ( duration < = 4 ) { // less than the setup time
for ( volatile uint32_t nop = 0 ; nop < 5 * duration ; nop + + ) ; // busy loop, approximate
2019-04-02 19:52:41 +02:00
return ;
}
2020-12-10 23:52:48 +01:00
duration - = 4 ; // subtract setup time
2017-08-02 13:44:16 +02:00
systick_counter_disable ( ) ; // disable SysTick to reconfigure it
2019-04-02 19:51:18 +02:00
if ( ! systick_set_frequency ( 1000000 , rcc_ahb_frequency ) ) { // set SysTick frequency to microseconds
2018-04-03 16:59:02 +02:00
while ( true ) ; // unhandled error
}
2017-08-02 13:44:16 +02:00
systick_clear ( ) ; // reset SysTick
systick_interrupt_enable ( ) ; // enable interrupt to count duration
sleep_duration = duration ; // save sleep duration for count down
systick_counter_enable ( ) ; // start counting
2019-04-02 19:51:18 +02:00
while ( sleep_duration > 0 ) { // wait for count down to complete
2019-03-26 19:27:40 +01:00
__WFI ( ) ; // go to sleep
2017-08-02 13:44:16 +02:00
}
}
void sleep_ms ( uint32_t duration )
{
2019-04-02 19:52:41 +02:00
if ( 0 = = duration ) {
return ;
}
2017-08-02 13:44:16 +02:00
systick_counter_disable ( ) ; // disable SysTick to reconfigure it
2019-04-02 19:51:18 +02:00
if ( ! systick_set_frequency ( 1000 , rcc_ahb_frequency ) ) { // set SysTick frequency to milliseconds
2018-04-03 16:59:02 +02:00
while ( true ) ; // unhandled error
}
2017-08-02 13:44:16 +02:00
systick_clear ( ) ; // reset SysTick
systick_interrupt_enable ( ) ; // enable interrupt to count duration
sleep_duration = duration ; // save sleep duration for count down
systick_counter_enable ( ) ; // start counting
2019-04-02 19:51:18 +02:00
while ( sleep_duration > 0 ) { // wait for count down to complete
2019-03-26 19:27:40 +01:00
__WFI ( ) ; // go to sleep
2017-08-02 13:44:16 +02:00
}
}
/** SysTick interrupt handler */
void sys_tick_handler ( void )
{
2019-04-02 19:51:18 +02:00
if ( sleep_duration > 0 ) {
2017-08-02 13:44:16 +02:00
sleep_duration - - ; // decrement duration
}
2019-04-02 19:51:18 +02:00
if ( 0 = = sleep_duration ) { // sleep complete
2017-08-02 13:44:16 +02:00
systick_counter_disable ( ) ; // stop systick
systick_interrupt_disable ( ) ; // stop interrupting
2018-04-03 16:59:02 +02:00
sleep_duration = 0 ; // ensure it still is at 0
2017-08-02 13:44:16 +02:00
}
}
2018-02-18 15:20:47 +01:00
char user_input_get ( void )
{
2020-02-11 12:15:31 +01:00
while ( 0 = = user_input_used ) { // wait for user input to be available (don't trust user_input_available since it user modifiable)
2018-02-18 15:20:47 +01:00
__WFI ( ) ; // go to sleep
}
volatile char to_return = user_input_buffer [ user_input_i ] ; // get the next available character
2019-04-02 19:51:18 +02:00
user_input_i = ( user_input_i + 1 ) % LENGTH ( user_input_buffer ) ; // update used buffer
2018-02-18 15:20:47 +01:00
user_input_used - - ; // update used buffer
2019-04-02 19:51:18 +02:00
user_input_available = ( user_input_used ! = 0 ) ; // update available data
2018-02-18 15:20:47 +01:00
return to_return ;
}
void user_input_store ( char c )
{
// only save data if there is space in the buffer
2019-04-02 19:51:18 +02:00
if ( user_input_used > = LENGTH ( user_input_buffer ) ) { // if buffer is full
user_input_i = ( user_input_i + 1 ) % LENGTH ( user_input_buffer ) ; // drop oldest data
2018-02-18 15:20:47 +01:00
user_input_used - - ; // update used buffer information
}
2019-04-02 19:51:18 +02:00
user_input_buffer [ ( user_input_i + user_input_used ) % LENGTH ( user_input_buffer ) ] = c ; // put character in buffer
2018-02-18 15:20:47 +01:00
user_input_used + + ; // update used buffer
user_input_available = true ; // update available data
}
2016-10-23 17:42:27 +02:00
void board_setup ( void )
{
2020-11-24 15:59:42 +01:00
// setup main clock
# if defined(MINIF401)
rcc_clock_setup_pll ( & rcc_hse_25mhz_3v3 [ RCC_CLOCK_3V3_84MHZ ] ) ; // the MINIF401 uses an STM32F401 which can go up to 84 MHz, and the board has a 25 MHz crystal
# else
rcc_clock_setup_pll ( & rcc_hsi_configs [ RCC_CLOCK_3V3_84MHZ ] ) ; // use HSI which is present on all boards, and limit to 84MHz (supported by all STM32F4
# endif
# if defined(LED_PIN) && defined(LED_ON)
2016-10-23 17:42:27 +02:00
// setup LED
2019-12-21 20:02:33 +01:00
rcc_periph_clock_enable ( GPIO_RCC ( LED_PIN ) ) ; // enable clock for LED
2020-11-24 15:59:42 +01:00
gpio_mode_setup ( GPIO_PORT ( LED_PIN ) , GPIO_MODE_OUTPUT , GPIO_PUPD_NONE , GPIO_PIN ( LED_PIN ) ) ; // set LED pin as output
# if LED_ON // LED is on when sourcing
gpio_set_output_options ( GPIO_PORT ( LED_PIN ) , GPIO_OTYPE_PP , GPIO_OSPEED_2MHZ , GPIO_PIN ( LED_PIN ) ) ; // set LED pin output as push-pull
# else // LED is on when sinking
gpio_set_output_options ( GPIO_PORT ( LED_PIN ) , GPIO_OTYPE_OD , GPIO_OSPEED_2MHZ , GPIO_PIN ( LED_PIN ) ) ; // set LED pin output as open-drain
2018-04-03 16:59:02 +02:00
# endif
2016-10-23 17:42:27 +02:00
led_off ( ) ; // switch off LED per default
2019-12-21 14:13:28 +01:00
# endif // LED_PIN
2016-10-23 17:42:27 +02:00
// setup button
2021-03-24 00:20:11 +01:00
/*
2020-11-24 15:59:42 +01:00
# if defined(BUTTON_PIN) && defined(BUTTON_PRESSED)
2019-12-21 20:02:33 +01:00
rcc_periph_clock_enable ( GPIO_RCC ( BUTTON_PIN ) ) ; // enable clock for button
exti_select_source ( GPIO_EXTI ( BUTTON_PIN ) , GPIO_PORT ( BUTTON_PIN ) ) ; // mask external interrupt of this pin only for this port
2020-11-24 15:59:42 +01:00
# if BUTTON_PRESSED // level goes high when pressed
gpio_mode_setup ( GPIO_PORT ( BUTTON_PIN ) , GPIO_MODE_INPUT , GPIO_PUPD_PULLDOWN , GPIO_PIN ( BUTTON_PIN ) ) ; // set GPIO to input and pull down
2019-12-21 20:02:33 +01:00
exti_set_trigger ( GPIO_EXTI ( BUTTON_PIN ) , EXTI_TRIGGER_RISING ) ; // trigger when button is pressed
2020-11-24 15:59:42 +01:00
# else // level goes low when pressed
gpio_mode_setup ( GPIO_PORT ( BUTTON_PIN ) , GPIO_MODE_INPUT , GPIO_PUPD_PULLUP , GPIO_PIN ( BUTTON_PIN ) ) ; // set GPIO to input and pull up
2019-12-21 20:02:33 +01:00
exti_set_trigger ( GPIO_EXTI ( BUTTON_PIN ) , EXTI_TRIGGER_FALLING ) ; // trigger when button is pressed
2017-04-03 13:03:29 +02:00
# endif
2019-12-21 20:02:33 +01:00
exti_enable_request ( GPIO_EXTI ( BUTTON_PIN ) ) ; // enable external interrupt
2020-02-09 18:36:54 +01:00
nvic_enable_irq ( GPIO_NVIC_EXTI_IRQ ( BUTTON_PIN ) ) ; // enable interrupt
2016-10-23 17:42:27 +02:00
# endif
2021-03-24 00:20:11 +01:00
*/
2018-02-18 15:20:47 +01:00
// reset user input buffer
user_input_available = false ;
user_input_i = 0 ;
user_input_used = 0 ;
2016-10-23 17:42:27 +02:00
}
2020-11-27 16:37:52 +01:00
/** disconnect USB by sending a reset condition */
static void usb_disconnect ( void )
{
if ( OTG_FS_GUSBCFG & OTG_GUSBCFG_FDMOD ) { // USB configured as device
// pull USB D+ low for a short while
OTG_FS_DCTL | = OTG_DCTL_SDIS ; // disconnect DP pull-up to simulate a disconnect
// in case there is an external pull-up resistor, pull DP low
// I have no idea why, but once USB is configured, I can't use PA12/DP back as GPIO
gpio_mode_setup ( GPIOA , GPIO_MODE_OUTPUT , GPIO_PUPD_NONE , GPIO12 ) ; // be sure the D+ pin can be used as GPIO output
gpio_set_output_options ( GPIOA , GPIO_OTYPE_PP , GPIO_OSPEED_2MHZ , GPIO12 ) ; // use push-pull output
gpio_clear ( GPIOA , GPIO12 ) ; // pull D+ low
for ( volatile uint32_t i = 0 ; i < 0x2000 ; i + + ) ; // USB disconnected must be at least 10 ms long, at most 100 ms
}
}
void system_memory ( void )
{
usb_disconnect ( ) ; // disconnect from USB (if necessary)
// for more details, see https://stm32f4-discovery.net/2017/04/tutorial-jump-system-memory-software-stm32/
// deinit RCC (according to STM32CubeF4 source code)
RCC_CR | = RCC_CR_HSION ; // enable high speed internal clock
while ( ! ( RCC_CR & RCC_CR_HSIRDY ) ) ; // wait until clock is ready
RCC_CR | = ( 0x10U < < RCC_CR_HSITRIM_SHIFT ) ; // set HSITRIM[4:0] bits to the reset value
RCC_CFGR = 0 ; // reset CFGR register
while ( ( ( RCC_CFGR > > RCC_CFGR_SWS_SHIFT ) & RCC_CFGR_SWS_MASK ) ! = RCC_CFGR_SWS_HSI ) ; // wait it till clock switch is ready
RCC_CR & = ~ ( RCC_CR_HSEON | RCC_CR_HSEBYP | RCC_CR_CSSON ) ; // clear HSEON, HSEBYP and CSSON bits
while ( RCC_CR & RCC_CR_HSERDY ) ; // wait till HSE is disabled
RCC_CR & = ~ RCC_CR_PLLON ; // Clear PLLON bit
while ( RCC_CR & RCC_CR_PLLRDY ) ; // wait till PLL is disabled
//RCC_PLLCFGR = 0x24003010; // reset PLLCFGR register to default value (value for STM32F401)
RCC_CIR & = ~ ( RCC_CIR_LSIRDYIE | RCC_CIR_LSERDYIE | RCC_CIR_HSIRDYIE | RCC_CIR_HSERDYIE | RCC_CIR_PLLRDYIE ) ; // disable all interrupts
RCC_CIR | = ( RCC_CIR_LSIRDYC | RCC_CIR_LSERDYC | RCC_CIR_HSIRDYC | RCC_CIR_HSERDYC | RCC_CIR_PLLRDYC | RCC_CIR_CSSC ) ; // clear all interrupt flags
RCC_CR & = ~ RCC_CSR_LSION ; // clear LSION bit
RCC_CSR | = RCC_CSR_RMVF ; // reset all CSR flags
// switch to system memory
RCC_APB2ENR = RCC_APB2ENR_SYSCFGEN ; // enable system configure clock (all others are not required)
cm_disable_interrupts ( ) ; // disable all interrupts
SYSCFG_MEMRM = 1 ; // map system memory to 0x0000 0000 (this bypasses the BOOT0 pin)
const uint32_t address = 0x1FFF0000 ; // system memory address
__asm__ volatile ( " MSR msp,%0 " : : " r " ( * ( uint32_t * ) address ) ) ; // set stack pointer to address provided in the beginning of the bootloader (loaded into a register first)
( * ( void ( * * ) ( void ) ) ( ( uint32_t ) address + 4 ) ) ( ) ; // start system memory (by jumping to the reset function which address is stored as second entry of the vector table)
// we should not reach this point
}
void dfu_bootloader ( void )
{
usb_disconnect ( ) ; // disconnect from USB (if necessary)
// set DFU magic to specific RAM location
__dfu_magic [ 0 ] = ' D ' ;
__dfu_magic [ 1 ] = ' F ' ;
__dfu_magic [ 2 ] = ' U ' ;
__dfu_magic [ 3 ] = ' ! ' ;
scb_reset_system ( ) ; // reset system (core and peripherals)
while ( true ) ; // wait for the reset to happen
}
2016-10-23 17:42:27 +02:00
# if defined(BUTTON_PIN)
/** interrupt service routine called when button is pressed */
2019-12-21 20:02:33 +01:00
void GPIO_EXTI_ISR ( BUTTON_PIN ) ( void )
2016-10-23 17:42:27 +02:00
{
2019-12-21 20:02:33 +01:00
exti_reset_request ( GPIO_EXTI ( BUTTON_PIN ) ) ; // reset interrupt
2016-10-23 17:42:27 +02:00
button_flag = true ; // perform button action
}
# endif