SolarLader_Firmware/src/main.c

825 lines
20 KiB
C

#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/adc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/dma.h>
#include <libopencm3/stm32/timer.h>
#include <libopencm3/stm32/rtc.h>
#include <libopencm3/stm32/pwr.h>
#include <libopencm3/stm32/exti.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/cm3/systick.h>
#include <libopencmsis/core_cm3.h>
#include <fxp.h>
#include <fxp_basic.h>
#include "lcd.h"
#include "debug.h"
#define CONV_PWM_PERIOD 960
#define CONV_PWM_MAX (98*CONV_PWM_PERIOD/100)
#define TIM_CH_CONV TIM_OC1
#define TIM_CH_BOOTSTRAP TIM_OC2
#define MAX_SLEEP_TIME 3600
enum OperState {
Bootstrap,
ConvConstVoltage,
ConvConstCurrent,
ConvMPP,
Idle,
};
volatile int wait_frame = 1;
#define ADC_NUM_CHANNELS 3
volatile uint16_t adc_values[ADC_NUM_CHANNELS];
static void unlock_rtc_access(void)
{
pwr_disable_backup_domain_write_protect();
RTC_WPR = 0xCA;
RTC_WPR = 0x53;
}
static void lock_rtc_access(void)
{
RTC_WPR = 0xFF;
pwr_enable_backup_domain_write_protect();
}
static void init_gpio(void)
{
// Set up UART TX on PB6 for debugging
gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO6);
gpio_set_af(GPIOB, GPIO_AF0, GPIO6);
// GPIO for converter switch
gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO8);
gpio_set_af(GPIOA, GPIO_AF2, GPIO8);
// GPIO for bootstrap pulse
gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO9);
gpio_set_af(GPIOA, GPIO_AF2, GPIO9);
// GPIO for load activation
gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO15);
gpio_set(GPIOA, GPIO15);
}
static void init_clock(void)
{
/* Set STM32 to 48 MHz. */
// Relevant for Timers
//rcc_clock_setup_in_hse_8mhz_out_48mhz();
rcc_clock_setup_in_hsi_out_48mhz();
// enable GPIO clocks:
// Port A is needed for the Display and more
rcc_periph_clock_enable(RCC_GPIOA);
// Port B is needed for debugging
rcc_periph_clock_enable(RCC_GPIOB);
// enable TIM3 for scheduling
rcc_periph_clock_enable(RCC_TIM3);
// enable TIM1 for PWM generation
rcc_periph_clock_enable(RCC_TIM1);
// enable ADC1 clock
rcc_periph_clock_enable(RCC_ADC1);
// enable DMA
rcc_periph_clock_enable(RCC_DMA);
}
static void init_rtc(void)
{
// RTC clock setup
// see libopencm3-examples::examples/stm32/l1/stm32l-discovery/button-irq-printf-lowpower/main.c
/* turn on power block to enable unlocking */
rcc_periph_clock_enable(RCC_PWR);
pwr_disable_backup_domain_write_protect();
/* reset rtc */
RCC_BDCR |= RCC_BDCR_BDRST;
RCC_BDCR &= ~RCC_BDCR_BDRST;
// use LSI for RTC
rcc_osc_on(RCC_LSI);
rcc_wait_for_osc_ready(RCC_LSI);
/* Select the LSI as rtc clock */
RCC_BDCR |= RCC_BDCR_RTCSEL_LSI;
/* ?! Stdperiph examples don't turn this on until _afterwards_ which
* simply doesn't work. It must be on at least to be able to
* configure it */
RCC_BDCR |= RCC_BDCR_RTCEN;
pwr_enable_backup_domain_write_protect();
nvic_enable_irq(NVIC_RTC_IRQ);
exti_set_trigger(EXTI17, EXTI_TRIGGER_RISING);
exti_enable_request(EXTI17);
}
static void init_timer(void)
{
// *** TIM1 ***
// Configure channels 1 and 2 for PWM (-> Pins PA8, PA9)
// Ch1 = Buck converter switch, Ch2 = bootstrap pulse
timer_reset(TIM1);
timer_set_mode(TIM1, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
// set up PWM channels
timer_set_oc_mode(TIM1, TIM_CH_CONV, TIM_OCM_PWM1);
timer_enable_oc_output(TIM1, TIM_CH_CONV);
timer_enable_oc_preload(TIM1, TIM_CH_CONV);
timer_set_oc_polarity_high(TIM1, TIM_CH_CONV);
timer_set_oc_mode(TIM1, TIM_CH_BOOTSTRAP, TIM_OCM_PWM1);
timer_enable_oc_output(TIM1, TIM_CH_BOOTSTRAP);
timer_enable_oc_preload(TIM1, TIM_CH_BOOTSTRAP);
timer_set_oc_polarity_high(TIM1, TIM_CH_BOOTSTRAP);
timer_set_oc_value(TIM1, TIM_CH_CONV, 0); // no PWM by default
timer_set_oc_value(TIM1, TIM_CH_BOOTSTRAP, 0); // no PWM by default
// wanted: 50 kHz / 20 us period
// system clock: 48 MHz
// => 960 clock cycles / period = CONV_PWM_PERIOD
// prescaler
timer_set_prescaler(TIM1, 0); // Timer runs at system clock
// auto-reload value
timer_set_period(TIM1, CONV_PWM_PERIOD - 1);
// only generate interrupt on overflow
timer_update_on_overflow(TIM1);
// enable master output bit
timer_enable_break_main_output(TIM1);
// *** TIM3 ***
// used for the 1-millisecond system tick
timer_reset(TIM3);
timer_set_mode(TIM3, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
// prescaler
timer_set_prescaler(TIM3, 47); // -> 1 us counting at 48 MHz
// auto-reload value
timer_set_period(TIM3, 999); // -> update interrupt every 1 ms
// enable update interrupt (triggered on timer restart)
timer_enable_irq(TIM3, TIM_DIER_UIE);
nvic_enable_irq(NVIC_TIM3_IRQ);
// Start all the timers!
timer_enable_counter(TIM3);
timer_enable_counter(TIM1);
}
static void init_adc(void)
{
uint8_t channels[ADC_NUM_CHANNELS] = {
ADC_CHANNEL0, // VInSense
ADC_CHANNEL1, // VOutSense
ADC_CHANNEL2 // CurrentSense
};
adc_power_off(ADC1);
// configure ADC
//adc_enable_scan_mode(ADC1);
adc_set_single_conversion_mode(ADC1);
adc_set_resolution(ADC1, ADC_RESOLUTION_12BIT);
adc_set_sample_time_on_all_channels(ADC1, ADC_SMPR_SMP_071DOT5);
adc_disable_external_trigger_regular(ADC1);
adc_set_right_aligned(ADC1);
adc_set_regular_sequence(ADC1, ADC_NUM_CHANNELS, channels);
// configure DMA for ADC
//nvic_enable_irq(NVIC_DMA1_STREAM5_IRQ);
dma_channel_reset(DMA1, DMA_CHANNEL1);
dma_set_priority(DMA1, DMA_CHANNEL1, DMA_CCR_PL_LOW);
dma_set_memory_size(DMA1, DMA_CHANNEL1, DMA_CCR_MSIZE_16BIT);
dma_set_peripheral_size(DMA1, DMA_CHANNEL1, DMA_CCR_PSIZE_16BIT);
dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL1);
dma_enable_circular_mode(DMA1, DMA_CHANNEL1);
dma_set_read_from_peripheral(DMA1, DMA_CHANNEL1);
dma_set_peripheral_address(DMA1, DMA_CHANNEL1, (uint32_t) &ADC1_DR);
/* The array adc_values[] is filled with the waveform data to be output */
dma_set_memory_address(DMA1, DMA_CHANNEL1, (uint32_t) adc_values);
dma_set_number_of_data(DMA1, DMA_CHANNEL1, ADC_NUM_CHANNELS);
//dma_enable_transfer_complete_interrupt(DMA1, DMA_CHANNEL1);
dma_enable_channel(DMA1, DMA_CHANNEL1);
adc_enable_dma(ADC1);
// GO!
adc_power_on(ADC1);
}
static void deepsleep(uint32_t duration_secs)
{
uint32_t tmp = 0;
// unlock RTC registers
unlock_rtc_access();
// enter initialization mode
RTC_ISR |= RTC_ISR_INIT;
// wait until initialization mode has been entered
while((RTC_ISR & RTC_ISR_INITF) != RTC_ISR_INITF) {
// do nothing
}
RTC_TR = 0; // 00:00:00
RTC_DR = // friday, 01.01.16
(1 << RTC_DR_YT_SHIFT) |
(6 << RTC_DR_YU_SHIFT) |
(5 << RTC_DR_WDU_SHIFT) |
(0 << RTC_DR_MT_SHIFT) |
(1 << RTC_DR_MU_SHIFT) |
(0 << RTC_DR_DT_SHIFT) |
(1 << RTC_DR_DU_SHIFT);
// disable Alarm A
RTC_CR &= ~RTC_CR_ALRAE;
// wait until register is writeable
while((RTC_ISR & RTC_ISR_ALRAWF) != RTC_ISR_ALRAWF) {
// do nothing
}
tmp |= (duration_secs % 10) << RTC_ALRMXR_SU_SHIFT;
duration_secs /= 10;
tmp |= (duration_secs % 6) << RTC_ALRMXR_ST_SHIFT;
duration_secs /= 6;
tmp |= (duration_secs % 10) << RTC_ALRMXR_MNU_SHIFT;
duration_secs /= 10;
tmp |= (duration_secs % 6) << RTC_ALRMXR_MNT_SHIFT;
duration_secs /= 6;
tmp |= (duration_secs % 10) << RTC_ALRMXR_HU_SHIFT;
duration_secs /= 10;
tmp |= (duration_secs % 2) << RTC_ALRMXR_HT_SHIFT;
// FIXME: >1d is not supported
tmp |= RTC_ALRMXR_MSK4; // ignore day/date
// set alarm register
RTC_ALRMAR = tmp;
// clear Alarm A flag
RTC_ISR &= ~RTC_ISR_ALRAF;
// enable RTC alarm interrupt for wakeup
RTC_CR |= RTC_CR_ALRAE | RTC_CR_ALRAIE;
// leave initialization mode
RTC_ISR &= ~RTC_ISR_INIT;
// lock registers again (using invalid key)
lock_rtc_access();
// enter deep sleep mode
SCB_SCR |= SCB_SCR_SLEEPDEEP;
PWR_CR |= PWR_CR_LPDS; // voltage regulator low-power mode
pwr_set_stop_mode();
__WFI();
SCB_SCR &= ~SCB_SCR_SLEEPDEEP; // no deepsleep except in this function
init_clock();
}
#if 0
/* Set up timer to fire freq times per second */
static void init_systick(int freq)
{
systick_set_clocksource(STK_CSR_CLKSOURCE_AHB);
/* clear counter so it starts right away */
STK_CVR = 0;
systick_set_reload(rcc_ahb_frequency / freq);
systick_counter_enable();
systick_interrupt_enable();
}
#endif
int main(void)
{
uint16_t cpuload = 0;
uint64_t timebase_ms = 0;
uint32_t time_in_state = 0;
char msg[128];
char number[FXP_STR_MAXLEN];
uint8_t sentSomething = 0;
int32_t pwm = 0;
enum OperState operState = Bootstrap;
enum OperState nextState = ConvConstVoltage;
enum OperState lastState = operState;
fxp_t vin, vout, current;
fxp_t vin_avg = 0, vout_avg = 0, current_avg = 0;
fxp_t pErr = 0, iErr = 0;
fxp_t controlAction = 0;
fxp_t power_avg, refPower = 0;
fxp_t testPower[2];
int32_t testPWM[2];
int testPWMStep = 5;
uint32_t sleep_time = 10;
uint64_t force_display_update_time = 1000;
fxp_t PGAIN_CV = fxp_from_float( 50.000f);
fxp_t IGAIN_CV = fxp_from_float( 0.300f);
fxp_t IERR_LIMIT = fxp_from_int(4000);
fxp_t PGAIN_CC = fxp_from_float( 50.000f);
fxp_t IGAIN_CC = fxp_from_float( 0.300f);
fxp_t CURRENT_THRESHOLD = fxp_from_float(0.01f);
fxp_t AVG_FACT = fxp_from_float(0.10f);
fxp_t AVG_FACT_INV = fxp_sub(fxp_from_int(1), AVG_FACT);
fxp_t MAX_VOLTAGE = fxp_from_float(14.400f);
fxp_t CONST_VOLTAGE = fxp_from_float(13.800f);
fxp_t MAX_CURRENT = fxp_from_float( 5.000f);
fxp_t MPP_VOLTAGE_THR = fxp_sub(CONST_VOLTAGE, fxp_from_float(0.500f));
fxp_t MPP_CURRENT_THR = fxp_sub(MAX_CURRENT, fxp_from_float(0.500f));
// Calculated values
//fxp_t VIN_SCALE = fxp_from_float(3.3f * (100 + 12.4f) / 12.4f / 4095.0f);
//fxp_t VOUT_SCALE = fxp_from_float(3.3f * (100 + 12.0f) / 12.0f / 4095.0f);
//fxp_t CURRENT_SCALE = fxp_from_float(9.7f / 4095.0f);
// Calibrated from measurements
fxp_t VIN_SCALE = fxp_from_float(12.11f / 1600.0f);
fxp_t VOUT_SCALE = fxp_from_float(12.6f / 1620.0f);
fxp_t CURRENT_SCALE = fxp_from_float(9.01f / 4095.0f);
fxp_t CURRENT_OFFSET = fxp_from_float(0.049);
init_clock();
init_rtc();
init_gpio();
init_adc();
init_timer();
lcd_init();
debug_init();
debug_send_string("Init complete\r\n");
//init_systick(1000);
// triggered every 1 ms
while (1) {
// let the ADC+DMA do its work
adc_start_conversion_regular(ADC1);
// *** Do some calculations while ADC converts ***
if(lcd_setup()) {
lcd_process();
if(timebase_ms == force_display_update_time) {
lcd_set_cursor_pos(1, 0);
fxp_format(vin_avg, number, 1);
fxp_right_align(number, msg, 4, ' ');
lcd_send_string("I:");
lcd_send_string(msg);
lcd_send_string("V ");
fxp_format(vout_avg, number, 2);
fxp_right_align(number, msg, 5, ' ');
lcd_send_string("O:");
lcd_send_string(msg);
lcd_send_string("V ");
lcd_set_cursor_pos(0, 10);
fxp_format(power_avg, number, 2);
fxp_right_align(number, msg, 5, ' ');
lcd_send_string(msg);
lcd_send_string("W");
force_display_update_time += 500;
}
if((timebase_ms % 1000) == 10) {
cpuload /= 1000;
lcd_set_cursor_pos(0, 0);
fxp_format_int((int32_t)cpuload, number);
fxp_right_align(number, msg, 3, '0');
lcd_send_string("L:");
lcd_send_string(msg);
cpuload = 0;
}
}
// wait for DMA transfer to complete
while(!dma_get_interrupt_flag(DMA1, DMA_CHANNEL1, DMA_TCIF) && wait_frame);
dma_clear_interrupt_flags(DMA1, DMA_CHANNEL1, DMA_TCIF);
// convert read values
vin = fxp_mult(fxp_from_int(adc_values[0]), VIN_SCALE);
vout = fxp_mult(fxp_from_int(adc_values[1]), VOUT_SCALE);
current = fxp_mult(fxp_from_int(adc_values[2]), CURRENT_SCALE);
current = fxp_sub(current, CURRENT_OFFSET);
vin_avg = fxp_add(fxp_mult(vin, AVG_FACT), fxp_mult(vin_avg, AVG_FACT_INV));
vout_avg = fxp_add(fxp_mult(vout, AVG_FACT), fxp_mult(vout_avg, AVG_FACT_INV));
current_avg = fxp_add(fxp_mult(current, AVG_FACT), fxp_mult(current_avg, AVG_FACT_INV));
power_avg = fxp_mult(vout_avg, current_avg);
// Main FSM
if(timebase_ms >= 1000) {
switch(operState) {
case Bootstrap:
// enable bootstrap pulse with very low duty cycle
// disable converter
timer_set_oc_value(TIM1, TIM_CH_BOOTSTRAP, 48);
timer_set_oc_value(TIM1, TIM_CH_CONV, 0);
if(time_in_state >= 5) { // bootstrap duration in ms
iErr = fxp_div(IERR_LIMIT, fxp_from_int(2));
operState = nextState;
}
break;
case ConvConstVoltage:
// bootstrap off
timer_set_oc_value(TIM1, TIM_CH_BOOTSTRAP, 0);
// calculate error values
pErr = fxp_sub(CONST_VOLTAGE, vout_avg);
iErr = fxp_add(iErr, pErr);
// limit integral error range
if (iErr > IERR_LIMIT) iErr = IERR_LIMIT;
else if(iErr < -IERR_LIMIT) iErr = -IERR_LIMIT;
// calculate the controller output ("action")
controlAction = fxp_add(
fxp_mult(pErr, PGAIN_CV),
fxp_mult(iErr, IGAIN_CV));
pwm = fxp_to_int(controlAction);
if(pwm >= CONV_PWM_MAX) {
timer_set_oc_value(TIM1, TIM_CH_CONV, CONV_PWM_MAX);
} else if(pwm > 0) {
timer_set_oc_value(TIM1, TIM_CH_CONV, pwm);
} else {
timer_set_oc_value(TIM1, TIM_CH_CONV, 0);
}
if(time_in_state > 5000 && pwm > CONV_PWM_MAX && current < CURRENT_THRESHOLD) {
operState = Bootstrap;
nextState = ConvConstVoltage;
}
if(time_in_state > 1000 && vout_avg < MPP_VOLTAGE_THR) {
operState = ConvMPP;
}
if(current_avg > MAX_CURRENT) {
operState = ConvConstCurrent;
}
if(vin_avg < vout_avg) {
operState = Idle;
}
break;
case ConvConstCurrent:
// bootstrap off
timer_set_oc_value(TIM1, TIM_CH_BOOTSTRAP, 0);
// calculate error values
pErr = fxp_sub(MAX_CURRENT, current_avg);
iErr = fxp_add(iErr, pErr);
// limit integral error range
if (iErr > IERR_LIMIT) iErr = IERR_LIMIT;
else if(iErr < -IERR_LIMIT) iErr = -IERR_LIMIT;
// calculate the controller output ("action")
controlAction = fxp_add(
fxp_mult(pErr, PGAIN_CC),
fxp_mult(iErr, IGAIN_CC));
pwm = fxp_to_int(controlAction);
if(pwm >= CONV_PWM_MAX) {
timer_set_oc_value(TIM1, TIM_CH_CONV, CONV_PWM_MAX);
} else if(pwm > 0) {
timer_set_oc_value(TIM1, TIM_CH_CONV, pwm);
} else {
timer_set_oc_value(TIM1, TIM_CH_CONV, 0);
}
if(time_in_state > 5000 && pwm > CONV_PWM_MAX && current < CURRENT_THRESHOLD) {
operState = Bootstrap;
nextState = ConvConstCurrent;
}
if(time_in_state > 1000 && current_avg < MPP_CURRENT_THR) {
operState = ConvMPP;
}
if(vout_avg > MAX_VOLTAGE) {
operState = ConvConstVoltage;
}
break;
case ConvMPP:
// bootstrap off
timer_set_oc_value(TIM1, TIM_CH_BOOTSTRAP, 0);
if((time_in_state % 50) == 0) {
switch(testPWMStep) {
case 0:
case 2:
pwm = testPWM[testPWMStep/2];
timer_set_oc_value(TIM1, TIM_CH_CONV, pwm);
break;
case 1:
case 3:
testPower[testPWMStep/2] = power_avg;
break;
case 4:
if(testPower[1] > testPower[0]) {
refPower = testPower[1];
pwm = testPWM[1];
} else {
refPower = testPower[0];
pwm = testPWM[0];
}
if(pwm < (CONV_PWM_PERIOD/2) || pwm > CONV_PWM_MAX) {
pwm = 3 * CONV_PWM_PERIOD / 4;
}
timer_set_oc_value(TIM1, TIM_CH_CONV, pwm);
break;
case 5:
// power test is currently idle, but power has changed
// too much.
// -> start a new one.
testPWM[0] = pwm + 4;
testPWM[1] = pwm - 3;
testPWMStep = 0;
break;
default:
testPWMStep = 4;
break;
}
testPWMStep++;
}
if(time_in_state > 5000 && current < CURRENT_THRESHOLD) {
operState = Bootstrap;
nextState = ConvMPP;
}
if(vout_avg > MAX_VOLTAGE) {
operState = ConvConstVoltage;
}
if(current_avg > MAX_CURRENT) {
operState = ConvConstCurrent;
}
if(vin_avg < vout_avg) {
operState = Idle;
}
break;
case Idle:
// disable all PWMs
timer_set_oc_value(TIM1, TIM_CH_CONV, 0);
timer_set_oc_value(TIM1, TIM_CH_BOOTSTRAP, 0);
if(time_in_state > 1000 && vin_avg > vout_avg) {
sleep_time = 10;
operState = Bootstrap;
nextState = ConvMPP;
}
if(time_in_state > 10000) {
// not enough power for too long -> put system to deep sleep
lcd_set_cursor_pos(0, 0);
lcd_send_string("Sleep(");
fxp_format_int(sleep_time, msg);
lcd_send_string(msg);
lcd_send_string(") ");
while(lcd_process() == 0); // send everything immediately
deepsleep(sleep_time);
// Woke up again.
lcd_set_cursor_pos(0, 0);
lcd_send_string(" ");
time_in_state = 9900; // run the voltage test again
sleep_time *= 2;
if(sleep_time > MAX_SLEEP_TIME) {
sleep_time = MAX_SLEEP_TIME;
}
force_display_update_time = timebase_ms + 20;
}
break;
default:
debug_send_string("Invalid state detected!");
sentSomething = 1;
operState = Idle;
break;
}
if(operState != lastState) {
time_in_state = 0;
lastState = operState;
lcd_set_cursor_pos(0, 6);
switch(operState) {
case Idle:
lcd_send_string("IDL");
break;
case Bootstrap:
lcd_send_string("BTS");
break;
case ConvConstCurrent:
lcd_send_string("CC ");
break;
case ConvConstVoltage:
lcd_send_string("CV ");
break;
case ConvMPP:
lcd_send_string("MPP");
break;
default:
lcd_send_string("???");
break;
}
} else {
time_in_state++;
}
}
if((timebase_ms % 500) == 0) {
debug_send_string("ADC:");
for(uint8_t i = 0; i < ADC_NUM_CHANNELS; i++) {
fxp_format_int(adc_values[i], msg);
debug_send_string(" ");
debug_send_string(msg);
}
sentSomething = 1;
}
if((timebase_ms % 500) == 100) {
debug_send_string("pErr=");
fxp_format(pErr, msg, 3);
debug_send_string(msg);
debug_send_string("; iErr=");
fxp_format(iErr, msg, 3);
debug_send_string(msg);
debug_send_string("; action=");
fxp_format(controlAction, msg, 1);
debug_send_string(msg);
sentSomething = 1;
}
if((timebase_ms % 500) == 200) {
debug_send_string("power_avg=");
fxp_format(power_avg, msg, 2);
debug_send_string(msg);
debug_send_string("; ref=");
fxp_format(refPower, msg, 2);
debug_send_string(msg);
debug_send_string("; pwm=");
fxp_format_int(pwm, msg);
debug_send_string(msg);
sentSomething = 1;
}
if((timebase_ms % 500) == 300) {
debug_send_string("RTC seconds: ");
fxp_format_int((RTC_TR >> RTC_TR_SU_SHIFT) & RTC_TR_SU_MASK, msg);
debug_send_string(msg);
debug_send_string(" - ALRAF: ");
fxp_format_int(RTC_ISR & RTC_ISR_ALRAF, msg);
debug_send_string(msg);
debug_send_string(" - EXTI17: ");
fxp_format_int(exti_get_flag_status(EXTI17), msg);
debug_send_string(msg);
sentSomething = 1;
}
if(sentSomething) {
debug_send_string("\r\n");
sentSomething = 0;
}
if((timebase_ms % 1000) == 490) {
report_status(vin_avg, vout_avg, current_avg, pwm, operState);
}
// cpu load = timer1 value after main loop operations
cpuload += timer_get_counter(TIM3);
timebase_ms++;
while(wait_frame) {
__WFI();
}
wait_frame = 1;
}
return 0;
}
/* Called when systick fires */
void sys_tick_handler(void)
{
wait_frame = 0;
}
void tim3_isr(void)
{
// check for update interrupt
if(timer_interrupt_source(TIM3, TIM_SR_UIF)) {
wait_frame = 0;
timer_clear_flag(TIM3, TIM_SR_UIF);
}
}
void rtc_isr(void)
{
exti_reset_request(EXTI17);
}
void hard_fault_handler(void)
{
while (1);
}