added basic hard fault handler, moving shit around; debounce made to work

master
Ondřej Hruška 8 years ago
parent 39296f1b8a
commit 3fd42eb41a
  1. 15
      CMakeLists.txt
  2. 9
      Src/gpio.c
  3. 51
      Src/stm32f1xx_it.c
  4. 2
      Src/tim.c
  5. 176
      User/debounce.c
  6. 61
      User/debounce.h
  7. 0
      User/dotmatrix.c
  8. 0
      User/dotmatrix.h
  9. 0
      User/malloc_safe.c
  10. 0
      User/malloc_safe.h
  11. 0
      User/max2719.c
  12. 0
      User/max2719.h
  13. 0
      User/syscalls.c
  14. 347
      User/timebase.c
  15. 167
      User/timebase.h
  16. 92
      User/user_main.c
  17. 0
      User/user_main.h

@ -1,7 +1,8 @@
project(f107-fft C ASM) project(f107-fft C ASM)
cmake_minimum_required(VERSION 3.5.0) 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 HAL_SOURCES "Drivers/STM32F1xx_HAL_Driver/Src/*.c")
file(GLOB_RECURSE CMSIS_SOURCES "Drivers/CMSIS/DSP_Lib/Source/*.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/STM32F1xx_HAL_Driver/Inc)
include_directories(Drivers/CMSIS/Include) include_directories(Drivers/CMSIS/Include)
include_directories(Drivers/CMSIS/Device/ST/STM32F1xx/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/arm-none-eabi/include)
include_directories(/usr/lib/gcc/arm-none-eabi/6.1.1/include/) include_directories(/usr/lib/gcc/arm-none-eabi/6.1.1/include/)
# defines for the used libraries
add_definitions(-DSTM32F107xC) add_definitions(-DSTM32F107xC)
add_definitions(-DARM_MATH_CM3) add_definitions(-DARM_MATH_CM3)
add_definitions(-DF_CPU=72000000UL) add_definitions(-DF_CPU=72000000UL)
add_definitions(-DUSE_FULL_ASSERT) 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(HEX_FILE ${PROJECT_SOURCE_DIR}/build/${PROJECT_NAME}.hex)
set(BIN_FILE ${PROJECT_SOURCE_DIR}/build/${PROJECT_NAME}.bin) set(BIN_FILE ${PROJECT_SOURCE_DIR}/build/${PROJECT_NAME}.bin)

@ -64,13 +64,14 @@ void MX_GPIO_Init(void)
__HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOD_CLK_ENABLE();
/*Configure GPIO pin Output Level */ /*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 */ /*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 */ /*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 */ /*Configure GPIO pins : PEPin PEPin PEPin */
GPIO_InitStruct.Pin = SPI1_CS_Pin|LED4_Pin|LED3_Pin; 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 GPIO_InitStruct.Pin = BTN_UP_Pin|BTN_DN_Pin|BTN_L_Pin|BTN_R_Pin
|BTN_CE_Pin; |BTN_CE_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Pull = GPIO_PULLUP; // MX: Changed
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
/*Configure GPIO pins : PDPin PDPin */ /*Configure GPIO pins : PDPin PDPin */

@ -60,13 +60,61 @@ void NMI_Handler(void)
/* USER CODE END NonMaskableInt_IRQn 1 */ /* 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. * @brief This function handles Hard fault interrupt.
*/ */
void HardFault_Handler(void) void HardFault_Handler(void)
{ {
/* USER CODE BEGIN HardFault_IRQn 0 */ /* 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 */ /* USER CODE END HardFault_IRQn 0 */
while (1) while (1)
@ -204,6 +252,5 @@ void DMA1_Channel1_IRQHandler(void)
} }
/* USER CODE BEGIN 1 */ /* USER CODE BEGIN 1 */
/* USER CODE END 1 */ /* USER CODE END 1 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

@ -50,7 +50,7 @@ void MX_TIM3_Init(void)
htim3.Instance = TIM3; htim3.Instance = TIM3;
htim3.Init.Prescaler = 0; htim3.Init.Prescaler = 0;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP; htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 1800*2; htim3.Init.Period = 1800;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
if (HAL_TIM_OC_Init(&htim3) != HAL_OK) if (HAL_TIM_OC_Init(&htim3) != HAL_OK)
{ {

@ -0,0 +1,176 @@
#include <stdbool.h>
#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;
}

@ -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 */

@ -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;
}

@ -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 <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
/** 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 */

@ -3,16 +3,17 @@
// //
#include <inttypes.h> #include <inttypes.h>
#include <stm32f1xx_hal_gpio.h>
#include <dotmatrix.h>
#include <arm_math.h>
#include <arm_const_structs.h> #include <arm_const_structs.h>
#include <arm_math.h>
#include <stm32f1xx_hal_gpio.h>
#include "dotmatrix.h"
#include "mxconstants.h" #include "mxconstants.h"
#include "stm32f1xx_hal.h" #include "stm32f1xx_hal.h"
#include "utils.h" #include "utils.h"
#include "adc.h" #include "adc.h"
#include "tim.h" #include "tim.h"
#include "user_main.h" #include "user_main.h"
#include "debounce.h"
#define SAMPLE_COUNT 256 #define SAMPLE_COUNT 256
#define BIN_COUNT (SAMPLE_COUNT/2) #define BIN_COUNT (SAMPLE_COUNT/2)
@ -20,6 +21,13 @@
#define SCREEN_W 32 #define SCREEN_W 32
#define SCREEN_H 16 #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 uint32_t audio_samples[SAMPLE_COUNT * 2]; // 2x size needed for complex FFT
static float *audio_samples_f = (float *) audio_samples; 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_wave();
static void display_fft(); static void display_fft();
// region Audio capture & display
void capture_start() void capture_start()
{ {
if (capture_pending) return; if (capture_pending) return;
@ -140,20 +150,29 @@ static void display_fft()
dmtx_show(dmtx); 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 static void gamepad_button_press(uint32_t btn)
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, 1); {
HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, 1); uart_print("Button press ");
HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, 1); char x[2];
HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, 1); x[0] = '0' + btn;
x[1] = 0;
uart_print(x);
uart_print("\n");
}
void user_init() {
// Enable audio input // Enable audio input
HAL_GPIO_WritePin(AUDIO_NSTBY_GPIO_Port, AUDIO_NSTBY_Pin, 1); HAL_GPIO_WritePin(AUDIO_NSTBY_GPIO_Port, AUDIO_NSTBY_Pin, 1);
// Init display
DotMatrix_Init disp_init; DotMatrix_Init disp_init;
disp_init.cols = 4; disp_init.cols = 4;
disp_init.rows = 2; disp_init.rows = 2;
@ -167,24 +186,61 @@ void user_main()
dmtx_clear(disp); dmtx_clear(disp);
dmtx_show(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; uint32_t counter2 = 0;
while (1) { while (1) {
if (counter1++ == 500) { if (ms_loop_elapsed(&counter1, 500)) {
counter2 = 0;
// Blink // Blink
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin); HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
} }
if (counter2++ >= 5) {
if (!capture_pending) { if (!capture_pending) {
counter2 = 0;
capture_start(); capture_start();
} }
} }
HAL_Delay(1);
}
} }
//region Error handlers //region Error handlers
Loading…
Cancel
Save