From 3fd42eb41ac72d47704089919f1eed5a3cb4e307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 3 Sep 2016 12:55:17 +0200 Subject: [PATCH] added basic hard fault handler, moving shit around; debounce made to work --- CMakeLists.txt | 15 +- Src/gpio.c | 9 +- Src/stm32f1xx_it.c | 53 ++++- Src/tim.c | 2 +- User/debounce.c | 176 ++++++++++++++++ User/debounce.h | 61 ++++++ {Max2719 => User}/dotmatrix.c | 0 {Max2719 => User}/dotmatrix.h | 0 {Max2719 => User}/malloc_safe.c | 0 {Max2719 => User}/malloc_safe.h | 0 {Max2719 => User}/max2719.c | 0 {Max2719 => User}/max2719.h | 0 {Src => User}/syscalls.c | 0 User/timebase.c | 347 ++++++++++++++++++++++++++++++++ User/timebase.h | 167 +++++++++++++++ {Src => User}/user_main.c | 96 +++++++-- {Inc => User}/user_main.h | 0 17 files changed, 892 insertions(+), 34 deletions(-) create mode 100644 User/debounce.c create mode 100644 User/debounce.h rename {Max2719 => User}/dotmatrix.c (100%) rename {Max2719 => User}/dotmatrix.h (100%) rename {Max2719 => User}/malloc_safe.c (100%) rename {Max2719 => User}/malloc_safe.h (100%) rename {Max2719 => User}/max2719.c (100%) rename {Max2719 => User}/max2719.h (100%) rename {Src => User}/syscalls.c (100%) create mode 100644 User/timebase.c create mode 100644 User/timebase.h rename {Src => User}/user_main.c (75%) rename {Inc => User}/user_main.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index c7884c5..8cd105d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,8 @@ project(f107-fft C ASM) cmake_minimum_required(VERSION 3.5.0) -file(GLOB_RECURSE USER_SOURCES "Src/*.c" "Max2719/*.c") +file(GLOB_RECURSE USER_SOURCES "User/*.c") +file(GLOB_RECURSE MX_SOURCES "Src/*.c") file(GLOB_RECURSE HAL_SOURCES "Drivers/STM32F1xx_HAL_Driver/Src/*.c") file(GLOB_RECURSE CMSIS_SOURCES "Drivers/CMSIS/DSP_Lib/Source/*.c") @@ -14,19 +15,21 @@ include_directories(Inc) include_directories(Drivers/STM32F1xx_HAL_Driver/Inc) include_directories(Drivers/CMSIS/Include) include_directories(Drivers/CMSIS/Device/ST/STM32F1xx/Include) -include_directories(Max2719) +include_directories(User) + +# stdlib path include_directories(/usr/arm-none-eabi/include) include_directories(/usr/lib/gcc/arm-none-eabi/6.1.1/include/) - +# defines for the used libraries add_definitions(-DSTM32F107xC) add_definitions(-DARM_MATH_CM3) add_definitions(-DF_CPU=72000000UL) - add_definitions(-DUSE_FULL_ASSERT) -add_executable(${PROJECT_NAME}.elf ${USER_SOURCES} ${LINKER_SCRIPT}) -target_link_libraries(${PROJECT_NAME}.elf HAL CMSIS ) +add_executable(${PROJECT_NAME}.elf ${MX_SOURCES} ${USER_SOURCES} ${LINKER_SCRIPT}) + +target_link_libraries(${PROJECT_NAME}.elf HAL CMSIS) set(HEX_FILE ${PROJECT_SOURCE_DIR}/build/${PROJECT_NAME}.hex) set(BIN_FILE ${PROJECT_SOURCE_DIR}/build/${PROJECT_NAME}.bin) diff --git a/Src/gpio.c b/Src/gpio.c index b1d501b..281597a 100644 --- a/Src/gpio.c +++ b/Src/gpio.c @@ -64,13 +64,14 @@ void MX_GPIO_Init(void) __HAL_RCC_GPIOD_CLK_ENABLE(); /*Configure GPIO pin Output Level */ - HAL_GPIO_WritePin(GPIOE, SPI1_CS_Pin|LED4_Pin|LED3_Pin, GPIO_PIN_RESET); + HAL_GPIO_WritePin(GPIOE, SPI1_CS_Pin, GPIO_PIN_RESET); + HAL_GPIO_WritePin(GPIOE, LED4_Pin|LED3_Pin, 1); // MX: Changed /*Configure GPIO pin Output Level */ - HAL_GPIO_WritePin(AUDIO_NSTBY_GPIO_Port, AUDIO_NSTBY_Pin, GPIO_PIN_RESET); + HAL_GPIO_WritePin(AUDIO_NSTBY_GPIO_Port, AUDIO_NSTBY_Pin, 1); // MX: Changed /*Configure GPIO pin Output Level */ - HAL_GPIO_WritePin(GPIOD, LED2_Pin|LED1_Pin, GPIO_PIN_RESET); + HAL_GPIO_WritePin(GPIOD, LED2_Pin|LED1_Pin, 1); // MX: Changed /*Configure GPIO pins : PEPin PEPin PEPin */ GPIO_InitStruct.Pin = SPI1_CS_Pin|LED4_Pin|LED3_Pin; @@ -89,7 +90,7 @@ void MX_GPIO_Init(void) GPIO_InitStruct.Pin = BTN_UP_Pin|BTN_DN_Pin|BTN_L_Pin|BTN_R_Pin |BTN_CE_Pin; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; - GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Pull = GPIO_PULLUP; // MX: Changed HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); /*Configure GPIO pins : PDPin PDPin */ diff --git a/Src/stm32f1xx_it.c b/Src/stm32f1xx_it.c index 25ec695..1404aa9 100644 --- a/Src/stm32f1xx_it.c +++ b/Src/stm32f1xx_it.c @@ -44,7 +44,7 @@ extern DMA_HandleTypeDef hdma_adc1; /******************************************************************************/ -/* Cortex-M3 Processor Interruption and Exception Handlers */ +/* Cortex-M3 Processor Interruption and Exception Handlers */ /******************************************************************************/ /** @@ -60,13 +60,61 @@ void NMI_Handler(void) /* USER CODE END NonMaskableInt_IRQn 1 */ } + +/* USER CODE BEGIN prvGetRegistersFromStack */ +/** + * Hard Fault diagnosis function (for use with debugger) + * + * Source: + * http://www.freertos.org/Debugging-Hard-Faults-On-Cortex-M-Microcontrollers.html + */ +static void __attribute__((noreturn)) __attribute__((used)) +prvGetRegistersFromStack(uint32_t *pulFaultStackAddress) +{ + /* These are volatile to try and prevent the compiler/linker optimising them + away as the variables never actually get used. If the debugger won't show the + values of the variables, make them global my moving their declaration outside + of this function. */ + volatile __attribute__((unused)) uint32_t r0; + volatile __attribute__((unused)) uint32_t r1; + volatile __attribute__((unused)) uint32_t r2; + volatile __attribute__((unused)) uint32_t r3; + volatile __attribute__((unused)) uint32_t r12; + volatile __attribute__((unused)) uint32_t lr; /* Link register. */ + volatile __attribute__((unused)) uint32_t pc; /* Program counter. */ + volatile __attribute__((unused)) uint32_t psr;/* Program status register. */ + + r0 = pulFaultStackAddress[ 0 ]; + r1 = pulFaultStackAddress[ 1 ]; + r2 = pulFaultStackAddress[ 2 ]; + r3 = pulFaultStackAddress[ 3 ]; + + r12 = pulFaultStackAddress[ 4 ]; + lr = pulFaultStackAddress[ 5 ]; + pc = pulFaultStackAddress[ 6 ]; + psr = pulFaultStackAddress[ 7 ]; + + /* When the following line is hit, the variables contain the register values. */ + for (;;); +} +/* USER CODE END prvGetRegistersFromStack */ + /** * @brief This function handles Hard fault interrupt. */ void HardFault_Handler(void) { /* USER CODE BEGIN HardFault_IRQn 0 */ - uart_print("Hard fault.\n"); + __asm volatile + ( + " tst lr, #4 \n" + " ite eq \n" + " mrseq r0, msp \n" + " mrsne r0, psp \n" + " ldr r1, [r0, #24] \n" + " ldr r2, =prvGetRegistersFromStack \n" + " bx r2 \n" + ); /* USER CODE END HardFault_IRQn 0 */ while (1) @@ -204,6 +252,5 @@ void DMA1_Channel1_IRQHandler(void) } /* USER CODE BEGIN 1 */ - /* USER CODE END 1 */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Src/tim.c b/Src/tim.c index 33a74b5..2d7f21a 100644 --- a/Src/tim.c +++ b/Src/tim.c @@ -50,7 +50,7 @@ void MX_TIM3_Init(void) htim3.Instance = TIM3; htim3.Init.Prescaler = 0; htim3.Init.CounterMode = TIM_COUNTERMODE_UP; - htim3.Init.Period = 1800*2; + htim3.Init.Period = 1800; htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; if (HAL_TIM_OC_Init(&htim3) != HAL_OK) { diff --git a/User/debounce.c b/User/debounce.c new file mode 100644 index 0000000..8223fea --- /dev/null +++ b/User/debounce.c @@ -0,0 +1,176 @@ +#include +#include "debounce.h" +#include "timebase.h" +#include "malloc_safe.h" + +// ms debounce time + +#define DEF_DEBO_TIME 20 + +typedef struct { + GPIO_TypeDef *GPIOx; ///< GPIO base + uint16_t pin; ///< bit mask + bool state; ///< current state + bool invert; ///< invert pin + debo_id_t id; ///< pin ID + uint32_t cb_payload; ///< payload passed to the callbac + ms_time_t debo_time; ///< debouncing time (ms) + ms_time_t counter_0; ///< counter for falling edge (ms) + ms_time_t counter_1; ///< counter for rising edge (ms) + void (*falling_cb)(uint32_t); + void (*rising_cb)(uint32_t); +} debo_slot_t; + + +/** Number of allocated slots */ +static size_t debo_slot_count = 0; + +/** Slots array */ +static debo_slot_t *debo_slots; + +/** Next free pin ID for make_id() */ +static debo_id_t next_pin_id = 1; + +void debo_periodic_task(void *unused); + + +/** + * @brief Get a valid free pin ID for a new entry. + * @return the ID. + */ +static debo_id_t make_id(void) +{ + debo_id_t id = next_pin_id++; + + // make sure no task is given PID 0 + if (next_pin_id == DEBO_PIN_NONE) { + next_pin_id++; + } + + return id; +} + + +/** Init the debouncer */ +void debounce_init(size_t slot_count) +{ + debo_slots = calloc_s(slot_count, sizeof(debo_slot_t)); + debo_slot_count = slot_count; + + add_periodic_task(debo_periodic_task, NULL, 1, false); +} + + +/** Register a pin */ +debo_id_t debo_register_pin(debo_init_t *init) +{ + for (size_t i = 0; i < debo_slot_count; i++) { + debo_slot_t *slot = &debo_slots[i]; + + if (slot->id != DEBO_PIN_NONE) continue; // slot is used + + slot->GPIOx = init->GPIOx; + slot->pin = init->pin; + slot->falling_cb = init->falling_cb; + slot->rising_cb = init->rising_cb; + slot->cb_payload = init->cb_payload; + slot->invert = init->invert; + slot->counter_0 = 0; + slot->counter_1 = 0; + slot->debo_time = (init->debo_time == 0) ? DEF_DEBO_TIME : init->debo_time; + + bool state = HAL_GPIO_ReadPin(slot->GPIOx, slot->pin); + if (slot->invert) state = !state; + slot->state = state; + + slot->id = make_id(); + + return slot->id; + } + + return DEBO_PIN_NONE; +} + + +/** Callback that must be called every 1 ms */ +void debo_periodic_task(void *unused) +{ + UNUSED(unused); + + for (size_t i = 0; i < debo_slot_count; i++) { + debo_slot_t *slot = &debo_slots[i]; + if (slot->id == DEBO_PIN_NONE) continue; // unused + + bool state = HAL_GPIO_ReadPin(slot->GPIOx, slot->pin); + if (slot->invert) state = !state; + + if (slot->state != state) { + if (state == 0) { + // falling + + if (slot->counter_0++ == slot->debo_time) { + slot->state = 0; + + if (slot->falling_cb != NULL) { + slot->falling_cb(slot->cb_payload); + } + } + } else { + // rising + + if (slot->counter_1++ == slot->debo_time) { + slot->state = 1; + + if (slot->rising_cb != NULL) { + slot->rising_cb(slot->cb_payload); + } + } + } + } else { + // reset counters + slot->counter_0 = 0; + slot->counter_1 = 0; + } + } +} + + +/** + * @brief Check if a pin is high + * @param pin_id : Slot ID + * @return true if the pin is registered and is HIGH + */ +bool debo_pin_state(debo_id_t pin_id) +{ + if (pin_id == DEBO_PIN_NONE) return false; + + for (size_t i = 0; i < debo_slot_count; i++) { + debo_slot_t *slot = &debo_slots[i]; + if (slot->id != pin_id) continue; + + return slot->state; + } + + return false; +} + + +/** + * @brief Remove a pin entry from the debouncer. + * @param pin_id : Slot ID + * @return true if task found & removed. + */ +bool debo_remove_pin(debo_id_t pin_id) +{ + if (pin_id == DEBO_PIN_NONE) return false; + + for (size_t i = 0; i < debo_slot_count; i++) { + debo_slot_t *slot = &debo_slots[i]; + if (slot->id != pin_id) continue; + + slot->id = DEBO_PIN_NONE; + return true; + } + + return false; +} diff --git a/User/debounce.h b/User/debounce.h new file mode 100644 index 0000000..7f4239b --- /dev/null +++ b/User/debounce.h @@ -0,0 +1,61 @@ +#ifndef MPORK_DEBOUNCE_H +#define MPORK_DEBOUNCE_H + +#include "timebase.h" +#include "stm32f1xx_hal.h" + +// Debouncer requires that you setup SysTick first. + +/** Debounced pin ID - used for state readout */ +typedef uint32_t debo_id_t; + +/** debo_id_t indicating unused slot */ +#define DEBO_PIN_NONE 0 + + +/** + * @brief Initialize the debouncer. + * + * Registers the callback. + * + * @param pin_count : number of pin slots to allocate + */ +void debounce_init(size_t pin_count); + + +typedef struct { + GPIO_TypeDef *GPIOx; ///< GPIO base + uint16_t pin; ///< pin mask + bool invert; ///< invert value read from GPIO (button to ground) + ms_time_t debo_time; ///< debounce time in ms, 0 = default (20 ms) + uint32_t cb_payload; ///< Value passed to the callback func + void (*rising_cb)(uint32_t); ///< callback when the pin goes HIGH + void (*falling_cb)(uint32_t); ///< callback when the pin goes LOW +} debo_init_t; + + +/** + * @brief Add a pin for debouncing. + * + * The pin state will be checked with the configured hysteresis + * and callbacks will be called when a state change is detected. + */ +debo_id_t debo_register_pin(debo_init_t *init_struct); + + +/** + * @brief Check if a pin is high + * @param pin_id : Slot ID + * @return true if the pin is registered and is HIGH + */ +bool debo_pin_state(debo_id_t pin_id); + + +/** + * @brief Remove a pin entry from the debouncer. + * @param pin_id : Slot ID + * @return true if task found & removed. + */ +bool debo_remove_pin(debo_id_t pin_id); + +#endif /* MPORK_DEBOUNCE_H */ diff --git a/Max2719/dotmatrix.c b/User/dotmatrix.c similarity index 100% rename from Max2719/dotmatrix.c rename to User/dotmatrix.c diff --git a/Max2719/dotmatrix.h b/User/dotmatrix.h similarity index 100% rename from Max2719/dotmatrix.h rename to User/dotmatrix.h diff --git a/Max2719/malloc_safe.c b/User/malloc_safe.c similarity index 100% rename from Max2719/malloc_safe.c rename to User/malloc_safe.c diff --git a/Max2719/malloc_safe.h b/User/malloc_safe.h similarity index 100% rename from Max2719/malloc_safe.h rename to User/malloc_safe.h diff --git a/Max2719/max2719.c b/User/max2719.c similarity index 100% rename from Max2719/max2719.c rename to User/max2719.c diff --git a/Max2719/max2719.h b/User/max2719.h similarity index 100% rename from Max2719/max2719.h rename to User/max2719.h diff --git a/Src/syscalls.c b/User/syscalls.c similarity index 100% rename from Src/syscalls.c rename to User/syscalls.c diff --git a/User/timebase.c b/User/timebase.c new file mode 100644 index 0000000..55ce4b4 --- /dev/null +++ b/User/timebase.c @@ -0,0 +1,347 @@ +#include "timebase.h" +#include "malloc_safe.h" + +// Time base +static volatile ms_time_t SystemTime_ms = 0; + + +typedef struct { + /** User callback with arg */ + void (*callback)(void *); + /** Arg for the arg callback */ + void *cb_arg; + /** Callback interval */ + ms_time_t interval_ms; + /** Counter, when reaches interval_ms, is cleared and callback is called. */ + ms_time_t countup; + /** Unique task ID (for cancelling / modification) */ + task_pid_t pid; + /** Enable flag - disabled tasks still count, but CB is not run */ + bool enabled; + /** Marks that the task is due to be run */ + bool enqueue; +} periodic_task_t; + + +typedef struct { + /** User callback with arg */ + void (*callback)(void *); + /** Arg for the arg callback */ + void *cb_arg; + /** Counter, when reaches 0ms, callback is called and the task is removed */ + ms_time_t countdown_ms; + /** Unique task ID (for cancelling / modification) */ + task_pid_t pid; + /** Whether this task is long and needs posting on the queue */ + bool enqueue; +} future_task_t; + + +static size_t periodic_slot_count = 0; +static size_t future_slot_count = 0; + +static periodic_task_t *periodic_tasks; +static future_task_t *future_tasks; + + +/** Init timebase */ +void timebase_init(size_t periodic, size_t future) +{ + periodic_slot_count = periodic; + future_slot_count = future; + + periodic_tasks = calloc_s(periodic, sizeof(periodic_task_t)); + future_tasks = calloc_s(future, sizeof(future_task_t)); +} + + +static task_pid_t next_task_pid = 1; // 0 (PID_NONE) is reserved + + +/** Get a valid free PID for a new task. */ +static task_pid_t make_pid(void) +{ + task_pid_t pid = next_task_pid++; + + // make sure no task is given PID 0 + if (next_task_pid == PID_NONE) { + next_task_pid++; + } + + return pid; +} + + +/** Take an empty periodic task slot and populate the basics. */ +static periodic_task_t* claim_periodic_task_slot(ms_time_t interval, bool enqueue) +{ + for (size_t i = 0; i < periodic_slot_count; i++) { + periodic_task_t *task = &periodic_tasks[i]; + if (task->pid != PID_NONE) continue; // task is used + + task->countup = 0; + task->interval_ms = interval - 1; + task->enqueue = enqueue; + task->pid = make_pid(); + task->enabled = true; + return task; + } + + // TODO logging + //error("Periodic task table full."); + + return NULL; +} + + +/** Take an empty future task slot and populate the basics. */ +static future_task_t* claim_future_task_slot(ms_time_t delay, bool enqueue) +{ + for (size_t i = 0; i < future_slot_count; i++) { + future_task_t *task = &future_tasks[i]; + if (task->pid != PID_NONE) continue; // task is used + + task->countdown_ms = delay; + task->enqueue = enqueue; + task->pid = make_pid(); + return task; + } + + //error("Future task table full."); + + return NULL; +} + +/** Add a periodic task with an arg. */ +task_pid_t add_periodic_task(void (*callback)(void*), void* arg, ms_time_t interval, bool enqueue) +{ + periodic_task_t *task = claim_periodic_task_slot(interval, enqueue); + + if (task == NULL) return PID_NONE; + + task->callback = callback; + task->cb_arg = arg; + + return task->pid; +} + + +/** Schedule a future task, with uint32_t argument. */ +task_pid_t schedule_task(void (*callback)(void*), void *arg, ms_time_t delay, bool enqueue) +{ + future_task_t *task = claim_future_task_slot(delay, enqueue); + + if (task == NULL) return PID_NONE; + + task->callback = callback; + task->cb_arg = arg; + + return task->pid; +} + + +/** Enable or disable a periodic task. */ +bool enable_periodic_task(task_pid_t pid, bool enable) +{ + if (pid == PID_NONE) return false; + + for (size_t i = 0; i < periodic_slot_count; i++) { + periodic_task_t *task = &periodic_tasks[i]; + if (task->pid != pid) continue; + + task->enabled = (enable == ENABLE); + return true; + } + + return false; +} + + +/** Check if a periodic task is enabled */ +bool is_periodic_task_enabled(task_pid_t pid) +{ + if (pid == PID_NONE) return false; + + for (size_t i = 0; i < periodic_slot_count; i++) { + periodic_task_t *task = &periodic_tasks[i]; + if (task->pid != pid) continue; + + return task->enabled; + } + + return false; +} + +bool reset_periodic_task(task_pid_t pid) +{ + if (pid == PID_NONE) return false; + + for (size_t i = 0; i < periodic_slot_count; i++) { + periodic_task_t *task = &periodic_tasks[i]; + if (task->pid != pid) continue; + + task->countup = 0; + return true; + } + + return false; +} + + +bool set_periodic_task_interval(task_pid_t pid, ms_time_t interval) +{ + if (pid == PID_NONE) return false; + + for (size_t i = 0; i < periodic_slot_count; i++) { + periodic_task_t *task = &periodic_tasks[i]; + if (task->pid != pid) continue; + task->interval_ms = interval; + return true; + } + + return false; +} + + +/** Remove a periodic task. */ +bool remove_periodic_task(task_pid_t pid) +{ + if (pid == PID_NONE) return false; + + for (size_t i = 0; i < periodic_slot_count; i++) { + periodic_task_t *task = &periodic_tasks[i]; + if (task->pid != pid) continue; + + task->pid = PID_NONE; // mark unused + return true; + } + + return false; +} + + +/** Abort a scheduled task. */ +bool abort_scheduled_task(task_pid_t pid) +{ + if (pid == PID_NONE) return false; + + for (size_t i = 0; i < future_slot_count; i++) { + future_task_t *task = &future_tasks[i]; + if (task->pid != pid) continue; + + task->pid = PID_NONE; // mark unused + return true; + } + + return false; +} + + +/** Run a periodic task */ +static void run_periodic_task(periodic_task_t *task) +{ + if (!task->enabled) return; + + if (task->enqueue) { + // queued task + // FIXME re-implement queue + //tq_post(task->callback, task->cb_arg); + } else { + // immediate task + task->callback(task->cb_arg); + } +} + + +/** Run a future task */ +static void run_future_task(future_task_t *task) +{ + if (task->enqueue) { + // queued task + // FIXME re-implement queue + //tq_post(task->callback, task->cb_arg); + } else { + // immediate task + task->callback(task->cb_arg); + } +} + + +/** + * @brief Millisecond callback, should be run in the SysTick handler. + */ +void timebase_ms_cb(void) +{ + // increment global time + SystemTime_ms++; + + // run periodic tasks + for (size_t i = 0; i < periodic_slot_count; i++) { + periodic_task_t *task = &periodic_tasks[i]; + if (task->pid == PID_NONE) continue; // unused + + if (task->countup++ >= task->interval_ms) { + // run if enabled + run_periodic_task(task); + // restart counter + task->countup = 0; + } + } + + // run planned future tasks + for (size_t i = 0; i < future_slot_count; i++) { + future_task_t *task = &future_tasks[i]; + if (task->pid == PID_NONE) continue; // unused + + if (task->countdown_ms-- == 0) { + // run + run_future_task(task); + // release the slot + task->pid = PID_NONE; + } + } +} + + + +/** Seconds delay */ +void delay_s(uint32_t s) +{ + while (s-- != 0) { + delay_ms(1000); + } +} + + +/** Delay N ms */ +void delay_ms(ms_time_t ms) +{ + ms_time_t start = SystemTime_ms; + while ((SystemTime_ms - start) < ms); // overrun solved by unsigned arithmetic +} + + +/** Get milliseconds elapsed since start timestamp */ +ms_time_t ms_elapsed(ms_time_t start) +{ + return SystemTime_ms - start; +} + + +/** Get current timestamp. */ +ms_time_t ms_now(void) +{ + return SystemTime_ms; +} + + +/** Helper for looping with periodic branches */ +bool ms_loop_elapsed(ms_time_t *start, ms_time_t duration) +{ + if (SystemTime_ms - *start >= duration) { + *start = SystemTime_ms; + return true; + } + + return false; +} diff --git a/User/timebase.h b/User/timebase.h new file mode 100644 index 0000000..7fbf82e --- /dev/null +++ b/User/timebase.h @@ -0,0 +1,167 @@ +#ifndef MPORK_TIMEBASE_H +#define MPORK_TIMEBASE_H + +/** + * To use the Timebase functionality, + * set up SysTick to 1 kHz and call + * timebase_ms_cb() in the IRQ. + * + * If you plan to use pendable future tasks, + * also make sure you call run_pending_tasks() + * in your main loop. + * + * This is not needed for non-pendable tasks. + */ + +#include +#include +#include + + +/** Task PID. */ +typedef uint32_t task_pid_t; + +/** Time value in ms */ +typedef uint32_t ms_time_t; + +// PID value that can be used to indicate no task +#define PID_NONE 0 + +/** Loop until timeout - use in place of while() or for(). break and continue work too! */ +#define until_timeout(to_ms) for(uint32_t _utmeo = ms_now(); ms_elapsed(_utmeo) < (to_ms);) + +/** Retry a call until a timeout. Variable 'suc' is set to the return value. Must be defined. */ +#define retry_TO(to_ms, call) \ + until_timeout(to_ms) { \ + suc = call; \ + if (suc) break; \ + } + +/** Init timebase, allocate slots for tasks. */ +void timebase_init(size_t periodic_count, size_t future_count); + +/** Must be called every 1 ms */ +void timebase_ms_cb(void); + + +// --- Periodic ----------------------------------------------- + + +/** + * @brief Add a periodic task with an arg. + * @param callback : task callback + * @param arg : callback argument + * @param interval : task interval (ms) + * @param enqueue : put on the task queue when due + * @return task PID + */ +task_pid_t add_periodic_task(void (*callback)(void *), void *arg, ms_time_t interval, bool enqueue); + + +/** Destroy a periodic task. */ +bool remove_periodic_task(task_pid_t pid); + +/** Enable or disable a periodic task. Returns true on success. */ +bool enable_periodic_task(task_pid_t pid, bool cmd); + +/** Check if a periodic task exists and is enabled. */ +bool is_periodic_task_enabled(task_pid_t pid); + +/** Reset timer for a task */ +bool reset_periodic_task(task_pid_t pid); + +/** Set inteval */ +bool set_periodic_task_interval(task_pid_t pid, ms_time_t interval); + + +// --- Future ------------------------------------------------- + + +/** + * @brief Schedule a future task, with uint32_t argument. + * @param callback : task callback + * @param arg : callback argument + * @param delay : task delay (ms) + * @param enqueue : put on the task queue when due + * @return task PID + */ +task_pid_t schedule_task(void (*callback_arg)(void *), void *arg, ms_time_t delay, bool enqueue); + + +/** Abort a scheduled task. */ +bool abort_scheduled_task(task_pid_t pid); + + +// --- Waiting functions -------------------------------------- + +/** Get milliseconds elapsed since start timestamp */ +ms_time_t ms_elapsed(ms_time_t start); + + +/** Get current timestamp. */ +ms_time_t ms_now(void); + + +/** Delay using SysTick */ +void delay_ms(ms_time_t ms); + + +/** Delay N seconds */ +void delay_s(uint32_t s); + + +inline __attribute__((always_inline)) +void delay_cycles(uint32_t n) +{ + uint32_t l = n >> 2; + + __asm volatile( + "0: mov r0,r0;" + "subs %[count], #1;" + "bne 0b;" + : [count] "+r"(l) + ); +} + + +inline __attribute__((always_inline)) +void delay_ns(uint32_t ns) +{ + delay_cycles(ns / 24); +} + + +/** + * @brief Microsecond delay. + * @param us + */ +inline __attribute__((always_inline)) +void delay_us(uint32_t us) +{ + delay_ns(us * 1150); +} + + +/** + * @brief Check if time since `start` elapsed. + * + * If so, sets the *start variable to the current time. + * + * Example: + * + * ms_time_t s = ms_now(); + * + * while(1) { + * if (ms_loop_elapsed(&s, 100)) { + * // this is called every 100 ms + * } + * // ... rest of the loop ... + * } + * + * @param start start time variable + * @param duration delay length + * @return delay elapsed; start was updated. + */ +bool ms_loop_elapsed(ms_time_t *start, ms_time_t duration); + +#endif /* MPORK_TIMEBASE_H */ diff --git a/Src/user_main.c b/User/user_main.c similarity index 75% rename from Src/user_main.c rename to User/user_main.c index 583f091..e445955 100644 --- a/Src/user_main.c +++ b/User/user_main.c @@ -3,16 +3,17 @@ // #include -#include -#include -#include #include +#include +#include +#include "dotmatrix.h" #include "mxconstants.h" #include "stm32f1xx_hal.h" #include "utils.h" #include "adc.h" #include "tim.h" #include "user_main.h" +#include "debounce.h" #define SAMPLE_COUNT 256 #define BIN_COUNT (SAMPLE_COUNT/2) @@ -20,6 +21,13 @@ #define SCREEN_W 32 #define SCREEN_H 16 +// Pins +#define BTN_CENTER 0 +#define BTN_LEFT 1 +#define BTN_RIGHT 2 +#define BTN_UP 3 +#define BTN_DOWN 4 + static uint32_t audio_samples[SAMPLE_COUNT * 2]; // 2x size needed for complex FFT static float *audio_samples_f = (float *) audio_samples; @@ -30,6 +38,8 @@ static volatile bool capture_pending = false; static void display_wave(); static void display_fft(); +// region Audio capture & display + void capture_start() { if (capture_pending) return; @@ -140,20 +150,29 @@ static void display_fft() dmtx_show(dmtx); } +// endregion -void user_main() +// Increment timebase counter each ms +void HAL_SYSTICK_Callback(void) { - uart_print("== USER CODE STARTING ==\n"); + timebase_ms_cb(); +} - // Leds OFF - HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, 1); - HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, 1); - HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, 1); - HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, 1); +static void gamepad_button_press(uint32_t btn) +{ + uart_print("Button press "); + char x[2]; + x[0] = '0' + btn; + x[1] = 0; + uart_print(x); + uart_print("\n"); +} +void user_init() { // Enable audio input HAL_GPIO_WritePin(AUDIO_NSTBY_GPIO_Port, AUDIO_NSTBY_Pin, 1); + // Init display DotMatrix_Init disp_init; disp_init.cols = 4; disp_init.rows = 2; @@ -167,23 +186,60 @@ void user_main() dmtx_clear(disp); dmtx_show(disp); - uint32_t counter1 = 0; + timebase_init(5, 5); + debounce_init(5); + + // Gamepad + debo_init_t debo; + debo.debo_time = 50; + debo.invert = true; + debo.falling_cb = NULL; + debo.rising_cb = gamepad_button_press; + // Central button + debo.cb_payload = BTN_CENTER; + debo.GPIOx = BTN_CE_GPIO_Port; + debo.pin = BTN_CE_Pin; + debo_register_pin(&debo); + // Left + debo.cb_payload = BTN_LEFT; + debo.GPIOx = BTN_L_GPIO_Port; + debo.pin = BTN_L_Pin; + debo_register_pin(&debo); + // Right + debo.cb_payload = BTN_RIGHT; + debo.GPIOx = BTN_R_GPIO_Port; + debo.pin = BTN_R_Pin; + debo_register_pin(&debo); + // Up + debo.cb_payload = BTN_UP; + debo.GPIOx = BTN_UP_GPIO_Port; + debo.pin = BTN_UP_Pin; + debo_register_pin(&debo); + // Down + debo.cb_payload = BTN_DOWN; + debo.GPIOx = BTN_DN_GPIO_Port; + debo.pin = BTN_DN_Pin; + debo_register_pin(&debo); +} + + +void user_main() +{ + uart_print("== USER CODE STARTING ==\n"); + + user_init(); + + ms_time_t counter1 = 0; uint32_t counter2 = 0; while (1) { - if (counter1++ == 500) { - counter2 = 0; + if (ms_loop_elapsed(&counter1, 500)) { // Blink HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin); } - if (counter2++ >= 5) { - if (!capture_pending) { - counter2 = 0; - capture_start(); - } + if (!capture_pending) { + capture_start(); } - - HAL_Delay(1); } } diff --git a/Inc/user_main.h b/User/user_main.h similarity index 100% rename from Inc/user_main.h rename to User/user_main.h