#include #include #include #include #include #include #include #include #include #include #include #include #include #include "led_chplex.h" #include "rs485.h" #include "charge_pump.h" #include "power_switch.h" #include "measurement.h" volatile int wait_frame = 1; static void init_clock(void) { /* Set STM32 to 48 MHz. */ rcc_clock_setup_in_hse_8mhz_out_48mhz(); // generate 48 MHz from external 8 MHz crystal //rcc_clock_setup_in_hsi_out_48mhz(); // generate ~48 MHz from internal RC oscillator // enable TIM1 for PWM generation rcc_periph_clock_enable(RCC_TIM1); // enable GPIO clocks: // Port A is needed for the charge pump, extension port and analog input rcc_periph_clock_enable(RCC_GPIOA); // Port B is needed for the LEDs rcc_periph_clock_enable(RCC_GPIOB); // USART1 is used for RS485 rcc_periph_clock_enable(RCC_USART1); // ADC1 for analog measuremnts rcc_periph_clock_enable(RCC_ADC1); // DMA1 is used for ADC data transfer rcc_periph_clock_enable(RCC_DMA1); } /* Set up systick 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(); } static bool ledtest(uint64_t timebase_ms) { if(timebase_ms == 0) { led_chplex_mask(0x3F); // all on } else if(timebase_ms == 1000) { led_chplex_mask(0x01); } else if(timebase_ms == 1200) { led_chplex_mask(0x02); } else if(timebase_ms == 1400) { led_chplex_mask(0x04); } else if(timebase_ms == 1600) { led_chplex_mask(0x08); } else if(timebase_ms == 1800) { led_chplex_mask(0x10); } else if(timebase_ms == 2000) { led_chplex_mask(0x20); } else if(timebase_ms == 2200) { led_chplex_mask(0x10); } else if(timebase_ms == 2400) { led_chplex_mask(0x08); } else if(timebase_ms == 2600) { led_chplex_mask(0x04); } else if(timebase_ms == 2800) { led_chplex_mask(0x02); } else if(timebase_ms == 3000) { led_chplex_mask(0x01); } else if(timebase_ms == 3200) { led_chplex_mask(0x00); return true; } return false; } static void report_status(struct MeasurementResult *meas_data) { char number[FXP_STR_MAXLEN]; rs485_enqueue("MEAS:"); fxp_format(meas_data->u_bat, number, 3); rs485_enqueue(number); rs485_enqueue(":"); fxp_format(meas_data->u_solar, number, 3); rs485_enqueue(number); rs485_enqueue(":"); fxp_format(meas_data->u_sw, number, 3); rs485_enqueue(number); rs485_enqueue(":"); fxp_format(meas_data->i_solar, number, 3); rs485_enqueue(number); rs485_enqueue(":"); fxp_format(meas_data->i_load, number, 3); rs485_enqueue(number); rs485_enqueue(":"); fxp_format(meas_data->temperature, number, 2); rs485_enqueue(number); rs485_enqueue("\n"); } int main(void) { //uint32_t cpuload = 0; uint64_t timebase_ms = 0; bool ledtest_done = false; bool startup_done = false; uint8_t switchtest = 0; // FIXME: delme: just for testing struct MeasurementResult meas_data; init_clock(); rs485_init(); charge_pump_init(); power_switch_init(); measurement_init(); led_chplex_init(); led_chplex_on(LED_CHPLEX_IDX_SOLAR_ON); init_systick(1000); rs485_enqueue("LNSC-2420 v" VERSION " initialized.\n"); // triggered every 1 ms while (1) { if(!ledtest_done) { ledtest_done = ledtest(timebase_ms); led_chplex_periodic(); } else if(!startup_done) { charge_pump_start(); power_switch_load_on(); // FIXME: just for testing! startup_done = true; } else { measurement_start(); // measurement takes some time, so do other things before waiting for // completion. This is a good place for tasks that are not critical in // latency, such as updating the LEDs, sending the state over RS485 etc. if(timebase_ms % 500 == 0) { led_chplex_toggle(LED_CHPLEX_IDX_DISCHARGE_PULSE); } // FIXME: just for testing if(timebase_ms % 10000 == 0) { switchtest++; if(switchtest & 0x01) { power_switch_solar_on(); led_chplex_on(LED_CHPLEX_IDX_SOLAR_ON); } else { power_switch_solar_off(); led_chplex_off(LED_CHPLEX_IDX_SOLAR_ON); } if(switchtest & 0x02) { power_switch_load_on(); led_chplex_on(LED_CHPLEX_IDX_LOAD_ON); } else { power_switch_load_off(); led_chplex_off(LED_CHPLEX_IDX_LOAD_ON); } } led_chplex_periodic(); // Send the status data from the last cycle. if(timebase_ms % 1000 == 0) { report_status(&meas_data); } measurement_wait_for_completion(); if(timebase_ms % 1000 == 100) { measurement_finalize(&meas_data); } // Check the protections directly after the measurement finishes. This // ensures fast reaction time. // TODO: check_protections(&meas_data); } timebase_ms++; while(wait_frame) { __WFI(); } wait_frame = 1; } return 0; } /* Called when systick fires */ void sys_tick_handler(void) { wait_frame = 0; } void hard_fault_handler(void) { while (1); }