2016-10-23 17:42:27 +02:00
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*
*/
/** global definitions and methods (code)
* @ file global . c
* @ author King Kévin < kingkevin @ cuvoodoo . info >
2017-08-02 13:44:16 +02:00
* @ date 2016 - 2017
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/timer.h> // timer library
# include <libopencm3/stm32/exti.h> // external interrupt defines
# include "global.h" // common methods
volatile bool button_flag = false ;
2017-08-02 13:44:16 +02:00
volatile uint32_t sleep_duration = 0 ; /**< sleep duration count down (in SysTick interrupts) */
2018-01-24 22:18:11 +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
char * b2s ( uint64_t binary , uint8_t rjust )
{
static char string [ 64 + 1 ] = { 0 } ; // the string representation to return
uint8_t bit = LENGTH ( string ) - 1 ; // the index of the bit to print
string [ bit - - ] = ' \0 ' ; // terminate string
while ( binary ) {
if ( binary & 1 ) {
string [ bit - - ] = ' 1 ' ;
} else {
string [ bit - - ] = ' 0 ' ;
}
binary > > = 1 ;
}
while ( 64 - bit - 1 < rjust & & bit > 0 ) {
string [ bit - - ] = ' 0 ' ;
}
return string ;
}
/** switch on board LED */
void led_on ( void )
{
2017-12-12 13:55:38 +01:00
# if defined(BUSVOODOO)
timer_disable_counter ( TIM1 ) ; // disable timer for PWM
gpio_set_mode ( GPIO ( LED_PORT ) , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO ( LED_PIN ) ) ; // set LED pin to 'output push-pull'
# endif
2017-11-24 11:29:10 +01:00
# if defined(LED_ON) && LED_ON
2016-10-23 17:42:27 +02:00
gpio_set ( GPIO ( LED_PORT ) , GPIO ( LED_PIN ) ) ;
2017-11-24 11:29:10 +01:00
# else
gpio_clear ( GPIO ( LED_PORT ) , GPIO ( LED_PIN ) ) ;
2016-10-23 17:42:27 +02:00
# endif
}
2017-11-24 11:29:10 +01:00
2016-10-23 17:42:27 +02:00
/** switch off board LED */
void led_off ( void )
{
2017-12-12 13:55:38 +01:00
# if defined(BUSVOODOO)
timer_disable_counter ( TIM1 ) ; // disable timer for PWM
gpio_set_mode ( GPIO ( LED_PORT ) , GPIO_MODE_INPUT , GPIO_CNF_INPUT_FLOAT , GPIO ( LED_PIN ) ) ; // set LED pin to 'output push-pull'
# else
2017-11-24 11:29:10 +01:00
# if defined(LED_ON) && LED_ON
2016-10-23 17:42:27 +02:00
gpio_clear ( GPIO ( LED_PORT ) , GPIO ( LED_PIN ) ) ;
2017-11-24 11:29:10 +01:00
# else
gpio_set ( GPIO ( LED_PORT ) , GPIO ( LED_PIN ) ) ;
2016-10-23 17:42:27 +02:00
# endif
2017-12-12 13:55:38 +01:00
# endif
2016-10-23 17:42:27 +02:00
}
2017-11-24 11:29:10 +01:00
2016-10-23 17:42:27 +02:00
/** toggle board LED */
void led_toggle ( void )
{
2017-12-12 13:55:38 +01:00
# if defined(BUSVOODOO)
timer_disable_counter ( TIM1 ) ; // disable timer for PWM
gpio_set_mode ( GPIO ( LED_PORT ) , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO ( LED_PIN ) ) ; // set LED pin to 'output push-pull'
# endif
2016-10-23 17:42:27 +02:00
gpio_toggle ( GPIO ( LED_PORT ) , GPIO ( LED_PIN ) ) ;
}
2017-12-12 13:55:38 +01:00
# if defined(BUSVOODOO)
void led_blink ( double period , double duty )
{
if ( period < 0.0 | | period > 6.0 | | duty < 0.0 | | duty > 1.0 ) { // input argument out of bounds
return ; // do nothing
}
timer_disable_counter ( TIM1 ) ; // disable timer for PWM before resetting it
if ( 0.0 = = period ) { // no blinking
gpio_set_mode ( GPIO ( LED_PORT ) , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO ( LED_PIN ) ) ; // set LED pin as normal output
if ( duty > 0.5 ) { // LED should be on
gpio_set ( GPIO ( LED_PORT ) , GPIO ( LED_PIN ) ) ; // switch LED on
} else { // LED should be off
gpio_clear ( GPIO ( LED_PORT ) , GPIO ( LED_PIN ) ) ; // switch LED off
}
} else {
gpio_set_mode ( GPIO ( LED_PORT ) , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_ALTFN_PUSHPULL , GPIO ( LED_PIN ) ) ; // set LED pin to alternate function for PWM
timer_set_counter ( TIM1 , 0 ) ; // reset counter
timer_set_period ( TIM1 , 0xffff * ( period / 6.0 ) ) ; // set period
timer_set_oc_value ( TIM1 , TIM_OC1 , 0xffff * ( period / 6.0 ) * duty ) ; // PWM duty cycle
timer_enable_counter ( TIM1 ) ; // enable timer to start blinking
}
}
2018-02-13 17:31:52 +01:00
void led_blue ( void )
{
timer_disable_counter ( TIM1 ) ; // disable timer for PWM
gpio_set_mode ( GPIO ( LED_PORT ) , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO ( LED_PIN ) ) ; // set LED pin to 'output push-pull'
gpio_set ( GPIO ( LED_PORT ) , GPIO ( LED_PIN ) ) ;
}
void led_red ( void )
{
timer_disable_counter ( TIM1 ) ; // disable timer for PWM
gpio_set_mode ( GPIO ( LED_PORT ) , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO ( LED_PIN ) ) ; // set LED pin to 'output push-pull'
gpio_clear ( GPIO ( LED_PORT ) , GPIO ( LED_PIN ) ) ;
}
2017-12-12 13:55:38 +01:00
# endif
2017-08-02 13:44:16 +02:00
void sleep_us ( uint32_t duration )
{
systick_counter_disable ( ) ; // disable SysTick to reconfigure it
systick_clear ( ) ; // reset SysTick
systick_set_frequency ( 1000000 , rcc_ahb_frequency ) ; // set SysTick frequency to microseconds
systick_interrupt_enable ( ) ; // enable interrupt to count duration
sleep_duration = duration ; // save sleep duration for count down
systick_counter_enable ( ) ; // start counting
while ( sleep_duration ) { // wait for count down to complete
__WFI ( ) ; // go to sleep
}
}
void sleep_ms ( uint32_t duration )
{
systick_counter_disable ( ) ; // disable SysTick to reconfigure it
systick_clear ( ) ; // reset SysTick
systick_set_frequency ( 1000 , rcc_ahb_frequency ) ; // set SysTick frequency to milliseconds
systick_interrupt_enable ( ) ; // enable interrupt to count duration
sleep_duration = duration ; // save sleep duration for count down
systick_counter_enable ( ) ; // start counting
while ( sleep_duration ) { // wait for count down to complete
__WFI ( ) ; // go to sleep
}
}
/** SysTick interrupt handler */
void sys_tick_handler ( void )
{
if ( sleep_duration ) {
sleep_duration - - ; // decrement duration
}
if ( 0 = = sleep_duration ) { // sleep complete
systick_counter_disable ( ) ; // stop systick
systick_interrupt_disable ( ) ; // stop interrupting
}
}
2018-01-24 22:18:11 +01:00
char user_input_get ( void )
{
2018-01-24 22:25:12 +01:00
while ( ! user_input_available ) { // wait for user input
__WFI ( ) ; // go to sleep
2018-01-24 22:18:11 +01:00
}
volatile char to_return = user_input_buffer [ user_input_i ] ; // get the next available character
user_input_i = ( user_input_i + 1 ) % LENGTH ( user_input_buffer ) ; // update used buffer
user_input_used - - ; // update used buffer
user_input_available = ( user_input_used ! = 0 ) ; // update available data
return to_return ;
}
void user_input_store ( char c )
{
// only save data if there is space in the buffer
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
user_input_used - - ; // update used buffer information
}
user_input_buffer [ ( user_input_i + user_input_used ) % LENGTH ( user_input_buffer ) ] = c ; // put character in buffer
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 )
{
// setup LED
rcc_periph_clock_enable ( RCC_GPIO ( LED_PORT ) ) ; // enable clock for LED
2017-12-12 13:55:38 +01:00
# if defined(BUSVOODOO)
// LED is connected to TIM1_CH1, allowing to used the PWM output so to display patterns
rcc_periph_clock_enable ( RCC_TIM1 ) ; // enable clock for timer domain
timer_reset ( TIM1 ) ; // reset timer configuration
timer_set_mode ( TIM1 , TIM_CR1_CKD_CK_INT , TIM_CR1_CMS_CENTER_1 , TIM_CR1_DIR_UP ) ; // configure timer to up counting mode (center aligned for more precise duty cycle control)
timer_set_oc_mode ( TIM1 , TIM_OC1 , TIM_OCM_PWM1 ) ; // use PWM output compare mode
timer_enable_oc_output ( TIM1 , TIM_OC1 ) ; // enable output compare output
timer_enable_break_main_output ( TIM1 ) ; // required to enable timer, even when no dead time is used
timer_set_prescaler ( TIM1 , 3296 - 1 ) ; // set prescaler to allow 3/3 seconds PWM output (72MHz/2^16/3296=0.33Hz)
# else
2016-10-23 17:42:27 +02:00
gpio_set_mode ( GPIO ( LED_PORT ) , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO ( LED_PIN ) ) ; // set LED pin to 'output push-pull'
2017-12-12 13:55:38 +01:00
# endif
2016-10-23 17:42:27 +02:00
led_off ( ) ; // switch off LED per default
// setup button
# if defined(BUTTON_PORT) && defined(BUTTON_PIN)
rcc_periph_clock_enable ( RCC_GPIO ( BUTTON_PORT ) ) ; // enable clock for button
gpio_set_mode ( GPIO ( BUTTON_PORT ) , GPIO_MODE_INPUT , GPIO_CNF_INPUT_PULL_UPDOWN , GPIO ( BUTTON_PIN ) ) ; // set button pin to input
rcc_periph_clock_enable ( RCC_AFIO ) ; // enable alternate function clock for external interrupt
exti_select_source ( EXTI ( BUTTON_PIN ) , GPIO ( BUTTON_PORT ) ) ; // mask external interrupt of this pin only for this port
2017-11-24 11:29:10 +01:00
# if defined(BUTTON_PRESSED) && BUTTON_PRESSED
2017-04-03 13:03:29 +02:00
gpio_clear ( GPIO ( BUTTON_PORT ) , GPIO ( BUTTON_PIN ) ) ; // pull down to be able to detect button push (go high)
2016-10-23 17:42:27 +02:00
exti_set_trigger ( EXTI ( BUTTON_PIN ) , EXTI_TRIGGER_RISING ) ; // trigger when button is pressed
2017-11-24 11:29:10 +01:00
# else
2017-04-03 13:03:29 +02:00
gpio_set ( GPIO ( BUTTON_PORT ) , GPIO ( BUTTON_PIN ) ) ; // pull up to be able to detect button push (go low)
exti_set_trigger ( EXTI ( BUTTON_PIN ) , EXTI_TRIGGER_FALLING ) ; // trigger when button is pressed
# endif
2016-10-23 17:42:27 +02:00
exti_enable_request ( EXTI ( BUTTON_PIN ) ) ; // enable external interrupt
nvic_enable_irq ( NVIC_EXTI_IRQ ( BUTTON_PIN ) ) ; // enable interrupt
# endif
2018-01-24 22:18:11 +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
}
# if defined(BUTTON_PIN)
/** interrupt service routine called when button is pressed */
void EXTI_ISR ( BUTTON_PIN ) ( void )
{
exti_reset_request ( EXTI ( BUTTON_PIN ) ) ; // reset interrupt
button_flag = true ; // perform button action
}
# endif