From ec28fcd9f905358759eea98161f451567135d17e Mon Sep 17 00:00:00 2001 From: jaseg Date: Sun, 27 Aug 2023 22:31:09 +0200 Subject: new driver blinkenlights --- driver_fw/main.c | 361 ------------------------------------------------------- 1 file changed, 361 deletions(-) delete mode 100644 driver_fw/main.c (limited to 'driver_fw/main.c') diff --git a/driver_fw/main.c b/driver_fw/main.c deleted file mode 100644 index f02c177..0000000 --- a/driver_fw/main.c +++ /dev/null @@ -1,361 +0,0 @@ -/* 8seg LED display driver firmware - * Copyright (C) 2018 Sebastian Götte - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - -#include "global.h" -#include "serial.h" -#include "i2c.h" -#include "lcd1602.h" -#include "mcp9801.h" -#include "ina226.h" - -#include "mini-printf.h" - -#include <8b10b.h> - -/* Part number: STM32F030F4C6 */ - -volatile unsigned int comm_led_ctr, err_led_ctr; -volatile unsigned int sys_time_tick = 0; -volatile unsigned int sys_time_ms; -volatile unsigned int sys_time_s; -volatile unsigned int sys_flag_1Hz; -unsigned int frame_duration_us; -volatile uint8_t global_brightness; /* FIXME implement sending */ - -void trigger_error_led() { - err_led_ctr = STATUS_LED_DURATION_MS/TICK_MS; -} - -void trigger_comm_led() { - comm_led_ctr = STATUS_LED_DURATION_MS/TICK_MS; -} - -static volatile struct { - int current_symbol; - struct state_8b10b_enc st; -} txstate; - -#define NO_SYMBOL (DECODER_RETURN_CODE_LAST + 1) - -uint8_t random() { - static uint8_t x, a, b, c; - x++; //x is incremented every round and is not affected by any other variable - a = (a ^ c ^ x); //note the mix of addition and XOR - b = (b + a); //And the use of very few instructions - c = ((c + ((b >> 1) ^ a))); // the AES S-Box Operation ensures an even distributon of entropy - return c; -} - -enum STATUS_LEDS { - STATUS_LED_COMMUNICATION = 1, - STATUS_LED_ERROR = 2, - STATUS_LED_LOAD = 4, - STATUS_LED_OPERATION = 8, - STATUS_LED_J5_GREEN = 16, - STATUS_LED_J5_YELLOW = 32, - STATUS_LED_J4_GREEN = 64, - STATUS_LED_J4_YELLOW = 128 -}; - -static void set_status_leds(uint8_t val) { - /* Reset strobe. Will be set in systick handler */ - GPIOA->BRR = 1<<4; - /* Workaround for *nasty* hardware behavior: If SPI data width is configured as 8 bit but DR is written as 16 - * bit, SPI actually sends 16 clock cycles. Thus, we have to make sure the compiler emits a 8-bit write here. - * Thanks, TI! */ - *((volatile uint8_t *)&(SPI1->DR)) = val ^ 0x0f; /* Invert LEDs connected to VCC instead of GND */ -} - -static int flipbits10(int in) { - return - (in&0x200)>>9 | - (in&0x100)>>7 | - (in&0x080)>>5 | - (in&0x040)>>3 | - (in&0x020)>>1 | - (in&0x010)<<1 | - (in&0x008)<<3 | - (in&0x004)<<5 | - (in&0x002)<<7 | - (in&0x001)<<9; - -} - -uint8_t spinner = 0; /* FIXME DEBUG */ - -int main(void) { - /* Startup code */ - RCC->CR |= RCC_CR_HSEON; - while (!(RCC->CR&RCC_CR_HSERDY)); - RCC->CFGR &= ~RCC_CFGR_PLLMUL_Msk & ~RCC_CFGR_SW_Msk & ~RCC_CFGR_PPRE_Msk & ~RCC_CFGR_HPRE_Msk; - RCC->CFGR |= ((6-2)< 48.0MHz */ - RCC->CR |= RCC_CR_PLLON; - while (!(RCC->CR&RCC_CR_PLLRDY)); - RCC->CFGR |= (2<AHBENR |= RCC_AHBENR_DMAEN | RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_FLITFEN; - RCC->APB1ENR |= RCC_APB1ENR_TIM3EN | RCC_APB1ENR_PWREN | RCC_APB1ENR_I2C1EN; - RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN | RCC_APB2ENR_ADCEN| RCC_APB2ENR_DBGMCUEN | RCC_APB2ENR_USART1EN | RCC_APB2ENR_SPI1EN; - - SystemCoreClockUpdate(); - SysTick_Config(SystemCoreClock/(1000/TICK_MS)); /* 10ms interval */ - NVIC_EnableIRQ(SysTick_IRQn); - NVIC_SetPriority(SysTick_IRQn, 3<<5); - - /* GPIO setup - * - * Note: since we have quite a bunch of pin constraints we can't actually use complementary outputs for the - * complementary MOSFET driver control signals (CTRL_A & CTRL_B). Instead, we use two totally separate output - * channels (1 & 4) and emulate the dead-time generator in software. - */ - GPIOA->MODER |= - (3<AFR[0] = - (1<AFR[1] = - (4<ODR = 0; /* Set PA4 ODR to 0 */ - - GPIOA->OTYPER |= - GPIO_OTYPER_OT_1 - | GPIO_OTYPER_OT_2; - - // FIXME lag 37.3us @ 720 Ohm / 16.0us @ 360 Ohm / 2.8us @ 88 Ohm - GPIOA->OSPEEDR |= - (3<MODER |= - (2<AFR[0] = (1<CR2 = (7< 375.0kHz */ - SPI1->CR1 = - SPI_CR1_SSM - | SPI_CR1_SSI - | (6<CR1 |= SPI_CR1_SPE; - - /* I2C for LCD, temp sensor, current sensor */ - i2c_config_filters(I2C1, I2C_AF_ENABLE, 0); - i2c_config_timing(I2C1, 0x2000090e); /* Magic value for 100kHz I2C @ 48MHz CLK. Fell out of STMCubeMX. I love - downloading 120MB of software to download another 100MB of software, only - this time over unsecured HTTP, to generate 3.5 bytes of configuration values - using a Java(TM) GUI. */ - i2c_enable(I2C1); - lcd1602_init(); - ina226_init(); /* Current/voltage monitor */ - mcp9801_init(); /* MOSFET temperature. Placed between middle two low-side MOSFETs. */ - - /* TIM3 is used to generate the MOSFET driver control signals */ - /* TIM3 running off 48MHz APB1 clk, T=20.833ns */ - TIM3->CR1 = 0; /* Disable ARR preload (double-buffering) */ - TIM3->PSC = 48-1; /* Prescaler 48 -> f=1MHz/T=1us */ - TIM3->DIER = TIM_DIER_UIE; /* Enable update (overflow) interrupt */ - - /* Set both CCRs to 0xffff to ensure both bridge halves are turned off after we enable the timer. If we don't do - * this, we will cause a very low-ohm short circuit that at best will trigger our power supply's short-circuit or - * over-current protection right after power-on but at worst will detonate the mosfets. */ - TIM3->CCR1 = 0xffff; - TIM3->CCR4 = 0xffff; - /* Configure output compare unit 1 to PWM mode 1, enable CCR1 preload */ - TIM3->CCMR1 = 6<CCMR2 = 6<CCER = TIM_CCER_CC1E | TIM_CCER_CC1P | TIM_CCER_CC4E | TIM_CCER_CC4P; - /* Enable MOE on next update event, i.e. on initial timer load. */ - TIM3->BDTR = TIM_BDTR_MOE; - /* Enable timer */ - TIM3->CR1 |= TIM_CR1_CEN; - /* Set f=1.25kHz/T=0.8ms */ - TIM3->ARR = 800-1; - - /* Initialize AC protocol state machine in TIM3 ISR with the AC protocol comma */ - xfr_8b10b_encode_reset(&txstate.st); - txstate.current_symbol = flipbits10(xfr_8b10b_encode(&txstate.st, K28_1)) | 1<<10; - /* The timer is still stopped. Start it by manually triggering an update event. */ - TIM3->EGR |= TIM_EGR_UG; - - NVIC_EnableIRQ(TIM3_IRQn); - NVIC_SetPriority(TIM3_IRQn, 2<<4); - - lcd_write_str(0, 0, "8seg driver"); - lcd_write_str(0, 1, "initialized \xbc"); - while (42) { - if (sys_flag_1Hz) { /* Update display every second */ - sys_flag_1Hz = 0; - spinner = ~spinner; - - char buf[17]; - int temp = mcp9801_read_mdegC(); - int deg = temp/1000; - int frac = (temp%1000)/100; - mini_snprintf(buf, sizeof(buf), "Temp: %d.%01d\xdf""C" LCD_FILL, deg, frac); - lcd_write_str(0, 0, buf); - mini_snprintf(buf, sizeof(buf), "I=%dmA U=%dmV" LCD_FILL, ina226_read_i()*INA226_I_LSB_uA/1000, ina226_read_v()*INA226_VB_LSB_uV/1000); - lcd_write_str(0, 1, buf); - } - } -} - -#define BACKCHANNEL_INTERVAL 10 - -__attribute__((__noreturn__)) void __assert_func (const char *, int, const char *, const char *){ - asm volatile ("bkpt"); - while (1); -} - -int hamming_weight(int i) { - int r = 0; - for (int j = 0; j < 32; j ++) { - if (i < 0) r ++; - i <<= 1; - } - return r; -} - -void TIM3_IRQHandler() { - static int txpos = -1; - static unsigned int tx_start_tick = 0; - static uint8_t txbuf[3] = {0x05, 0x01, 0}; - static int backchannel_counter = 0; - txbuf[2] = spinner; - - TIM3->SR &= ~TIM_SR_UIF; - int sym = txstate.current_symbol; - int bit = sym&1; - sym >>= 1; - if (sym == 1) { /* last bit shifted out */ - - /* Insert the backchannel sync control symbol K.28.2 once every BACKCHANNEL_INTERVAL symbols independent from AC - * forward channel protocol framing. The backchannel sync control symbol is different from the AC protocol comma - * K.28.1. The backchannel sync control symbol is not a comma, so the 8b10b receiver cannot lock on it. The only - * practical implication of this is that after powerup or other loss of sync, the receiver will only lock on the - * backchannel sync once the first AC forward-channel protocol frame has been begun. Since all backchannel comm - * is triggered by the driver anyway this should not be noticeable in practice. - */ - backchannel_counter++; - if (backchannel_counter == BACKCHANNEL_INTERVAL) { - backchannel_counter = 0; - sym = xfr_8b10b_encode(&txstate.st, -K28_2); /* TODO factor out backchannel comma into constant */ - - } else { - - if (txpos == -1) - sym = xfr_8b10b_encode(&txstate.st, -K28_1); /* TODO factor out comma into constant */ - else - sym = xfr_8b10b_encode(&txstate.st, txbuf[txpos]); - - txpos++; - if (txpos >= sizeof(txbuf)/sizeof(txbuf[0])) { - frame_duration_us = (sys_time_tick - tx_start_tick) * 10 * 1000; - tx_start_tick = sys_time_tick; - txpos = -1; - } - } - - /* Append one '1' bit as an end-of-symbol marker for this state machine. This bit is not actually transmitted. */ - sym = flipbits10(sym) | 1<<10; - } - txstate.current_symbol = sym; - - /* FIXME factor out into header, or even make configurable */ -#define DEAD_TIME 1 - /* Set both CCRs to values for opposing polarities. The dead time is always inserted at the beginning of the timer - * cycle due to the way the capture/compare unit PWM machinery works. By setting the CCR to 0xffff we make sure the - * output is never turned on, since 0xffff is larger than the ARR/counter top value. - */ - TIM3->CCR1 = bit ? 0xffff : DEAD_TIME; - TIM3->CCR4 = bit ? DEAD_TIME : 0xffff; -} - -void NMI_Handler(void) { -} - -void HardFault_Handler(void) __attribute__((naked)); -void HardFault_Handler() { - asm volatile ("bkpt"); -} - -void SVC_Handler(void) { -} - - -void PendSV_Handler(void) { -} - -void SysTick_Handler(void) { - sys_time_tick++; - sys_time_ms += TICK_MS; - if (sys_time_ms++ == 1000) { - sys_time_ms = 0; - sys_time_s++; - sys_flag_1Hz = 1; - } - - /* This is a hack. We could use the SPI interrupt here if that didn't fire at the start instead of end of transmission.... -.- */ - if (sys_time_tick&1) { - uint8_t val = (sys_time_ms >= 300) ? STATUS_LED_OPERATION : 0; - - if (comm_led_ctr) { - comm_led_ctr--; - val |= STATUS_LED_COMMUNICATION; - } - - if (err_led_ctr) { - err_led_ctr--; - val |= STATUS_LED_ERROR; - } - - set_status_leds(val); - } else { - /* Reset strobe for the status LED shift register. Reset in set_status_leds. */ - GPIOA->BSRR = 1<<4; - } -} - -void _init(void) { -} - -void BusFault_Handler(void) __attribute__((naked)); -void BusFault_Handler() { - asm volatile ("bkpt"); -} -- cgit