diff --git a/F1:F103/FX3U/Makefile b/F1:F103/FX3U/Makefile new file mode 100644 index 0000000..865c9a7 --- /dev/null +++ b/F1:F103/FX3U/Makefile @@ -0,0 +1,10 @@ +BINARY := fx3u +# MCU code +MCU ?= F103xC +# change this linking script depending on particular MCU model, +LDSCRIPT ?= stm32f103xC.ld +DEFINES := -DSTM32F10X_HD +INC_DIR := ../inc + +include ../makefile.f1 +include ../../makefile.stm32 diff --git a/F1:F103/FX3U/adc.c b/F1:F103/FX3U/adc.c new file mode 100644 index 0000000..ee10071 --- /dev/null +++ b/F1:F103/FX3U/adc.c @@ -0,0 +1,110 @@ +/* + * This file is part of the fx3u project. + * Copyright 2024 Edward V. Emelianov . + * + * 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 "adc.h" + +uint16_t ADC_array[NUMBER_OF_ADC_CHANNELS*9]; + +void adc_setup(){ + uint32_t ctr = 0; + // Enable clocking + RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; + RCC->AHBENR |= RCC_AHBENR_DMA1EN; + __DSB(); + // DMA configuration + DMA1_Channel1->CPAR = (uint32_t) (&(ADC1->DR)); + DMA1_Channel1->CMAR = (uint32_t)(ADC_array); + DMA1_Channel1->CNDTR = NUMBER_OF_ADC_CHANNELS * 9; + DMA1_Channel1->CCR = DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0 + | DMA_CCR_CIRC | DMA_CCR_PL | DMA_CCR_EN; + RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_ADCPRE)) | RCC_CFGR_ADCPRE_DIV8; // ADC clock = RCC / 8 + // sampling time - 239.5 cycles for channels 0, 16 and 17 + ADC1->SMPR2 = ADC_SMPR2_SMP0; + ADC1->SMPR1 = ADC_SMPR1_SMP16 | ADC_SMPR1_SMP17; + // sequence order: 0 -> 16 -> 17 + ADC1->SQR3 = (0 << 0) | (16<<5) | (17 << 10); + ADC1->SQR1 = (NUMBER_OF_ADC_CHANNELS - 1) << 20; // amount of conversions + ADC1->CR1 = ADC_CR1_SCAN; // scan mode + // DMA, continuous mode; enable vref & Tsens; enable SWSTART as trigger + ADC1->CR2 = ADC_CR2_DMA | ADC_CR2_TSVREFE | ADC_CR2_CONT | ADC_CR2_EXTSEL | ADC_CR2_EXTTRIG; + // wake up ADC + ADC1->CR2 |= ADC_CR2_ADON; + __DSB(); + // wait for Tstab - at least 1us + while(++ctr < 0xff) nop(); + // calibration + ADC1->CR2 |= ADC_CR2_RSTCAL; + ctr = 0; while((ADC1->CR2 & ADC_CR2_RSTCAL) && ++ctr < 0xfffff); + ADC1->CR2 |= ADC_CR2_CAL; + ctr = 0; while((ADC1->CR2 & ADC_CR2_CAL) && ++ctr < 0xfffff); + // clear possible errors and start + ADC1->SR = 0; + ADC1->CR2 |= ADC_CR2_SWSTART; +} + + +/** + * @brief getADCval - calculate median value for `nch` channel + * @param nch - number of channel + * @return + */ +uint16_t getADCval(int nch){ + int i, addr = nch; +#define PIX_SORT(a,b) { if ((a)>(b)) PIX_SWAP((a),(b)); } +#define PIX_SWAP(a,b) { register uint16_t temp=(a);(a)=(b);(b)=temp; } + uint16_t p[9]; + for(i = 0; i < 9; ++i, addr += NUMBER_OF_ADC_CHANNELS){ // first we should prepare array for optmed + p[i] = ADC_array[addr]; + } + PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ; + PIX_SORT(p[0], p[1]) ; PIX_SORT(p[3], p[4]) ; PIX_SORT(p[6], p[7]) ; + PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ; + PIX_SORT(p[0], p[3]) ; PIX_SORT(p[5], p[8]) ; PIX_SORT(p[4], p[7]) ; + PIX_SORT(p[3], p[6]) ; PIX_SORT(p[1], p[4]) ; PIX_SORT(p[2], p[5]) ; + PIX_SORT(p[4], p[7]) ; PIX_SORT(p[4], p[2]) ; PIX_SORT(p[6], p[4]) ; + PIX_SORT(p[4], p[2]) ; + return p[4]; +#undef PIX_SORT +#undef PIX_SWAP +} + +// get voltage @input nch (1/100V) +uint32_t getADCvoltage(int nch){ + uint32_t v = getADCval(nch); + v *= getVdd(); + v /= 0xfff; // 12bit ADC + return v; +} + +// return MCU temperature (degrees of celsius * 10) +int32_t getMCUtemp(){ + // Temp = (V25 - Vsense)/Avg_Slope + 25 + // V_25 = 1.45V, Slope = 4.3e-3 + int32_t Vsense = getVdd() * getADCval(CHTSENS); + int32_t temperature = 593920 - Vsense; // 593920 == 145*4096 + temperature /= 172; // == /(4096*10*4.3e-3), 10 - to convert from *100 to *10 + temperature += 250; + return(temperature); +} + +// return Vdd * 100 (V) +uint32_t getVdd(){ + uint32_t vdd = 120 * 4096; // 1.2V + vdd /= getADCval(CHVDD); + return vdd; +} diff --git a/F1:F103/FX3U/adc.h b/F1:F103/FX3U/adc.h new file mode 100644 index 0000000..b3539cd --- /dev/null +++ b/F1:F103/FX3U/adc.h @@ -0,0 +1,44 @@ +/* + * This file is part of the fx3u project. + * Copyright 2024 Edward V. Emelianov . + * + * 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 . + */ + +#pragma once + +#include + +#define NUMBER_OF_ADC_CHANNELS (3) + +// Vsen voltage channel +#define CHVSEN (0) +// channels for Tsens and Vdd +#define CHTSENS (1) +#define CHVDD (2) + +/** + * @brief ADC_array - array for ADC channels with median filtering: + * 0 - Isensor voltage + * 1 - internal Tsens + * 2 - Vdd + */ +extern uint16_t ADC_array[]; + +void adc_setup(); +int32_t getMCUtemp(); +uint32_t getVdd(); +uint16_t getADCval(int nch); +uint32_t getADCvoltage(int nch); + diff --git a/F1:F103/FX3U/can.c b/F1:F103/FX3U/can.c new file mode 100644 index 0000000..ff837d4 --- /dev/null +++ b/F1:F103/FX3U/can.c @@ -0,0 +1,296 @@ +/* + * This file is part of the fx3u project. + * Copyright 2024 Edward V. Emelianov . + * + * 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 "can.h" +#include "hardware.h" +#include "proto.h" +#include "usart.h" + +// REMAPPED to PD0/PD1!!! + +#include // memcpy + +// circular buffer for received messages +static CAN_message messages[CAN_INMESSAGE_SIZE]; +static uint8_t first_free_idx = 0; // index of first empty cell +static int8_t first_nonfree_idx = -1; // index of first data cell +static uint16_t oldspeed = 100; // speed of last init + +static CAN_status can_status = CAN_STOP; + +static void can_process_fifo(uint8_t fifo_num); + +CAN_status CAN_get_status(){ + int st = can_status; + can_status = CAN_OK; + return st; +} + +// push next message into buffer; return 1 if buffer overfull +static int CAN_messagebuf_push(CAN_message *msg){ + //MSG("Try to push\n"); +#ifdef EBUG + usart_send("push\n"); +#endif + if(first_free_idx == first_nonfree_idx){ +#ifdef EBUG + usart_send("INBUF OVERFULL\n"); +#endif + return 1; // no free space + } + if(first_nonfree_idx < 0) first_nonfree_idx = 0; // first message in empty buffer + memcpy(&messages[first_free_idx++], msg, sizeof(CAN_message)); + // need to roll? + if(first_free_idx == CAN_INMESSAGE_SIZE) first_free_idx = 0; + return 0; +} + +// pop message from buffer +CAN_message *CAN_messagebuf_pop(){ + if(first_nonfree_idx < 0) return NULL; + CAN_message *msg = &messages[first_nonfree_idx++]; + if(first_nonfree_idx == CAN_INMESSAGE_SIZE) first_nonfree_idx = 0; + if(first_nonfree_idx == first_free_idx){ // buffer is empty - refresh it + first_nonfree_idx = -1; + first_free_idx = 0; + } + return msg; +} + +void CAN_reinit(uint16_t speed){ + CAN1->TSR |= CAN_TSR_ABRQ0 | CAN_TSR_ABRQ1 | CAN_TSR_ABRQ2; + RCC->APB1RSTR |= RCC_APB1RSTR_CAN1RST; + RCC->APB1RSTR &= ~RCC_APB1RSTR_CAN1RST; + CAN_setup(speed); +} + +/* +Can filtering: FSCx=0 (CAN1->FS1R) -> 16-bit identifiers +MASK: FBMx=0 (CAN1->FM1R), two filters (n in FR1 and n+1 in FR2) + ID: CAN1->sFilterRegister[x].FRn[0..15] + MASK: CAN1->sFilterRegister[x].FRn[16..31] + FR bits: STID[10:0] RTR IDE EXID[17:15] +LIST: FBMx=1, four filters (n&n+1 in FR1, n+2&n+3 in FR2) + IDn: CAN1->sFilterRegister[x].FRn[0..15] + IDn+1: CAN1->sFilterRegister[x].FRn[16..31] +*/ + +/* +Can timing: main freq - APB (PLL=48MHz) +segment = 1sync + TBS1 + TBS2, sample point is between TBS1 and TBS2, +so if TBS1=4 and TBS2=3, sum=8, bit sampling freq is 48/8 = 6MHz +-> to get 100kbps we need prescaler=60 + 250kbps - 24 + 500kbps - 12 + 1MBps - 6 +*/ + +// speed - in kbps +void CAN_setup(uint16_t speed){ + if(speed == 0) speed = oldspeed; + else if(speed < 50) speed = 50; + else if(speed > 3000) speed = 3000; + oldspeed = speed; + uint32_t tmout = 16000000; + // Configure GPIO: PD0 - CAN_Rx, PD1 - CAN_Tx + AFIO->MAPR |= AFIO_MAPR_CAN_REMAP_REMAP3; + GPIOD->CRL = (GPIOD->CRL & ~(CRL(0,0xf)|CRL(1,0xf))) | + CRL(0, CNF_FLINPUT | MODE_INPUT) | CRL(1, CNF_AFPP | MODE_NORMAL); + /* Enable the peripheral clock CAN */ + RCC->APB1ENR |= RCC_APB1ENR_CAN1EN; + /* Configure CAN */ + /* (1) Enter CAN init mode to write the configuration */ + /* (2) Wait the init mode entering */ + /* (3) Exit sleep mode */ + /* (4) Normal mode, set timing to 100kb/s: TBS1 = 4, TBS2 = 3, prescaler = 60 */ + /* (5) Leave init mode */ + /* (6) Wait the init mode leaving */ + /* (7) Enter filter init mode, (16-bit + mask, bank 0 for FIFO 0) */ + /* (8) Acivate filter 0 for two IDs */ + /* (9) Identifier list mode */ + /* (10) Set the Id list */ + /* (12) Leave filter init */ + /* (13) Set error interrupts enable (& bus off) */ + CAN1->MCR |= CAN_MCR_INRQ; /* (1) */ + while((CAN1->MSR & CAN_MSR_INAK) != CAN_MSR_INAK) /* (2) */ + if(--tmout == 0) break; + CAN1->MCR &=~ CAN_MCR_SLEEP; /* (3) */ + CAN1->MCR |= CAN_MCR_ABOM; /* allow automatically bus-off */ + + CAN1->BTR = 2 << 20 | 3 << 16 | (4500/speed - 1); //| CAN_BTR_SILM | CAN_BTR_LBKM; /* (4) */ + CAN1->MCR &= ~CAN_MCR_INRQ; /* (5) */ + tmout = 16000000; + while((CAN1->MSR & CAN_MSR_INAK) == CAN_MSR_INAK) /* (6) */ + if(--tmout == 0) break; + // accept ALL + CAN1->FMR = CAN_FMR_FINIT; /* (7) */ + CAN1->FA1R = CAN_FA1R_FACT0 | CAN_FA1R_FACT1; /* (8) */ + // set to 1 all needed bits of CAN1->FFA1R to switch given filters to FIFO1 + CAN1->sFilterRegister[0].FR1 = (1<<21)|(1<<5); // all odd IDs + CAN1->FFA1R = 2; // filter 1 for FIFO1, filter 0 - for FIFO0 + CAN1->sFilterRegister[1].FR1 = (1<<21); // all even IDs + CAN1->FMR &= ~CAN_FMR_FINIT; /* (12) */ + CAN1->IER |= CAN_IER_ERRIE | CAN_IER_FOVIE0 | CAN_IER_FOVIE1 | CAN_IER_BOFIE; /* (13) */ + + /* Configure IT */ + NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 0); // RX FIFO0 IRQ + NVIC_SetPriority(CAN1_RX1_IRQn, 0); // RX FIFO1 IRQ + NVIC_SetPriority(CAN1_SCE_IRQn, 0); // RX status changed IRQ + NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn); + NVIC_EnableIRQ(CAN1_RX1_IRQn); + NVIC_EnableIRQ(CAN1_SCE_IRQn); + CAN1->MSR = 0; // clear SLAKI, WKUI, ERRI + can_status = CAN_READY; +} + +void can_proc(){ + // check for messages in FIFO0 & FIFO1 + if(CAN1->RF0R & CAN_RF0R_FMP0){ + can_process_fifo(0); + } + if(CAN1->RF1R & CAN_RF1R_FMP1){ + can_process_fifo(1); + } + IWDG->KR = IWDG_REFRESH; + if(CAN1->ESR & (CAN_ESR_BOFF | CAN_ESR_EPVF | CAN_ESR_EWGF)){ // much errors - restart CAN BUS + usart_send("\nToo much errors, restarting CAN!\n"); + // request abort for all mailboxes + CAN1->TSR |= CAN_TSR_ABRQ0 | CAN_TSR_ABRQ1 | CAN_TSR_ABRQ2; + // reset CAN bus + RCC->APB1RSTR |= RCC_APB1RSTR_CAN1RST; + RCC->APB1RSTR &= ~RCC_APB1RSTR_CAN1RST; + CAN_setup(0); + } +} + +CAN_status can_send(uint8_t *msg, uint8_t len, uint16_t target_id){ + uint8_t mailbox = 0; + // check first free mailbox + if(CAN1->TSR & (CAN_TSR_TME)){ + mailbox = (CAN1->TSR & CAN_TSR_CODE) >> 24; + }else{ // no free mailboxes + return CAN_BUSY; + } + CAN_TxMailBox_TypeDef *box = &CAN1->sTxMailBox[mailbox]; + uint32_t lb = 0, hb = 0; + switch(len){ + case 8: + hb |= (uint32_t)msg[7] << 24; + __attribute__((fallthrough)); + case 7: + hb |= (uint32_t)msg[6] << 16; + __attribute__((fallthrough)); + case 6: + hb |= (uint32_t)msg[5] << 8; + __attribute__((fallthrough)); + case 5: + hb |= (uint32_t)msg[4]; + __attribute__((fallthrough)); + case 4: + lb |= (uint32_t)msg[3] << 24; + __attribute__((fallthrough)); + case 3: + lb |= (uint32_t)msg[2] << 16; + __attribute__((fallthrough)); + case 2: + lb |= (uint32_t)msg[1] << 8; + __attribute__((fallthrough)); + default: + lb |= (uint32_t)msg[0]; + } + box->TDLR = lb; + box->TDHR = hb; + box->TDTR = len; + box->TIR = (target_id & 0x7FF) << 21 | CAN_TI0R_TXRQ; + return CAN_OK; +} + +static void can_process_fifo(uint8_t fifo_num){ + if(fifo_num > 1) return; + CAN_FIFOMailBox_TypeDef *box = &CAN1->sFIFOMailBox[fifo_num]; + volatile uint32_t *RFxR = (fifo_num) ? &CAN1->RF1R : &CAN1->RF0R; + // read all + while(*RFxR & CAN_RF0R_FMP0){ // amount of messages pending + // CAN_RDTxR: (16-31) - timestamp, (8-15) - filter match index, (0-3) - data length + /* TODO: check filter match index if more than one ID can receive */ + CAN_message msg; + uint8_t *dat = msg.data; + uint8_t len = box->RDTR & 0x0f; + msg.length = len; + msg.ID = box->RIR >> 21; + //msg.filterNo = (box->RDTR >> 8) & 0xff; + //msg.fifoNum = fifo_num; + if(len){ // message can be without data + uint32_t hb = box->RDHR, lb = box->RDLR; + switch(len){ + case 8: + dat[7] = hb>>24; + __attribute__((fallthrough)); + case 7: + dat[6] = (hb>>16) & 0xff; + __attribute__((fallthrough)); + case 6: + dat[5] = (hb>>8) & 0xff; + __attribute__((fallthrough)); + case 5: + dat[4] = hb & 0xff; + __attribute__((fallthrough)); + case 4: + dat[3] = lb>>24; + __attribute__((fallthrough)); + case 3: + dat[2] = (lb>>16) & 0xff; + __attribute__((fallthrough)); + case 2: + dat[1] = (lb>>8) & 0xff; + __attribute__((fallthrough)); + case 1: + dat[0] = lb & 0xff; + } + } + if(CAN_messagebuf_push(&msg)) return; // error: buffer is full, try later + *RFxR |= CAN_RF0R_RFOM0; // release fifo for access to next message + } + //if(*RFxR & CAN_RF0R_FULL0) *RFxR &= ~CAN_RF0R_FULL0; + *RFxR = 0; // clear FOVR & FULL +} + +void usb_lp_can_rx0_isr(){ // Rx FIFO0 (overrun) + if(CAN1->RF0R & CAN_RF0R_FOVR0){ // FIFO overrun + CAN1->RF0R &= ~CAN_RF0R_FOVR0; + can_status = CAN_FIFO_OVERRUN; + } +} + +void can_rx1_isr(){ // Rx FIFO1 (overrun) + if(CAN1->RF1R & CAN_RF1R_FOVR1){ + CAN1->RF1R &= ~CAN_RF1R_FOVR1; + can_status = CAN_FIFO_OVERRUN; + } +} + +void can_sce_isr(){ // status changed + if(CAN1->MSR & CAN_MSR_ERRI){ // Error + CAN1->MSR &= ~CAN_MSR_ERRI; + // request abort for problem mailbox + if(CAN1->TSR & CAN_TSR_TERR0) CAN1->TSR |= CAN_TSR_ABRQ0; + if(CAN1->TSR & CAN_TSR_TERR1) CAN1->TSR |= CAN_TSR_ABRQ1; + if(CAN1->TSR & CAN_TSR_TERR2) CAN1->TSR |= CAN_TSR_ABRQ2; + can_status = CAN_ERR; + } +} diff --git a/F1:F103/FX3U/can.h b/F1:F103/FX3U/can.h new file mode 100644 index 0000000..087bb75 --- /dev/null +++ b/F1:F103/FX3U/can.h @@ -0,0 +1,56 @@ +/* + * This file is part of the fx3u project. + * Copyright 2024 Edward V. Emelianov . + * + * 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 . + */ + +#pragma once + +#include + +// amount of filter banks in STM32F0 +#define STM32F0FBANKNO 28 +// flood period in milliseconds +#define FLOOD_PERIOD_MS 5 + +// incoming message buffer size +#define CAN_INMESSAGE_SIZE (8) + +// CAN message +typedef struct{ + uint8_t data[8]; // up to 8 bytes of data + uint8_t length; // data length + uint16_t ID; // ID of receiver +} CAN_message; + +typedef enum{ + CAN_STOP, + CAN_READY, + CAN_BUSY, + CAN_OK, + CAN_ERR, + CAN_FIFO_OVERRUN +} CAN_status; + +CAN_status CAN_get_status(); + +void CAN_reinit(uint16_t speed); +void CAN_setup(uint16_t speed); + +CAN_status can_send(uint8_t *msg, uint8_t len, uint16_t target_id); +void can_proc(); +void printCANerr(); + +CAN_message *CAN_messagebuf_pop(); diff --git a/F1:F103/FX3U/flash.c b/F1:F103/FX3U/flash.c new file mode 100644 index 0000000..d81beea --- /dev/null +++ b/F1:F103/FX3U/flash.c @@ -0,0 +1,191 @@ +/* + * This file is part of the fx3u project. + * Copyright 2024 Edward V. Emelianov . + * + * 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 "stm32f1.h" + +#include "flash.h" +#include "strfunc.h" +#include "usart.h" +#include // memcpy + +extern const uint32_t __varsstart, _BLOCKSIZE; +static const uint32_t FLASH_blocksize = (uint32_t)&_BLOCKSIZE; +static uint32_t maxCnum = 1024 / sizeof(user_conf); // can't use blocksize here + +#define USERCONF_INITIALIZER { \ + .userconf_sz = sizeof(user_conf) \ + ,.canspeed = 250 \ + ,.canID = 0xAA \ + ,.rs232speed = 115200 \ + } + +static int write2flash(const void*, const void*, uint32_t); +const user_conf *Flash_Data = (const user_conf *)(&__varsstart); +user_conf the_conf = USERCONF_INITIALIZER; + +static int currentconfidx = -1; // index of current configuration + +/** + * @brief binarySearch - binary search in flash for last non-empty cell + * any struct searched should have its sizeof() @ the first field!!! + * @param l - left index + * @param r - right index (should be @1 less than last index!) + * @param start - starting address + * @param stor_size - size of structure to search + * @return index of non-empty cell or -1 + */ +static int binarySearch(int r, const uint8_t *start, int stor_size){ + int l = 0; + while(r >= l){ + int mid = l + (r - l) / 2; + const uint8_t *s = start + mid * stor_size; + if(*((const uint16_t*)s) == stor_size){ + if(*((const uint16_t*)(s + stor_size)) == 0xffff){ // next is free + return mid; + }else{ // element is to the right + l = mid + 1; + } + }else{ // element is to the left + r = mid - 1; + } + } + return -1; // not found +} + +/** + * @brief flashstorage_init - initialization of user conf storage + * run in once @ start + */ +void flashstorage_init(){ + if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){ + uint32_t flsz = FLASH_SIZE * 1024; // size in bytes + flsz -= (uint32_t)(&__varsstart) - FLASH_BASE; + maxCnum = flsz / sizeof(user_conf); + } + // -1 if there's no data at all & flash is clear; maxnum-1 if flash is full + currentconfidx = binarySearch((int)maxCnum-2, (const uint8_t*)Flash_Data, sizeof(user_conf)); + if(currentconfidx > -1){ + memcpy(&the_conf, &Flash_Data[currentconfidx], sizeof(user_conf)); + } +} + +// store new configuration +// @return 0 if all OK +int store_userconf(){ + // maxnum - 3 means that there always should be at least one empty record after last data + // for binarySearch() checking that there's nothing more after it! + if(currentconfidx > (int)maxCnum - 3){ // there's no more place + currentconfidx = 0; + if(erase_storage(-1)) return 1; + }else ++currentconfidx; // take next data position (0 - within first run after firmware flashing) + return write2flash((const void*)&Flash_Data[currentconfidx], &the_conf, sizeof(the_conf)); +} + +static int write2flash(const void *start, const void *wrdata, uint32_t stor_size){ + int ret = 0; + if (FLASH->CR & FLASH_CR_LOCK){ // unloch flash + FLASH->KEYR = FLASH_KEY1; + FLASH->KEYR = FLASH_KEY2; + } + while (FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH; + FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR; // clear all flags + FLASH->CR |= FLASH_CR_PG; + const uint16_t *data = (const uint16_t*) wrdata; + volatile uint16_t *address = (volatile uint16_t*) start; + uint32_t i, count = (stor_size + 1) / 2; + for(i = 0; i < count; ++i){ + *(volatile uint16_t*)(address + i) = data[i]; + while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH; + if(*(volatile uint16_t*)(address + i) != data[i]){ + usart_send("DON'T MATCH!\n"); + ret = 1; + break; + } + if(FLASH->SR & FLASH_SR_PGERR){ + usart_send("Prog err\n"); + ret = 1; // program error - meet not 0xffff + break; + } + FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR; + } + FLASH->CR &= ~(FLASH_CR_PG); + return ret; +} + +// erase Nth page of flash storage (flash should be prepared!) +static int erase_pageN(int N){ + int ret = 0; + FLASH->AR = (uint32_t)Flash_Data + N*FLASH_blocksize; + FLASH->CR |= FLASH_CR_STRT; + while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH; + FLASH->SR = FLASH_SR_EOP; + if(FLASH->SR & FLASH_SR_WRPRTERR){ /* Check Write protection error */ + ret = 1; + FLASH->SR = FLASH_SR_WRPRTERR; /* Clear the flag by software by writing it at 1*/ + } + return ret; +} + +// erase full storage (npage < 0) or its nth page; @return 0 if all OK +int erase_storage(int npage){ + int ret = 0; + uint32_t end = 1, start = 0, flsz = 0; + if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){ + flsz = FLASH_SIZE * 1024; // size in bytes + flsz -= (uint32_t)Flash_Data - FLASH_BASE; + } + end = flsz / FLASH_blocksize; +#ifdef EBUG + usart_send("FLASH_SIZE="); usart_send(u2str(FLASH_SIZE)); + usart_send("\nflsz="); usart_send(u2str(flsz)); + usart_send("\nend="); usart_send(u2str(end)); + usart_send("\ncurrentconfidx="); usart_send(u2str(currentconfidx)); + usart_send("\nmaxCnum="); usart_send(u2str(maxCnum)); + usart_send("\n"); +#endif + if(end == 0 || end >= FLASH_SIZE) return 1; + if(npage > -1){ // erase only one page + if((uint32_t)npage >= end) return 1; + start = npage; + end = start + 1; + } + if((FLASH->CR & FLASH_CR_LOCK) != 0){ + FLASH->KEYR = FLASH_KEY1; + FLASH->KEYR = FLASH_KEY2; + } + while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH; + FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR; + FLASH->CR |= FLASH_CR_PER; + for(uint32_t i = start; i < end; ++i){ + if(erase_pageN(i)){ + ret = 1; + break; + } + } + FLASH->CR &= ~FLASH_CR_PER; + return ret; +} + +void dump_userconf(){ + usart_send("userconf_sz="); printu(the_conf.userconf_sz); + usart_send("\ncurrentconfidx="); usart_send(u2str(currentconfidx)); + usart_send("\nCAN_speed="); printu(the_conf.canspeed); + usart_send("\nCAN_ID="); printu(the_conf.canID); + usart_send("\nRS_232_speed="); printu(the_conf.rs232speed); + usart_send("\n"); +} diff --git a/F1:F103/FX3U/flash.h b/F1:F103/FX3U/flash.h new file mode 100644 index 0000000..7e6baf8 --- /dev/null +++ b/F1:F103/FX3U/flash.h @@ -0,0 +1,41 @@ +/* + * This file is part of the fx3u project. + * Copyright 2024 Edward V. Emelianov . + * + * 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 . + */ + +#pragma once + +#include + +#define FLASH_SIZE_REG ((uint32_t)0x1FFFF7E0) +#define FLASH_SIZE *((uint16_t*)FLASH_SIZE_REG) + +/* + * struct to save user configurations + */ +typedef struct __attribute__((packed, aligned(4))){ + uint16_t userconf_sz; // "magick number" + uint16_t canspeed; // CAN bus speed + uint16_t canID; // CAN bus device ID + uint32_t rs232speed; // speed of RS-232 +} user_conf; + +extern user_conf the_conf; + +void flashstorage_init(); +int store_userconf(); +int erase_storage(int npage); +void dump_userconf(); diff --git a/F1:F103/FX3U/fx3u.cflags b/F1:F103/FX3U/fx3u.cflags new file mode 100644 index 0000000..68d5165 --- /dev/null +++ b/F1:F103/FX3U/fx3u.cflags @@ -0,0 +1 @@ +-std=c17 \ No newline at end of file diff --git a/F1:F103/FX3U/fx3u.config b/F1:F103/FX3U/fx3u.config new file mode 100644 index 0000000..4a7b815 --- /dev/null +++ b/F1:F103/FX3U/fx3u.config @@ -0,0 +1,6 @@ +// Add predefined macros for your project here. For example: +// #define THE_ANSWER 42 +#define EBUG +#define STM32F1 +#define STM32F103xC +#define STM32F10X_HD diff --git a/F1:F103/FX3U/fx3u.creator b/F1:F103/FX3U/fx3u.creator new file mode 100644 index 0000000..e94cbbd --- /dev/null +++ b/F1:F103/FX3U/fx3u.creator @@ -0,0 +1 @@ +[General] diff --git a/F1:F103/FX3U/fx3u.creator.user b/F1:F103/FX3U/fx3u.creator.user new file mode 100644 index 0000000..2d3fcea --- /dev/null +++ b/F1:F103/FX3U/fx3u.creator.user @@ -0,0 +1,180 @@ + + + + + + EnvironmentId + {7bd84e39-ca37-46d3-be9d-99ebea85bc0d} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + KOI8-R + false + 4 + false + 80 + true + true + 1 + 0 + false + true + false + 0 + true + true + 0 + 8 + true + false + 1 + true + false + true + *.md, *.MD, Makefile + false + true + true + + + + ProjectExplorer.Project.PluginSettings + + + true + false + true + true + true + true + + + 0 + true + + true + true + Builtin.DefaultTidyAndClazy + 8 + true + + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + Desktop + {65a14f9e-e008-4c1b-89df-4eaa4774b6e3} + 0 + 0 + 0 + + /Big/Data/00__Electronics/STM32/F1-nolib/CAR_CANbus/WindShield + + + + all + + true + GenericProjectManager.GenericMakeStep + + 1 + Сборка + Сборка + ProjectExplorer.BuildSteps.Build + + + + + clean + + true + GenericProjectManager.GenericMakeStep + + 1 + Очистка + Очистка + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + По умолчанию + GenericProjectManager.GenericBuildConfiguration + + 1 + + + 0 + Развёртывание + Развёртывание + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + 0 + true + + + 2 + + false + + ProjectExplorer.CustomExecutableRunConfiguration + + false + true + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/F1:F103/FX3U/fx3u.creator.user.cf63021 b/F1:F103/FX3U/fx3u.creator.user.cf63021 new file mode 100644 index 0000000..ebefce4 --- /dev/null +++ b/F1:F103/FX3U/fx3u.creator.user.cf63021 @@ -0,0 +1,160 @@ + + + + + + EnvironmentId + {cf63021e-ef53-49b0-b03b-2f2570cdf3b6} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + KOI8-R + false + 4 + false + 80 + true + true + 1 + false + false + false + 1 + true + true + 0 + 8 + true + false + 2 + true + true + true + *.md, *.MD, Makefile + true + true + + + + ProjectExplorer.Project.PluginSettings + + + true + true + Builtin.DefaultTidyAndClazy + 4 + + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + Desktop + {91347f2c-5221-46a7-80b1-0a054ca02f79} + 0 + 0 + 0 + + /home/eddy/Docs/SAO/ELECTRONICS/STM32/F1-srcs/usbcan + + + + all + + true + GenericProjectManager.GenericMakeStep + + 1 + Сборка + Сборка + ProjectExplorer.BuildSteps.Build + + + + + clean + + true + GenericProjectManager.GenericMakeStep + + 1 + Очистка + Очистка + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + По умолчанию + GenericProjectManager.GenericBuildConfiguration + + 1 + + + 0 + Развёртывание + Развёртывание + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + 2 + + ProjectExplorer.CustomExecutableRunConfiguration + + false + true + false + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/F1:F103/FX3U/fx3u.cxxflags b/F1:F103/FX3U/fx3u.cxxflags new file mode 100644 index 0000000..6435dfc --- /dev/null +++ b/F1:F103/FX3U/fx3u.cxxflags @@ -0,0 +1 @@ +-std=c++17 \ No newline at end of file diff --git a/F1:F103/FX3U/fx3u.files b/F1:F103/FX3U/fx3u.files new file mode 100644 index 0000000..f964000 --- /dev/null +++ b/F1:F103/FX3U/fx3u.files @@ -0,0 +1,16 @@ +adc.c +adc.h +buttons.c +can.c +can.h +flash.c +flash.h +hardware.c +hardware.h +main.c +proto.c +proto.h +strfunc.c +strfunc.h +usart.c +usart.h diff --git a/F1:F103/FX3U/fx3u.includes b/F1:F103/FX3U/fx3u.includes new file mode 100644 index 0000000..0dce01d --- /dev/null +++ b/F1:F103/FX3U/fx3u.includes @@ -0,0 +1,7 @@ +. +../inc +../inc/Fx +../inc/cm +../inc/ld +../inc/startup + diff --git a/F1:F103/FX3U/hardware.c b/F1:F103/FX3U/hardware.c new file mode 100644 index 0000000..6b0a98f --- /dev/null +++ b/F1:F103/FX3U/hardware.c @@ -0,0 +1,105 @@ +/* + * This file is part of the fx3u project. + * Copyright 2024 Edward V. Emelianov . + * + * 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 "hardware.h" + +/* pinout: + +| **Pin #** | **Pin name ** | **function** | **settings** | **comment ** | +| --------- | ------------- | ------------ | ---------------------- | --------------------------------------- | +| 15 | PC0/adcin10 | ADC4 | ADC in | | +| 16 | PC1/adcin11 | ADC5 | ADC in | | +| 23 | PA0 | Y3 | PPOUT | | +| 24 | PA1/adcin1 | ADC0 | ADC in | | +| 25 | PA2 | Y11 | PPOUT | | +| 26 | PA3/adcin3 | ADC1 | ADC in | | +| 31 | PA6 | Y10 | PPOUT | | +| 32 | PA7 | Y7 | PPOUT | | +| 33 | PC4/adcin14 | ADC2 | ADC in | | +| 34 | PC5/adcin15 | ADC3 | ADC in | | +| 37 | PB2/boot1 | PROG SW | PUIN | | +| 38 | PE7 | X14 | PUIN | | +| 39 | PE8 | X15 | PUIN | | +| 40 | PE9 | X12 | PUIN | | +| 41 | PE10 | X13 | PUIN | | +| 42 | PE11 | X10 | PUIN | | +| 43 | PE12 | X11 | PUIN | | +| 44 | PE13 | X6 | PUIN | | +| 45 | PE14 | X7 | PUIN | | +| 46 | PE15 | X4 | PUIN | | +| 47 | PB10 | X5 | PUIN | | +| 48 | PB11 | X2 | PUIN | | +| 51 | PB12 | X3 | PUIN | | +| 52 | PB13 | X0 | PUIN | | +| 53 | PB14 | X1 | PUIN | | +| 54 | PB15 | Y6 | PPOUT | | +| 59 | PD12 | Y5 | PPOUT | | +| 65 | PC8 | Y1 | PPOUT | | +| 66 | PC9 | Y0 | PPOUT | | +| 67 | PA8 | Y2 | PPOUT | | +| 68 | PA9 | RS TX | AFPP | | +| 69 | PA10 | RS RX | FLIN | | +| 76 | PA14/SWCLK | 485 DE * | (default) | (Not now) | +| 81 | PD0 | CAN RX | FLIN | | +| 82 | PD1 | CAN TX | AFPP | | +| 89 | PB3/JTDO | Y4 | PPOUT | | +| | | | | | +| | | | | | +| | | | | | + */ + +void gpio_setup(void){ + // PD0 & PD1 (CAN) setup in can.c; PA9 & PA10 (USART) in usart.c + RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN | + RCC_APB2ENR_IOPEEN | RCC_APB2ENR_AFIOEN; + // Turn off JTAG + AFIO->MAPR = AFIO_MAPR_SWJ_CFG_JTAGDISABLE; + GPIOA->CRL = CRL(0, CNF_PPOUTPUT|MODE_NORMAL) | CRL(1, CNF_ANALOG) | CRL(2, CNF_PPOUTPUT|MODE_NORMAL) | + CRL(3, CNF_ANALOG) | CRL(6, CNF_PPOUTPUT|MODE_NORMAL) | CRL(7, CNF_PPOUTPUT|MODE_NORMAL); + GPIOA->CRH = 0; + GPIOB->CRL = CRL(2, CNF_PUDINPUT); + GPIOB->CRH = CRH(10, CNF_PUDINPUT) | CRH(11, CNF_PUDINPUT) | CRH(12, CNF_PUDINPUT) | CRH(13, CNF_PUDINPUT) | + CRH(14, CNF_PUDINPUT) | CRH(15, CNF_PPOUTPUT|MODE_NORMAL); + GPIOC-> CRL = CRL(0, CNF_ANALOG) | CRL(1, CNF_ANALOG) | CRL(4, CNF_ANALOG) | CRL(5, CNF_ANALOG); + GPIOC->CRH = CRH(8, CNF_PPOUTPUT|MODE_NORMAL) | CRH(9, CNF_PPOUTPUT|MODE_NORMAL); + GPIOD->CRL = 0; + GPIOD->CRH = CRH(12, CNF_PPOUTPUT|MODE_NORMAL); + GPIOE->CRL = CRL(7, CNF_PUDINPUT); + GPIOE->CRH = CRH(8, CNF_PUDINPUT) | CRH(9, CNF_PUDINPUT) | CRH(10, CNF_PUDINPUT) | CRH(11, CNF_PUDINPUT) | + CRH(12, CNF_PUDINPUT) | CRH(13, CNF_PUDINPUT) | CRH(14, CNF_PUDINPUT) | CRH(15, CNF_PUDINPUT); + // pullups & initial values + GPIOA->ODR = 0; + GPIOB->ODR = (1<<2) | (1<<10) | (1<<11) | (1<<12) | (1<<13) | (1<<14); + GPIOC->ODR = 0; + GPIOD->ODR = 0; + GPIOE->ODR = (1<<7) | (1<<8) | (1<<9) | (1<<10) | (1<<11) | (1<<12) | (1<<13) | (1<<14) | (1<<15); +} + + +void iwdg_setup(){ + uint32_t tmout = 16000000; + RCC->CSR |= RCC_CSR_LSION; + while((RCC->CSR & RCC_CSR_LSIRDY) != RCC_CSR_LSIRDY){if(--tmout == 0) break;} + IWDG->KR = IWDG_START; + IWDG->KR = IWDG_WRITE_ACCESS; + IWDG->PR = IWDG_PR_PR_1; + IWDG->RLR = 1250; + tmout = 16000000; + while(IWDG->SR){if(--tmout == 0) break;} + IWDG->KR = IWDG_REFRESH; +} diff --git a/F1:F103/FX3U/hardware.h b/F1:F103/FX3U/hardware.h new file mode 100644 index 0000000..d90dcec --- /dev/null +++ b/F1:F103/FX3U/hardware.h @@ -0,0 +1,33 @@ +/* + * This file is part of the fx3u project. + * Copyright 2024 Edward V. Emelianov . + * + * 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 . + */ + +#pragma once + +#include + +#define CONCAT(a,b) a ## b +#define STR_HELPER(s) #s +#define STR(s) STR_HELPER(s) + +#define FORMUSART(X) CONCAT(USART, X) +#define USARTX FORMUSART(USARTNUM) + +extern volatile uint32_t Tms; + +void gpio_setup(void); +void iwdg_setup(); diff --git a/F1:F103/FX3U/main.c b/F1:F103/FX3U/main.c new file mode 100644 index 0000000..61880d3 --- /dev/null +++ b/F1:F103/FX3U/main.c @@ -0,0 +1,84 @@ +/* + * This file is part of the fx3u project. + * Copyright 2024 Edward V. Emelianov . + * + * 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 "adc.h" +#include "can.h" +#include "flash.h" +#include "hardware.h" +#include "proto.h" +#include "strfunc.h" + +volatile uint32_t Tms = 0; + +/* Called when systick fires */ +void sys_tick_handler(void){ + ++Tms; +} + +int main(void){ + uint32_t lastT = 0; + CAN_message *can_mesg; + StartHSE(); + SysTick_Config(72000); + flashstorage_init(); + gpio_setup(); // should be run before other peripherial setup + adc_setup(); + usart_setup(the_conf.rs232speed); + CAN_setup(the_conf.canspeed); + RCC->CSR |= RCC_CSR_RMVF; // remove reset flags +#ifndef EBUG + iwdg_setup(); +#endif + while (1){ + IWDG->KR = IWDG_REFRESH; // refresh watchdog + if(Tms - lastT > 499){ // throw out short messages twice per second + usart_transmit(); + lastT = Tms; + } + can_proc(); + CAN_status st = CAN_get_status(); + if(st == CAN_FIFO_OVERRUN){ + usart_send("CAN bus fifo overrun occured!\n"); + }else if(st == CAN_ERR){ + usart_send("Some CAN error occured\n"); + } + while((can_mesg = CAN_messagebuf_pop())){ + IWDG->KR = IWDG_REFRESH; + // TODO: check ID and process messages + if(flags.can_monitor){ + lastT = Tms; + if(!lastT) lastT = 1; + uint8_t len = can_mesg->length; + printu(Tms); + usart_send(" #"); + printuhex(can_mesg->ID); + for(uint8_t ctr = 0; ctr < len; ++ctr){ + usart_putchar(' '); + printuhex(can_mesg->data[ctr]); + } + usart_putchar('\n'); + } + } + char *str; + int g = usart_getline(&str); + if(g < 0) usart_send("USART IN buffer overflow!\n"); + else if(g > 0) cmd_parser(str); + } + return 0; +} + diff --git a/F1:F103/FX3U/openocd.cfg b/F1:F103/FX3U/openocd.cfg new file mode 100644 index 0000000..5f64fe2 --- /dev/null +++ b/F1:F103/FX3U/openocd.cfg @@ -0,0 +1,6 @@ +# STM32F103VC + +set FLASH_SIZE 0x40000 + +source [find interface/stlink-v2-1.cfg] +source [find target/stm32f1x.cfg] diff --git a/F1:F103/FX3U/proto.c b/F1:F103/FX3U/proto.c new file mode 100644 index 0000000..4b7e6b5 --- /dev/null +++ b/F1:F103/FX3U/proto.c @@ -0,0 +1,54 @@ +/* + * This file is part of the fx3u project. + * Copyright 2024 Edward V. Emelianov . + * + * 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 "adc.h" +#include "can.h" +#include "hardware.h" +#include "proto.h" +#include "strfunc.h" +#include "usart.h" +#include "version.inc" + +flags_t flags = { + .can_monitor = 0 +}; + +static void printans(int res){ + if(res) usart_send("OK"); + else usart_send("FAIL"); +} + +// setters of uint/int +static void usetter(int(*fn)(uint32_t), char* str){ + uint32_t d = 0; + if(str == getnum(str, &d)) printans(FALSE); + else printans(fn(d)); +} +static void isetter(int(*fn)(int32_t), char* str){ + int32_t d = 0; + if(str == getint(str, &d)) printans(FALSE); + else printans(fn(d)); +} + +/** + * @brief cmd_parser - command parsing + * @param txt - buffer with commands & data + */ +void cmd_parser(char *txt){ + (void)txt; +} diff --git a/F1:F103/FX3U/proto.h b/F1:F103/FX3U/proto.h new file mode 100644 index 0000000..023471a --- /dev/null +++ b/F1:F103/FX3U/proto.h @@ -0,0 +1,40 @@ +/* + * This file is part of the fx3u project. + * Copyright 2024 Edward V. Emelianov . + * + * 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 . + */ + +#pragma once + +#include +#include "hardware.h" + +#define BUFSZ (64) + +typedef struct{ + uint32_t can_monitor : 1; +} flags_t; + +extern flags_t flags; + +#ifdef EBUG +#define DBG(str) do{usart_send(__FILE__ " (L" STR(__LINE__) "): " str); \ + usart_putchar('\n'); usart_transmit(); }while(0) +#else +#define DBG(str) +#endif + + +void cmd_parser(char *buf); diff --git a/F1:F103/FX3U/strfunc.c b/F1:F103/FX3U/strfunc.c new file mode 100644 index 0000000..7c42a70 --- /dev/null +++ b/F1:F103/FX3U/strfunc.c @@ -0,0 +1,268 @@ +/* + * This file is part of the fx3u project. + * Copyright 2024 Edward V. Emelianov . + * + * 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 +#include + +/** + * @brief hexdump - dump hex array by 16 bytes in string + * @param sendfun - function to send data + * @param arr - array to dump + * @param len - length of `arr` + */ +void hexdump(int (*sendfun)(const char *s), uint8_t *arr, uint16_t len){ + char buf[52], *bptr = buf; + for(uint16_t l = 0; l < len; ++l, ++arr){ + for(int16_t j = 1; j > -1; --j){ + register uint8_t half = (*arr >> (4*j)) & 0x0f; + if(half < 10) *bptr++ = half + '0'; + else *bptr++ = half - 10 + 'a'; + } + if(l % 16 == 15){ + *bptr++ = '\n'; + *bptr = 0; + sendfun(buf); + bptr = buf; + }else *bptr++ = ' '; + } + if(bptr != buf){ + *bptr++ = '\n'; + *bptr = 0; + sendfun(buf); + } +} + +/** + * @brief _2str - convert value into string buffer + * @param val - |value| + * @param minus - ==0 if value > 0 + * @return buffer with number + */ +static char *_2str(uint32_t val, uint8_t minus){ + static char strbuf[12]; + char *bufptr = &strbuf[11]; + *bufptr = 0; + if(!val){ + *(--bufptr) = '0'; + }else{ + while(val){ + uint32_t x = val / 10; + *(--bufptr) = (val - 10*x) + '0'; + val = x; + //*(--bufptr) = val % 10 + '0'; + //val /= 10; + } + } + if(minus) *(--bufptr) = '-'; + return bufptr; +} + +// return string with number `val` +char *u2str(uint32_t val){ + return _2str(val, 0); +} +char *i2str(int32_t i){ + uint8_t minus = 0; + uint32_t val; + if(i < 0){ + minus = 1; + val = -i; + }else val = i; + return _2str(val, minus); +} + +/** + * @brief uhex2str - print 32bit unsigned int as hex + * @param val - value + * @return string with number + */ +char *uhex2str(uint32_t val){ + static char buf[12] = "0x"; + int npos = 2; + uint8_t *ptr = (uint8_t*)&val + 3; + int8_t i, j, z=1; + for(i = 0; i < 4; ++i, --ptr){ + if(*ptr == 0){ // omit leading zeros + if(i == 3) z = 0; + if(z) continue; + } + else z = 0; + for(j = 1; j > -1; --j){ + uint8_t half = (*ptr >> (4*j)) & 0x0f; + if(half < 10) buf[npos++] = half + '0'; + else buf[npos++] = half - 10 + 'a'; + } + } + buf[npos] = 0; + return buf; +} + +/** + * @brief omit_spaces - eliminate leading spaces and other trash in string + * @param buf - string + * @return - pointer to first character in `buf` > ' ' + */ +const char *omit_spaces(const char *buf){ + while(*buf){ + if(*buf > ' ') break; + ++buf; + } + return buf; +} + +/** + * @brief getdec - read decimal number & return pointer to next non-number symbol + * @param buf - string + * @param N - number read + * @return Next non-number symbol. In case of overflow return `buf` and N==0xffffffff + */ +static const char *getdec(const char *buf, uint32_t *N){ + char *start = (char*)buf; + uint32_t num = 0; + while(*buf){ + char c = *buf; + if(c < '0' || c > '9'){ + break; + } + if(num > 429496729 || (num == 429496729 && c > '5')){ // overflow + *N = 0xffffff; + return start; + } + num *= 10; + num += c - '0'; + ++buf; + } + *N = num; + return buf; +} +// read hexadecimal number (without 0x prefix!) +static const char *gethex(const char *buf, uint32_t *N){ + const char *start = buf; + uint32_t num = 0; + while(*buf){ + char c = *buf; + uint8_t M = 0; + if(c >= '0' && c <= '9'){ + M = '0'; + }else if(c >= 'A' && c <= 'F'){ + M = 'A' - 10; + }else if(c >= 'a' && c <= 'f'){ + M = 'a' - 10; + } + if(M){ + if(num & 0xf0000000){ // overflow + *N = 0xffffff; + return start; + } + num <<= 4; + num += c - M; + }else{ + break; + } + ++buf; + } + *N = num; + return buf; +} +// read octal number (without 0 prefix!) +static const char *getoct(const char *buf, uint32_t *N){ + const char *start = (char*)buf; + uint32_t num = 0; + while(*buf){ + char c = *buf; + if(c < '0' || c > '7'){ + break; + } + if(num & 0xe0000000){ // overflow + *N = 0xffffff; + return start; + } + num <<= 3; + num += c - '0'; + ++buf; + } + *N = num; + return buf; +} +// read binary number (without b prefix!) +static const char *getbin(const char *buf, uint32_t *N){ + const char *start = (char*)buf; + uint32_t num = 0; + while(*buf){ + char c = *buf; + if(c < '0' || c > '1'){ + break; + } + if(num & 0x80000000){ // overflow + *N = 0xffffff; + return start; + } + num <<= 1; + if(c == '1') num |= 1; + ++buf; + } + *N = num; + return buf; +} + +/** + * @brief getnum - read uint32_t from string (dec, hex or bin: 127, 0x7f, 0b1111111) + * @param buf - buffer with number and so on + * @param N - the number read + * @return pointer to first non-number symbol in buf + * (if it is == buf, there's no number or if *N==0xffffffff there was overflow) + */ +const char *getnum(const char *txt, uint32_t *N){ + const char *nxt = NULL; + const char *s = omit_spaces(txt); + if(*s == '0'){ // hex, oct or 0 + if(s[1] == 'x' || s[1] == 'X'){ // hex + nxt = gethex(s+2, N); + if(nxt == s+2) nxt = (char*)txt; + }else if(s[1] > '0'-1 && s[1] < '8'){ // oct + nxt = getoct(s+1, N); + if(nxt == s+1) nxt = (char*)txt; + }else{ // 0 + nxt = s+1; + *N = 0; + } + }else if(*s == 'b' || *s == 'B'){ + nxt = getbin(s+1, N); + if(nxt == s+1) nxt = (char*)txt; + }else{ + nxt = getdec(s, N); + if(nxt == s) nxt = (char*)txt; + } + return nxt; +} + +// get signed integer +const char *getint(const char *txt, int32_t *I){ + const char *s = omit_spaces(txt); + int32_t sign = 1; + uint32_t U; + if(*s == '-'){ + sign = -1; + ++s; + } + const char *nxt = getnum(s, &U); + if(nxt == s) return txt; + if(U & 0x80000000) return txt; // overfull + *I = sign * (int32_t)U; + return nxt; +} diff --git a/F1:F103/FX3U/strfunc.h b/F1:F103/FX3U/strfunc.h new file mode 100644 index 0000000..3be4040 --- /dev/null +++ b/F1:F103/FX3U/strfunc.h @@ -0,0 +1,37 @@ +/* + * This file is part of the fx3u project. + * Copyright 2024 Edward V. Emelianov . + * + * 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 . + */ + +#pragma once + +#include +#include + +#include "usart.h" + +void hexdump(int (*sendfun)(const char *s), uint8_t *arr, uint16_t len); +char *u2str(uint32_t val); +char *i2str(int32_t i); +char *uhex2str(uint32_t val); +const char *getnum(const char *txt, uint32_t *N); +const char *omit_spaces(const char *buf); +const char *getint(const char *txt, int32_t *I); + +#define newline() do{usart_putchar('\n');}while(0) +#define printu(a) do{usart_send(u2str(a));}while(0) +#define printi(a) do{usart_send(i2str(a));}while(0) +#define printuhex(a) do{usart_send(uhex2str(a));}while(0) diff --git a/F1:F103/FX3U/usart.c b/F1:F103/FX3U/usart.c new file mode 100644 index 0000000..7dffdeb --- /dev/null +++ b/F1:F103/FX3U/usart.c @@ -0,0 +1,152 @@ +/* + * This file is part of the fx3u project. + * Copyright 2024 Edward V. Emelianov . + * + * 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 +#include "usart.h" + +extern volatile uint32_t Tms; +static volatile int idatalen[2] = {0,0}; // received data line length (including '\n') +static volatile int odatalen[2] = {0,0}; + +volatile int usart_txrdy = 1; // transmission done + +static volatile int usart_linerdy = 0 // received data ready + ,dlen = 0 // length of data (including '\n') in current buffer + ,usart_bufovr = 0 // input buffer overfull + +; + +int rbufno = 0, tbufno = 0; // current rbuf/tbuf numbers +static char rbuf[2][UARTBUFSZI], tbuf[2][UARTBUFSZO]; // receive & transmit buffers +static char *recvdata = NULL; + +/** + * return length of received data (without trailing zero) + */ +int usart_getline(char **line){ + if(usart_bufovr){ + usart_bufovr = 0; + usart_linerdy = 0; + return -1; + } + if(!usart_linerdy) return 0; + *line = recvdata; + usart_linerdy = 0; + int x = dlen; + dlen = 0; + return x; +} + +// transmit current tbuf and swap buffers +int usart_transmit(){ + register int l = odatalen[tbufno]; + if(!l) return 0; + uint32_t tmout = 1600000; + while(!usart_txrdy){ + IWDG->KR = IWDG_REFRESH; + if(--tmout == 0) return 0; + }; // wait for previos buffer transmission + usart_txrdy = 0; + odatalen[tbufno] = 0; + DMA1_Channel4->CCR &= ~DMA_CCR_EN; + DMA1_Channel4->CMAR = (uint32_t) tbuf[tbufno]; // mem + DMA1_Channel4->CNDTR = l; + DMA1_Channel4->CCR |= DMA_CCR_EN; + tbufno = !tbufno; + return l; +} + +int usart_putchar(const char ch){ + if(odatalen[tbufno] == UARTBUFSZO){ + if(!usart_transmit()) return 0; + } + tbuf[tbufno][odatalen[tbufno]++] = ch; + return 1; +} + +int usart_send(const char *str){ + int l = 0; + while(*str){ + if(odatalen[tbufno] == UARTBUFSZO){ + if(!usart_transmit()) return 0; + } + tbuf[tbufno][odatalen[tbufno]++] = *str++; + ++l; + } + return l; +} + +/* + * USART speed: baudrate = Fck/(USARTDIV) + * USARTDIV stored in USART->BRR + * + * for 72MHz USARTDIV=72000/f(kboud); so for 115200 USARTDIV=72000/115.2=625 -> BRR=0x271 + * 9600: BRR = 7500 (0x1D4C) + */ + +void usart_setup(uint32_t speed){ + uint32_t tmout = 16000000; + // PA9 - Tx, PA10 - Rx + RCC->APB2ENR |= RCC_APB2ENR_USART1EN; + RCC->AHBENR |= RCC_AHBENR_DMA1EN; + GPIOA->CRH = (GPIOA->CRH & ~(CRH(9,0xf)|CRH(10,0xf))) | + CRH(9, CNF_AFPP|MODE_NORMAL) | CRH(10, CNF_FLINPUT|MODE_INPUT); + // USART1 Tx DMA - Channel4 (Rx - channel 5) + DMA1_Channel4->CPAR = (uint32_t) &USART1->DR; // periph + DMA1_Channel4->CCR |= DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE; // 8bit, mem++, mem->per, transcompl irq + // Tx CNDTR set @ each transmission due to data size + NVIC_SetPriority(DMA1_Channel4_IRQn, 3); + NVIC_EnableIRQ(DMA1_Channel4_IRQn); + NVIC_SetPriority(USART1_IRQn, 0); + // setup usart1 + USART1->BRR = 72000000 / speed; + USART1->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; // 1start,8data,nstop; enable Rx,Tx,USART + while(!(USART1->SR & USART_SR_TC)){if(--tmout == 0) break;} // polling idle frame Transmission + USART1->SR = 0; // clear flags + USART1->CR1 |= USART_CR1_RXNEIE; // allow Rx IRQ + USART1->CR3 = USART_CR3_DMAT; // enable DMA Tx + NVIC_EnableIRQ(USART1_IRQn); +} + + +void usart1_isr(){ + if(USART1->SR & USART_SR_RXNE){ // RX not emty - receive next char + uint8_t rb = USART1->DR; + if(idatalen[rbufno] < UARTBUFSZI){ // put next char into buf + if(rb == '\n'){ // got newline - line ready + rbuf[rbufno][idatalen[rbufno]] = 0; + usart_linerdy = 1; + dlen = idatalen[rbufno]; + recvdata = rbuf[rbufno]; + // prepare other buffer + rbufno = !rbufno; + idatalen[rbufno] = 0; + }else rbuf[rbufno][idatalen[rbufno]++] = rb; + }else{ // buffer overrun + usart_bufovr = 1; + idatalen[rbufno] = 0; + } + } +} + +void dma1_channel4_isr(){ + if(DMA1->ISR & DMA_ISR_TCIF4){ // Tx + DMA1->IFCR = DMA_IFCR_CTCIF4; // clear TC flag + usart_txrdy = 1; + } +} diff --git a/F1:F103/FX3U/usart.h b/F1:F103/FX3U/usart.h new file mode 100644 index 0000000..ef767df --- /dev/null +++ b/F1:F103/FX3U/usart.h @@ -0,0 +1,34 @@ +/* + * This file is part of the fx3u project. + * Copyright 2024 Edward V. Emelianov . + * + * 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 . + */ + +#pragma once + +// input and output buffers size +#define UARTBUFSZI (64) +#define UARTBUFSZO (128) + +#define usartrx() (usart_linerdy) +#define usartovr() (usart_bufovr) + +extern volatile int usart_txrdy; + +int usart_transmit(); +void usart_setup(uint32_t speed); +int usart_getline(char **line); +int usart_send(const char *str); +int usart_putchar(const char ch); diff --git a/F1:F103/FX3U/version.inc b/F1:F103/FX3U/version.inc new file mode 100644 index 0000000..249941c --- /dev/null +++ b/F1:F103/FX3U/version.inc @@ -0,0 +1,2 @@ +#define BUILD_NUMBER "5" +#define BUILD_DATE "2024-05-29"