4d7f4eb6a9
Reads ADC values into a bytearray (or similar) at a fixed rate. Needs a better name and improved API. Also fix up DAC dma function (which also needs a better name and API).
217 lines
6.5 KiB
C
217 lines
6.5 KiB
C
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <stm32f4xx_hal.h>
|
|
#include "usbd_cdc_msc_hid.h"
|
|
#include "usbd_cdc_interface.h"
|
|
|
|
#include "nlr.h"
|
|
#include "misc.h"
|
|
#include "mpconfig.h"
|
|
#include "qstr.h"
|
|
#include "obj.h"
|
|
#include "runtime.h"
|
|
#include "timer.h"
|
|
#include "servo.h"
|
|
|
|
// The timers can be used by multiple drivers, and need a common point for
|
|
// the interrupts to be dispatched, so they are all collected here.
|
|
//
|
|
// TIM3:
|
|
// - USB CDC interface, interval, to check for new data
|
|
// - LED 4, PWM to set the LED intensity
|
|
//
|
|
// TIM5:
|
|
// - servo controller, PWM
|
|
|
|
TIM_HandleTypeDef TIM3_Handle;
|
|
TIM_HandleTypeDef TIM5_Handle;
|
|
TIM_HandleTypeDef TIM6_Handle;
|
|
|
|
// TIM3 is set-up for the USB CDC interface
|
|
void timer_tim3_init(void) {
|
|
// set up the timer for USBD CDC
|
|
__TIM3_CLK_ENABLE();
|
|
|
|
TIM3_Handle.Instance = TIM3;
|
|
TIM3_Handle.Init.Period = (USBD_CDC_POLLING_INTERVAL*1000) - 1;
|
|
TIM3_Handle.Init.Prescaler = 84-1;
|
|
TIM3_Handle.Init.ClockDivision = 0;
|
|
TIM3_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
|
|
HAL_TIM_Base_Init(&TIM3_Handle);
|
|
|
|
HAL_NVIC_SetPriority(TIM3_IRQn, 6, 0);
|
|
HAL_NVIC_EnableIRQ(TIM3_IRQn);
|
|
|
|
if (HAL_TIM_Base_Start(&TIM3_Handle) != HAL_OK) {
|
|
/* Starting Error */
|
|
}
|
|
}
|
|
|
|
/* unused
|
|
void timer_tim3_deinit(void) {
|
|
// reset TIM3 timer
|
|
__TIM3_FORCE_RESET();
|
|
__TIM3_RELEASE_RESET();
|
|
}
|
|
*/
|
|
|
|
// TIM5 is set-up for the servo controller
|
|
// This function inits but does not start the timer
|
|
void timer_tim5_init(void) {
|
|
// TIM5 clock enable
|
|
__TIM5_CLK_ENABLE();
|
|
|
|
// set up and enable interrupt
|
|
HAL_NVIC_SetPriority(TIM5_IRQn, 6, 0);
|
|
HAL_NVIC_EnableIRQ(TIM5_IRQn);
|
|
|
|
// PWM clock configuration
|
|
TIM5_Handle.Instance = TIM5;
|
|
TIM5_Handle.Init.Period = 2000; // timer cycles at 50Hz
|
|
TIM5_Handle.Init.Prescaler = ((SystemCoreClock / 2) / 100000) - 1; // timer runs at 100kHz
|
|
TIM5_Handle.Init.ClockDivision = 0;
|
|
TIM5_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
|
|
HAL_TIM_PWM_Init(&TIM5_Handle);
|
|
}
|
|
|
|
// Init TIM6 with a counter-overflow at the given frequency (given in Hz)
|
|
// TIM6 is used by the DAC and ADC for auto sampling at a given frequency
|
|
// This function inits but does not start the timer
|
|
void timer_tim6_init(uint freq) {
|
|
// TIM6 clock enable
|
|
__TIM6_CLK_ENABLE();
|
|
|
|
// Timer runs at SystemCoreClock / 2
|
|
// Compute the prescaler value so TIM6 triggers at freq-Hz
|
|
uint32_t period = (SystemCoreClock / 2) / freq;
|
|
uint32_t prescaler = 1;
|
|
while (period > 0xffff) {
|
|
period >>= 1;
|
|
prescaler <<= 1;
|
|
}
|
|
|
|
// Time base clock configuration
|
|
TIM6_Handle.Instance = TIM6;
|
|
TIM6_Handle.Init.Period = period - 1;
|
|
TIM6_Handle.Init.Prescaler = prescaler - 1;
|
|
TIM6_Handle.Init.ClockDivision = 0; // unused for TIM6
|
|
TIM6_Handle.Init.CounterMode = TIM_COUNTERMODE_UP; // unused for TIM6
|
|
HAL_TIM_Base_Init(&TIM6_Handle);
|
|
}
|
|
|
|
// Interrupt dispatch
|
|
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
|
|
if (htim == &TIM3_Handle) {
|
|
USBD_CDC_HAL_TIM_PeriodElapsedCallback();
|
|
} else if (htim == &TIM5_Handle) {
|
|
servo_timer_irq_callback();
|
|
}
|
|
}
|
|
|
|
// below is old code from stm/ which has not yet been fully ported to stmhal/
|
|
#if 0
|
|
typedef struct _pyb_hal_tim_t {
|
|
mp_obj_base_t base;
|
|
TIM_HandleTypeDef htim;
|
|
} pyb_hal_tim_t;
|
|
|
|
pyb_hal_tim_t pyb_hal_tim_6;
|
|
|
|
pyb_hal_tim_6 = {
|
|
.base = {&pyb_type_hal_tim};
|
|
.htim = {TIM6
|
|
|
|
// TIM6 is used as an internal interrup to schedule something at a specific rate
|
|
mp_obj_t timer_py_callback;
|
|
|
|
mp_obj_t timer_py_set_callback(mp_obj_t f) {
|
|
timer_py_callback = f;
|
|
return mp_const_none;
|
|
}
|
|
|
|
mp_obj_t timer_py_set_period(mp_obj_t period) {
|
|
TIM6->ARR = mp_obj_get_int(period) & 0xffff;
|
|
return mp_const_none;
|
|
}
|
|
|
|
mp_obj_t timer_py_set_prescaler(mp_obj_t prescaler) {
|
|
TIM6->PSC = mp_obj_get_int(prescaler) & 0xffff;
|
|
return mp_const_none;
|
|
}
|
|
|
|
mp_obj_t timer_py_get_value(void) {
|
|
return mp_obj_new_int(TIM6->CNT & 0xfffff);
|
|
}
|
|
|
|
void timer_init(void) {
|
|
timer_py_callback = mp_const_none;
|
|
|
|
// TIM6 clock enable
|
|
__TIM6_CLK_ENABLE();
|
|
|
|
// Compute the prescaler value so TIM6 runs at 20kHz
|
|
uint16_t PrescalerValue = (uint16_t) ((SystemCoreClock / 2) / 20000) - 1;
|
|
|
|
// Time base configuration
|
|
tim_handle.Instance = TIM6;
|
|
tim_handle.Init.Prescaler = PrescalerValue;
|
|
tim_handle.Init.CounterMode = TIM_COUNTERMODE_UP; // unused for TIM6
|
|
tim_handle.Init.Period = 20000; // timer cycles at 1Hz
|
|
tim_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; // unused for TIM6
|
|
tim_handle.Init.RepetitionCounter = 0; // unused for TIM6
|
|
HAL_TIM_Base_Init(&tim_handle);
|
|
|
|
// enable perhipheral preload register
|
|
//TIM_ARRPreloadConfig(TIM6, ENABLE); ??
|
|
|
|
// set up interrupt
|
|
HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 0xf, 0xf); // lowest priority
|
|
HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn);
|
|
|
|
// start timer, so that it interrupts on overflow
|
|
HAL_TIM_Base_Start_IT(&tim_handle);
|
|
|
|
// Python interface
|
|
mp_obj_t m = mp_obj_new_module(QSTR_FROM_STR_STATIC("timer"));
|
|
rt_store_attr(m, QSTR_FROM_STR_STATIC("callback"), rt_make_function_n(1, timer_py_set_callback));
|
|
rt_store_attr(m, QSTR_FROM_STR_STATIC("period"), rt_make_function_n(1, timer_py_set_period));
|
|
rt_store_attr(m, QSTR_FROM_STR_STATIC("prescaler"), rt_make_function_n(1, timer_py_set_prescaler));
|
|
rt_store_attr(m, QSTR_FROM_STR_STATIC("value"), rt_make_function_n(0, timer_py_get_value));
|
|
rt_store_name(QSTR_FROM_STR_STATIC("timer"), m);
|
|
}
|
|
|
|
void timer_interrupt(void) {
|
|
if (timer_py_callback != mp_const_none) {
|
|
nlr_buf_t nlr;
|
|
if (nlr_push(&nlr) == 0) {
|
|
// XXX what to do if the GC is in the middle of running??
|
|
rt_call_function_0(timer_py_callback);
|
|
nlr_pop();
|
|
} else {
|
|
// uncaught exception
|
|
printf("exception in timer interrupt\n");
|
|
mp_obj_print((mp_obj_t)nlr.ret_val, PRINT_REPR);
|
|
printf("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
mp_obj_t pyb_Timer(mp_obj_t timx_in) {
|
|
TIM_TypeDef *TIMx = (TIM_TypeDef*)mp_obj_get_int(timx_in);
|
|
if (!IS_TIM_INSTANCE(TIMx)) {
|
|
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "argument 1 is not a TIM instance"));
|
|
}
|
|
pyb_hal_tim_t *tim = m_new_obj(pyb_hal_tim_t);
|
|
tim->htim.Instance = TIMx;
|
|
tim->htim.Instance.Init.Prescaler = x;
|
|
tim->htim.Instance.Init.CounterMode = y;
|
|
tim->htim.Instance.Init.Period = y;
|
|
tim->htim.Instance.Init.ClockDivision = y;
|
|
tim->htim.Instance.Init.RepetitionCounter = y;
|
|
HAL_TIM_Base_Init(&tim->htim);
|
|
return tim;
|
|
}
|
|
#endif
|