diff --git a/.gitignore b/.gitignore index 5551f23..5ebdffb 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,9 @@ *.pho *.drl .dropbox.attr +*.bk +*.config +*.creator +*.creator.user* +*.files +*.includes diff --git a/STM32/TSYS_controller/2DO b/STM32/TSYS_controller/2DO deleted file mode 100644 index 557fd4f..0000000 --- a/STM32/TSYS_controller/2DO +++ /dev/null @@ -1,3 +0,0 @@ -- USB bus: PA11 (DM), PA12 (DP) -- ADC inputs: PA0 (V12/4.93), PA1 (V5/2), PA3 (I12 - 1V/A), PA6 (V3.3/2) - diff --git a/STM32/TSYS_controller/Readme.md b/STM32/TSYS_controller/Readme.md index 042179d..5b412cb 100644 --- a/STM32/TSYS_controller/Readme.md +++ b/STM32/TSYS_controller/Readme.md @@ -4,7 +4,8 @@ Make regular scan of 8 sensors' pairs. USART speed 115200. Code for ../../kicad/stm32 ### Serial interface commands (ends with '\n'), small letter for only local processing: -- **1...7** - send message to Nth controller, not broadcast (after number should be CAN command) +- **0...7** send message to Nth controller, not broadcast (after number should be CAN command) +- **a** get raw ADC values - **B** send dummy CAN messages to broadcast address - **c** show coefficients for all thermosensors - **D** send dummy CAN messages to master (0) address @@ -12,8 +13,12 @@ USART speed 115200. Code for ../../kicad/stm32 - **Ff** turn sensors off - **g** get last CAN address - **Hh** switch I2C to high speed (100kHz) -- **i** reinit CAN +- **i** reinit CAN with new address (if changed) +- **Jj** get MCU temperature +- **Kk** get values of U and I - **Ll** switch I2C to low speed (default, 10kHz) +- **Mm** change master id to 0 (**m**) / broadcast (**M**) +- **Oo** turn onboard diagnostic LEDs **O**n or **o**ff (both commands are local!) - **P** ping everyone over CAN - **Rr** reinit I2C - **Ss** start temperature scan @@ -22,6 +27,10 @@ USART speed 115200. Code for ../../kicad/stm32 - **Vv** very low speed - **Z** get sensors state over CAN +The command **M** allows to temporaly change master ID of all +controllers to broadcast ID. So all data they sent will be +accessed @ any controller. + ### PINOUT - I2C: PB6 (SCL) & PB7 (SDA) - USART1: PA9 (Tx) & PA10 (Rx) @@ -31,7 +40,7 @@ USART speed 115200. Code for ../../kicad/stm32 - sensors' power: PB3 (in, overcurrent), PA8 (out, enable power) - signal LEDs: PB10 (LED0), PB11 (LED1) - ADC inputs: PA0 (V12/4.93), PA1 (V5/2), PA3 (I12 - 1V/A), PA6 (V3.3/2) -- controller CAN address: PA13..PA15 (0..2 bits); 0 - master, other address - slave +- controller CAN address: PA13..PA15 (0..2 bits), PB15 (3rd bit); 0 - master, other address - slave ### LEDS @@ -40,7 +49,7 @@ USART speed 115200. Code for ../../kicad/stm32 ### CAN protocol Variable data length: from 1 to 7 bytes. -First byte of every sequence is command mark (0xA5) or data mark (0x5A). +First (number zero) byte of every sequence is command mark (0xA5) or data mark (0x5A). Commands: - CMD_PING request for PONG cmd @@ -59,12 +68,27 @@ Dummy commands for test purposes: - CMD_DUMMY1 = 0xAD Data format: -- 1 byte - Controller number -- 2 byte - Command received -- 3..7 bytes - data +- byte 1 - Controller number +- byte 2 - Command received +- bytes 3..7 - data Thermal data format: -- 3 byte - Sensor number (10*N + M, where N is multiplexer number, M - number of sensor in pair, i.e. 0,1,10,11,20,21...70,71) -- 4 byte - thermal data H -- 5 byte - thermal data L +- byte 3 - Sensor number (10*N + M, where N is multiplexer number, M - number of sensor in pair, i.e. 0,1,10,11,20,21...70,71) +- byte 4 - thermal data H +- byte 5 - thermal data L +MCU temperature data format: +- byte 3 - data H +- byte 4 - data L + +All temperature is in degrC/100 + +U and I data format: +- byte 2 - type of data (CMD_GETUIVAL0 - V12 and V5, CMD_GETUIVAL1 - I12 and V3.3) +case CMD_GETUIVAL0: +- bytes 3,4 - V12 H/L +- bytes 5,6 - V5 H/L +case CMD_GETUIVAL1: +- bytes 3,4 - I12 H/L +- bytes 5,6 - V33 H/L +Voltage is in V/100, Current is in mA diff --git a/STM32/TSYS_controller/adc.c b/STM32/TSYS_controller/adc.c new file mode 100644 index 0000000..9d7ace9 --- /dev/null +++ b/STM32/TSYS_controller/adc.c @@ -0,0 +1,167 @@ +/* + * This file is part of the TSYS_controller project. + * Copyright 2019 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" + +/** + * @brief ADC_array - array for ADC channels with median filtering: + * 0..3 - external channels + * 4 - internal Tsens + * 5 - Vref + */ +#define TSENS_CHAN (NUMBER_OF_ADC_CHANNELS-2) +#define VREF_CHAN (NUMBER_OF_ADC_CHANNELS-1) +static uint16_t ADC_array[NUMBER_OF_ADC_CHANNELS*9]; + +/* + * ADC channels: + * IN0 - V12 + * IN1 - V5 + * IN3 - I12 + * IN6 - V3.3 + * IN16- temperature sensor + * IN17- vref + */ +void adc_setup(){ + uint16_t ctr = 0; // 0xfff0 - more than 1.3ms + // Enable clocking + /* (1) Enable the peripheral clock of the ADC */ + /* (2) Start HSI14 RC oscillator */ + /* (3) Wait HSI14 is ready */ + RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; /* (1) */ + RCC->CR2 |= RCC_CR2_HSI14ON; /* (2) */ + while ((RCC->CR2 & RCC_CR2_HSI14RDY) == 0 && ++ctr < 0xfff0){}; /* (3) */ + // calibration + /* (1) Ensure that ADEN = 0 */ + /* (2) Clear ADEN */ + /* (3) Launch the calibration by setting ADCAL */ + /* (4) Wait until ADCAL=0 */ + if ((ADC1->CR & ADC_CR_ADEN) != 0){ /* (1) */ + ADC1->CR &= (uint32_t)(~ADC_CR_ADEN); /* (2) */ + } + ADC1->CR |= ADC_CR_ADCAL; /* (3) */ + ctr = 0; // ADC calibration time is 5.9us + while ((ADC1->CR & ADC_CR_ADCAL) != 0 && ++ctr < 0xfff0){}; /* (4) */ + // enable ADC + ctr = 0; + do{ + ADC1->CR |= ADC_CR_ADEN; + }while ((ADC1->ISR & ADC_ISR_ADRDY) == 0 && ++ctr < 0xfff0); + // configure ADC + /* (1) Select HSI14 by writing 00 in CKMODE (reset value) */ + /* (2) Select the continuous mode */ + /* (3) Select CHSEL0,1,3,6 - ADC inputs, 16,17 - t. sensor and vref */ + /* (4) Select a sampling mode of 111 i.e. 239.5 ADC clk to be greater than 17.1us */ + /* (5) Wake-up the VREFINT and Temperature sensor (only for VBAT, Temp sensor and VRefInt) */ + // ADC1->CFGR2 &= ~ADC_CFGR2_CKMODE; /* (1) */ + ADC1->CFGR1 |= ADC_CFGR1_CONT; /* (2)*/ + ADC1->CHSELR = ADC_CHSELR_CHSEL0 | ADC_CHSELR_CHSEL1 | ADC_CHSELR_CHSEL3 | + ADC_CHSELR_CHSEL6 | ADC_CHSELR_CHSEL16 | ADC_CHSELR_CHSEL17; /* (3)*/ + ADC1->SMPR |= ADC_SMPR_SMP_0 | ADC_SMPR_SMP_1 | ADC_SMPR_SMP_2; /* (4) */ + ADC->CCR |= ADC_CCR_TSEN | ADC_CCR_VREFEN; /* (5) */ + // configure DMA for ADC + // DMA for AIN + /* (1) Enable the peripheral clock on DMA */ + /* (2) Enable DMA transfer on ADC and circular mode */ + /* (3) Configure the peripheral data register address */ + /* (4) Configure the memory address */ + /* (5) Configure the number of DMA tranfer to be performs on DMA channel 1 */ + /* (6) Configure increment, size, interrupts and circular mode */ + /* (7) Enable DMA Channel 1 */ + RCC->AHBENR |= RCC_AHBENR_DMA1EN; /* (1) */ + ADC1->CFGR1 |= ADC_CFGR1_DMAEN | ADC_CFGR1_DMACFG; /* (2) */ + DMA1_Channel1->CPAR = (uint32_t) (&(ADC1->DR)); /* (3) */ + DMA1_Channel1->CMAR = (uint32_t)(ADC_array); /* (4) */ + DMA1_Channel1->CNDTR = NUMBER_OF_ADC_CHANNELS * 9; /* (5) */ + DMA1_Channel1->CCR |= DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0 | DMA_CCR_CIRC; /* (6) */ + DMA1_Channel1->CCR |= DMA_CCR_EN; /* (7) */ + ADC1->CR |= ADC_CR_ADSTART; /* start the ADC conversions */ +} + + +/** + * @brief getADCval - calculate median value for `nch` channel + * @param nch - number of channel + * @return + */ +uint16_t getADCval(int nch){ + int i, addr = nch; + register uint16_t temp; +#define PIX_SORT(a,b) { if ((a)>(b)) PIX_SWAP((a),(b)); } +#define PIX_SWAP(a,b) { 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 +} + +// return MCU temperature (degrees of celsius * 10) +int32_t getMCUtemp(){ + getVdd(); + // make correction on Vdd value +// int32_t temperature = (int32_t)ADC_array[4] * VddValue / 330; + int32_t ADval = getADCval(TSENS_CHAN); + int32_t temperature = (int32_t) *TEMP30_CAL_ADDR - ADval; + temperature *= (int32_t)(1100 - 300); + temperature /= (int32_t)(*TEMP30_CAL_ADDR - *TEMP110_CAL_ADDR); + temperature += 300; + return(temperature); +} + +// return Vdd * 100 (V) +uint32_t getVdd(){ + uint32_t vdd = ((uint32_t) *VREFINT_CAL_ADDR) * (uint32_t)330; // 3.3V + vdd /= getADCval(VREF_CHAN); + return vdd; +} + +static inline uint32_t Ufromadu(uint8_t nch, uint32_t vdd){ + uint32_t ADU = getADCval(nch); + ADU *= vdd; + ADU >>= 12; // /4096 + return ADU; +} + +/** + * @brief getUval - calculate U & I + * @return array with members: + * 0 - V12 * 100V (U12 = 12Vin/4.93) + * 1 - V5 * 100V (U5 = 5Vin /2) + * 2 - I12 mA (U = 1V/1A) + * 3 - V3.3* 100V (U3.3= 3.3Vin/2) + */ +uint16_t *getUval(){ + static uint16_t Uval[4]; + uint32_t vdd = getVdd(); + uint32_t val = Ufromadu(0, vdd) * 493; + Uval[0] = (uint16_t)(val / 100); + Uval[1] = (uint16_t)(Ufromadu(1, vdd) << 1); + val = getADCval(2) * vdd * 10; + Uval[2] = (uint16_t)(val >> 12); + Uval[3] = (uint16_t)(Ufromadu(3, vdd) << 1); + return Uval; +} diff --git a/STM32/TSYS_controller/adc.h b/STM32/TSYS_controller/adc.h new file mode 100644 index 0000000..62ce781 --- /dev/null +++ b/STM32/TSYS_controller/adc.h @@ -0,0 +1,31 @@ +/* + * This file is part of the TSYS_controller project. + * Copyright 2019 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 . + */ + +#ifndef ADC_H +#define ADC_H +#include "stm32f0.h" + +#define NUMBER_OF_ADC_CHANNELS (6) + +int32_t getMCUtemp(); +uint32_t getVdd(); +uint16_t getADCval(int nch); +void adc_setup(); +uint16_t *getUval(); + +#endif // ADC_H diff --git a/STM32/TSYS_controller/can.c b/STM32/TSYS_controller/can.c index 60b23e8..ed95b6d 100644 --- a/STM32/TSYS_controller/can.c +++ b/STM32/TSYS_controller/can.c @@ -23,7 +23,7 @@ #include // memcpy #include "can.h" #include "hardware.h" -#include "usart.h" +#include "proto.h" // incoming message buffer size #define CAN_INMESSAGE_SIZE (6) @@ -56,24 +56,17 @@ static int CAN_messagebuf_push(CAN_message *msg){ memcpy(&messages[first_free_idx++], msg, sizeof(CAN_message)); // need to roll? if(first_free_idx == CAN_INMESSAGE_SIZE) first_free_idx = 0; - #ifdef EBUG - MSG("1st free: "); usart_putchar('0' + first_free_idx); newline(); - #endif return 0; } // pop message from buffer CAN_message *CAN_messagebuf_pop(){ if(first_nonfree_idx < 0) return NULL; - #ifdef EBUG - MSG("read from idx "); usart_putchar('0' + first_nonfree_idx); newline(); - #endif 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; - MSG("refresh buffer\n"); } return msg; } @@ -81,7 +74,7 @@ CAN_message *CAN_messagebuf_pop(){ // get CAN address data from GPIO pins void readCANID(){ uint8_t CAN_addr = READ_CAN_INV_ADDR(); - Controller_address = ~CAN_addr & 0x7; + Controller_address = ~CAN_addr & 0x0f; CANID = (CAN_ID_PREFIX & CAN_ID_MASK) | Controller_address; } @@ -168,42 +161,14 @@ void can_proc(){ RCC->APB1RSTR &= ~RCC_APB1RSTR_CANRST; can_status = CAN_ERROR; } -#ifdef EBUG - static uint32_t esr, msr, tsr; - uint32_t msr_now = CAN->MSR & 0xf; - if(esr != CAN->ESR || msr != msr_now || tsr != CAN->TSR){ - MSG("Timestamp: "); - printu(Tms); - newline(); - } - if((CAN->ESR) != esr){ - usart_putchar(((CAN->ESR & CAN_ESR_BOFF) != 0) + '0'); - esr = CAN->ESR; - MSG("CAN->ESR: "); - printuhex(esr); newline(); - } - if(msr_now != msr){ - msr = msr_now; - MSG("CAN->MSR & 0xf: "); - printuhex(msr); newline(); - } - if(CAN->TSR != tsr){ - tsr = CAN->TSR; - MSG("CAN->TSR: "); - printuhex(tsr); newline(); - } -#endif } CAN_status can_send(uint8_t *msg, uint8_t len, uint16_t target_id){ - LED_on(LED1); // turn ON LED1 at first data sent/receive + if(!noLED) LED_on(LED1); // turn ON LED1 at first data sent/receive uint8_t mailbox = 0; // check first free mailbox if(CAN->TSR & (CAN_TSR_TME)){ mailbox = (CAN->TSR & CAN_TSR_CODE) >> 24; - #ifdef EBUG - MSG("select "); usart_putchar('0'+mailbox); SEND(" mailbox\n"); - #endif }else{ // no free mailboxes return CAN_BUSY; } @@ -212,18 +177,25 @@ CAN_status can_send(uint8_t *msg, uint8_t len, uint16_t target_id){ 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]; } @@ -238,12 +210,7 @@ static void can_process_fifo(uint8_t fifo_num){ if(fifo_num > 1) return; CAN_FIFOMailBox_TypeDef *box = &CAN->sFIFOMailBox[fifo_num]; volatile uint32_t *RFxR = (fifo_num) ? &CAN->RF1R : &CAN->RF0R; - LED_on(LED1); // turn ON LED1 at first data sent/receive - MSG("Receive, RDTR="); - #ifdef EBUG - printuhex(box->RDTR); - newline(); - #endif + if(!noLED) LED_on(LED1); // turn ON LED1 at first data sent/receive // 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 @@ -257,18 +224,25 @@ static void can_process_fifo(uint8_t fifo_num){ 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; } @@ -288,9 +262,6 @@ void cec_can_isr(){ CAN->RF1R &= ~CAN_RF1R_FOVR1; can_status = CAN_FIFO_OVERRUN; } - #ifdef EBUG - if(can_status == CAN_FIFO_OVERRUN) MSG("fifo 0 overrun\n"); - #endif if(CAN->MSR & CAN_MSR_ERRI){ // Error CAN->MSR &= ~CAN_MSR_ERRI; // request abort for problem mailbox diff --git a/STM32/TSYS_controller/can.h b/STM32/TSYS_controller/can.h index d885b51..a8ad07a 100644 --- a/STM32/TSYS_controller/can.h +++ b/STM32/TSYS_controller/can.h @@ -26,8 +26,8 @@ #include "hardware.h" -// identifier mask (for ORing with Controller_address -#define CAN_ID_MASK ((uint16_t)0x7F8) +// identifier mask +#define CAN_ID_MASK ((uint16_t)0x7F0) // prefix of identifiers #define CAN_ID_PREFIX ((uint16_t)0xAAA) // this is master - Controller_address==0 diff --git a/STM32/TSYS_controller/can_process.c b/STM32/TSYS_controller/can_process.c index 1ac2ebd..46f6ef4 100644 --- a/STM32/TSYS_controller/can_process.c +++ b/STM32/TSYS_controller/can_process.c @@ -20,36 +20,82 @@ * MA 02110-1301, USA. * */ -#include "can_process.h" -#include "sensors_manage.h" +#include "adc.h" #include "can.h" -#include "usart.h" +#include "can_process.h" +#include "proto.h" +#include "sensors_manage.h" extern volatile uint32_t Tms; // timestamp data +// id of master - all data will be sent to it +static uint16_t master_id = MASTER_ID; + +static inline void sendmcut(uint8_t *data){ + uint8_t t[3]; + uint16_t T = getMCUtemp(); + t[0] = data[1]; // command itself + t[1] = (T >> 8) & 0xff; // H + t[2] = T & 0xff; // L + can_send_data(t,3); +} + +static inline void senduival(){ + uint8_t buf[5]; + uint16_t *vals = getUval(); + buf[0] = CMD_GETUIVAL0; // V12 and V5 + buf[1] = vals[0] >> 8; // H + buf[2] = vals[0] & 0xff;// L + buf[3] = vals[1] >> 8; // -//- + buf[4] = vals[1] & 0xff; + can_send_data(buf, 5); + buf[0] = CMD_GETUIVAL1; // I12 and V3.3 + buf[1] = vals[2] >> 8; // H + buf[2] = vals[2] & 0xff;// L + buf[3] = vals[3] >> 8; // -//- + buf[4] = vals[3] & 0xff; + can_send_data(buf, 5); +} + +static inline void showui(char *v1, char *v2, uint8_t *data){ + char N = '0' + data[1]; + addtobuf(v1); + bufputchar(N); + bufputchar('='); + uint16_t v = data[3]<<8 | data[4]; + printu(v); + newline(); + addtobuf(v2); + bufputchar(N); + bufputchar('='); + v = data[5]<<8 | data[6]; + printu(v); +} void can_messages_proc(){ CAN_message *can_mesg = CAN_messagebuf_pop(); if(!can_mesg) return; // no data in buffer uint8_t len = can_mesg->length; + IWDG->KR = IWDG_REFRESH; #ifdef EBUG - SEND("got message, len: "); usart_putchar('0' + len); + SEND("got message, len: "); bufputchar('0' + len); SEND(", data: "); uint8_t ctr; for(ctr = 0; ctr < len; ++ctr){ printuhex(can_mesg->data[ctr]); - usart_putchar(' '); + bufputchar(' '); } newline(); #endif uint8_t *data = can_mesg->data, b[2]; b[0] = data[1]; + int16_t t; if(data[0] == COMMAND_MARK){ // process commands if(len < 2) return; switch(data[1]){ case CMD_DUMMY0: case CMD_DUMMY1: SEND("DUMMY"); - usart_putchar('0' + (data[1]==CMD_DUMMY0 ? 0 : 1)); + bufputchar('0' + (data[1]==CMD_DUMMY0 ? 0 : 1)); newline(); break; case CMD_PING: // pong @@ -83,34 +129,62 @@ void can_messages_proc(){ case CMD_REINIT_I2C: i2c_setup(CURRENT_SPEED); break; + case CMD_CHANGE_MASTER_B: + master_id = BCAST_ID; + break; + case CMD_CHANGE_MASTER: + master_id = MASTER_ID; + break; + case CMD_GETMCUTEMP: + sendmcut(data); + break; + case CMD_GETUIVAL: + senduival(); + break; } }else if(data[0] == DATA_MARK){ // process received data if(len < 3) return; switch(data[2]){ case CMD_PING: SEND("PONG"); - usart_putchar('0' + data[1]); + bufputchar('0' + data[1]); break; case CMD_SENSORS_STATE: SEND("SSTATE"); - usart_putchar('0' + data[1]); - usart_putchar('='); + bufputchar('0' + data[1]); + bufputchar('='); printu(data[3]); break; case CMD_START_MEASUREMENT: // temperature if(len != 6) return; - usart_putchar('T'); - usart_putchar('0' + data[1]); - usart_putchar('_'); + bufputchar('T'); + bufputchar('0' + data[1]); + bufputchar('_'); printu(data[3]); - usart_putchar('='); - int16_t t = data[4]<<8 | data[5]; + bufputchar('='); + t = data[4]<<8 | data[5]; if(t < 0){ t = -t; - usart_putchar('-'); + bufputchar('-'); } printu(t); -#pragma message("TODO: process received T over USB!") + break; + case CMD_GETMCUTEMP: + addtobuf("TMCU"); + bufputchar('0' + data[1]); + bufputchar('='); + t = data[3]<<8 | data[4]; + if(t < 0){ + bufputchar('-'); + t = -t; + } + printu(t); + break; + case CMD_GETUIVAL0: // V12 and V5 + showui("V12_", "V5_", data); + break; + case CMD_GETUIVAL1: // I12 and V3.3 + showui("I12_", "V33_", data); break; default: SEND("UNKNOWN_DATA"); @@ -124,6 +198,7 @@ static CAN_status try2send(uint8_t *buf, uint8_t len, uint16_t id){ uint32_t Tstart = Tms; while(Tms - Tstart < SEND_TIMEOUT_MS){ if(CAN_OK == can_send(buf, len, id)) return CAN_OK; + IWDG->KR = IWDG_REFRESH; } SEND("CAN_BUSY\n"); return CAN_BUSY; @@ -136,7 +211,7 @@ static CAN_status try2send(uint8_t *buf, uint8_t len, uint16_t id){ * @param cmd - command to send */ CAN_status can_send_cmd(uint16_t targetID, uint8_t cmd){ - if(Controller_address != 0 && cmd != CMD_DUMMY0 && cmd != CMD_DUMMY1) return CAN_NOTMASTER; + //if(Controller_address != 0 && cmd != CMD_DUMMY0 && cmd != CMD_DUMMY1) return CAN_NOTMASTER; uint8_t buf[2]; buf[0] = COMMAND_MARK; buf[1] = cmd; @@ -151,7 +226,7 @@ CAN_status can_send_data(uint8_t *data, uint8_t len){ buf[1] = Controller_address; int i; for(i = 0; i < len; ++i) buf[i+2] = *data++; - return try2send(buf, len+2, MASTER_ID); + return try2send(buf, len+2, master_id); } /** diff --git a/STM32/TSYS_controller/can_process.h b/STM32/TSYS_controller/can_process.h index f9a161c..68b60db 100644 --- a/STM32/TSYS_controller/can_process.h +++ b/STM32/TSYS_controller/can_process.h @@ -41,6 +41,12 @@ typedef enum{ CMD_LOW_SPEED, // low I2C speed (10kHz) CMD_HIGH_SPEED, // high I2C speed (100kHz) CMD_REINIT_I2C, // reinit I2C with current speed + CMD_CHANGE_MASTER_B, // change master id to broadcast + CMD_CHANGE_MASTER, // change master id to 0 + CMD_GETMCUTEMP, // MCU temperature value + CMD_GETUIVAL, // request to get values of V12, V5, I12 and V3.3 + CMD_GETUIVAL0, // answer with values of V12 and V5 + CMD_GETUIVAL1, // answer with values of I12 and V3.3 // dummy commands for test purposes CMD_DUMMY0 = 0xDA, CMD_DUMMY1 = 0xAD diff --git a/STM32/TSYS_controller/hardware.c b/STM32/TSYS_controller/hardware.c index 5ee1a68..f1a0e8f 100644 --- a/STM32/TSYS_controller/hardware.c +++ b/STM32/TSYS_controller/hardware.c @@ -22,7 +22,6 @@ */ #include "hardware.h" -#include "usart.h" I2C_SPEED curI2Cspeed = LOW_SPEED; @@ -43,12 +42,14 @@ void gpio_setup(void){ // PA8 - power enable GPIOA->MODER = (GPIOA->MODER & ~(GPIO_MODER_MODER8)) | GPIO_MODER_MODER8_O; - // PA13..15 - CAN address, pullup inputs + // PA13..15 (low bits) + PB15 (high bit) - CAN address, pullup inputs GPIOA->PUPDR = (GPIOA->PUPDR & ~(GPIO_PUPDR_PUPDR13 | GPIO_PUPDR_PUPDR14 | GPIO_PUPDR_PUPDR15) ) | GPIO_PUPDR_PUPDR13_0 | GPIO_PUPDR_PUPDR14_0 | GPIO_PUPDR_PUPDR15_0; + GPIOB->PUPDR = (GPIOB->PUPDR & ~(GPIO_PUPDR_PUPDR15)) | + GPIO_PUPDR_PUPDR15_0; pin_set(LED0_port, LED0_pin); // clear LEDs pin_set(LED1_port, LED1_pin); } @@ -59,7 +60,6 @@ void i2c_setup(I2C_SPEED speed){ }else{ curI2Cspeed = speed; } - MSG("setup I2C\n"); I2C1->CR1 = 0; #if I2CPINS == 910 /* diff --git a/STM32/TSYS_controller/hardware.h b/STM32/TSYS_controller/hardware.h index e795a42..33ef58f 100644 --- a/STM32/TSYS_controller/hardware.h +++ b/STM32/TSYS_controller/hardware.h @@ -75,7 +75,7 @@ #define SENSORS_OVERCURNT() ((1<<3) != (GPIOB->IDR & (1<<3))) // CAN address - PA13..PA15 -#define READ_CAN_INV_ADDR() ((GPIOA->IDR & (0x7<<13))>>13) +#define READ_CAN_INV_ADDR() (((GPIOA->IDR & (0x7<<13))>>13) | ((GPIOB->IDR & (1<<15)) >> 12)) extern uint8_t Controller_address; typedef enum{ diff --git a/STM32/TSYS_controller/i2c.c b/STM32/TSYS_controller/i2c.c index 57deba5..16ced93 100644 --- a/STM32/TSYS_controller/i2c.c +++ b/STM32/TSYS_controller/i2c.c @@ -20,10 +20,8 @@ * MA 02110-1301, USA. * */ -#include "stm32f0.h" #include "hardware.h" #include "i2c.h" -#include "usart.h" /** * I2C for TSYS01 @@ -53,36 +51,38 @@ static uint32_t cntr; uint8_t write_i2c(uint8_t addr, uint8_t data){ cntr = Tms; I2C1->ICR = 0x3f38; // clear all errors - while(I2C1->ISR & I2C_ISR_BUSY) if(Tms - cntr > I2C_TIMEOUT){ - //MSG("always busy\n"); + while(I2C1->ISR & I2C_ISR_BUSY){ + IWDG->KR = IWDG_REFRESH; + if(Tms - cntr > I2C_TIMEOUT){ return 0; // check busy - } + }} cntr = Tms; - while(I2C1->CR2 & I2C_CR2_START) if(Tms - cntr > I2C_TIMEOUT){ - //MSG("always start\n"); + while(I2C1->CR2 & I2C_CR2_START){ + IWDG->KR = IWDG_REFRESH; + if(Tms - cntr > I2C_TIMEOUT){ return 0; // check start - } + }} //I2C1->ICR = 0x3f38; // clear all errors I2C1->CR2 = 1<<16 | addr | I2C_CR2_AUTOEND; // 1 byte, autoend // now start transfer I2C1->CR2 |= I2C_CR2_START; cntr = Tms; while(!(I2C1->ISR & I2C_ISR_TXIS)){ // ready to transmit + IWDG->KR = IWDG_REFRESH; if(I2C1->ISR & I2C_ISR_NACKF){ I2C1->ICR |= I2C_ICR_NACKCF; - //I2C1->ICR = 0x3f38; - //MSG("NACK\n"); return 0; } if(Tms - cntr > I2C_TIMEOUT){ - //I2C1->ICR = 0x3f38; - //MSG("Timeout\n"); return 0; } } I2C1->TXDR = data; // send data // wait for data gone - while(I2C1->ISR & I2C_ISR_BUSY) if(Tms - cntr > I2C_TIMEOUT){break;} + while(I2C1->ISR & I2C_ISR_BUSY){ + IWDG->KR = IWDG_REFRESH; + if(Tms - cntr > I2C_TIMEOUT){break;} + } return 1; } @@ -94,15 +94,17 @@ uint8_t read_i2c(uint8_t addr, uint32_t *data, uint8_t nbytes){ uint32_t result = 0; cntr = Tms; //MSG("read_i2c\n"); - while(I2C1->ISR & I2C_ISR_BUSY) if(Tms - cntr > I2C_TIMEOUT){ - //MSG("always busy\n"); + while(I2C1->ISR & I2C_ISR_BUSY){ + IWDG->KR = IWDG_REFRESH; + if(Tms - cntr > I2C_TIMEOUT){ return 0; // check busy - } + }} cntr = Tms; - while(I2C1->CR2 & I2C_CR2_START) if(Tms - cntr > I2C_TIMEOUT){ - //MSG("always start\n"); + while(I2C1->CR2 & I2C_CR2_START){ + IWDG->KR = IWDG_REFRESH; + if(Tms - cntr > I2C_TIMEOUT){ return 0; // check start - } + }} // I2C1->ICR = 0x3f38; // clear all errors // read N bytes I2C1->CR2 = (nbytes<<16) | addr | 1 | I2C_CR2_AUTOEND | I2C_CR2_RD_WRN; @@ -111,15 +113,12 @@ uint8_t read_i2c(uint8_t addr, uint32_t *data, uint8_t nbytes){ cntr = Tms; for(i = 0; i < nbytes; ++i){ while(!(I2C1->ISR & I2C_ISR_RXNE)){ // wait for data + IWDG->KR = IWDG_REFRESH; if(I2C1->ISR & I2C_ISR_NACKF){ I2C1->ICR |= I2C_ICR_NACKCF; - //I2C1->ICR = 0x3f38; - //MSG("NACK\n"); return 0; } if(Tms - cntr > I2C_TIMEOUT){ - //I2C1->ICR = 0x3f38; - //MSG("Timeout\n"); return 0; } } diff --git a/STM32/TSYS_controller/i2c.h b/STM32/TSYS_controller/i2c.h index a6ae255..7c6ab65 100644 --- a/STM32/TSYS_controller/i2c.h +++ b/STM32/TSYS_controller/i2c.h @@ -21,6 +21,8 @@ * */ +#include "stm32f0.h" + // timeout of I2C bus in ms #define I2C_TIMEOUT (100) // CSB=1, address 1110110 diff --git a/STM32/TSYS_controller/main.c b/STM32/TSYS_controller/main.c index 981e8a8..da20f4b 100644 --- a/STM32/TSYS_controller/main.c +++ b/STM32/TSYS_controller/main.c @@ -18,14 +18,15 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ - -#include "hardware.h" -#include "usart.h" -#include "i2c.h" -#include "sensors_manage.h" +#include "adc.h" #include "can.h" #include "can_process.h" +#include "hardware.h" +#include "i2c.h" #include "proto.h" +#include "sensors_manage.h" +#include "usart.h" +#include "usb.h" #pragma message("USARTNUM=" STR(USARTNUM)) #pragma message("I2CPINS=" STR(I2CPINS)) @@ -67,12 +68,13 @@ static void iwdg_setup(){ int main(void){ uint32_t lastT = 0, lastS = 0; uint8_t gotmeasurement = 0; + char inbuf[256]; sysreset(); SysTick_Config(6000, 1); gpio_setup(); + adc_setup(); usart_setup(); i2c_setup(LOW_SPEED); - iwdg_setup(); CAN_setup(); SEND("Greetings! My address is "); @@ -86,11 +88,13 @@ int main(void){ SEND("SOFTRESET=1\n"); } RCC->CSR |= RCC_CSR_RMVF; // remove reset flags + USB_setup(); + iwdg_setup(); while (1){ IWDG->KR = IWDG_REFRESH; // refresh watchdog if(lastT > Tms || Tms - lastT > 499){ - LED_blink(LED0); + if(!noLED) LED_blink(LED0); lastT = Tms; // send dummy command to noone to test CAN bus can_send_cmd(NOONE_ID, CMD_DUMMY0); @@ -104,7 +108,7 @@ int main(void){ if(stat == CAN_FIFO_OVERRUN){ SEND("CAN bus fifo overrun occured!\n"); }else if(stat == CAN_ERROR){ - LED_off(LED1); + if(!noLED) LED_off(LED1); CAN_setup(); canerror = 1; } @@ -117,9 +121,19 @@ int main(void){ }else{ gotmeasurement = 0; } - if(usartrx()){ // usart1 received data, store in in buffer - cmd_parser(); + usb_proc(); + uint8_t r = 0; + if((r = USB_receive(inbuf, 255))){ + inbuf[r] = 0; + cmd_parser(inbuf, 1); } + if(usartrx()){ // usart1 received data, store in in buffer + char *txt = NULL; + r = usart_getline(&txt); + txt[r] = 0; + cmd_parser(txt, 0); + } + sendbuf(); } return 0; } diff --git a/STM32/TSYS_controller/proto.c b/STM32/TSYS_controller/proto.c index 6f9574e..047bda0 100644 --- a/STM32/TSYS_controller/proto.c +++ b/STM32/TSYS_controller/proto.c @@ -20,33 +20,115 @@ * MA 02110-1301, USA. * */ -#include "hardware.h" -#include "usart.h" +#include "adc.h" #include "can.h" #include "can_process.h" +#include "hardware.h" +#include "proto.h" #include "sensors_manage.h" +#include "usart.h" +#include "usb.h" +#include // strlen, strcpy( extern volatile uint8_t canerror; +static char buff[UARTBUFSZ+1], *bptr = buff; +static uint8_t blen = 0, USBcmd = 0; +// LEDs are OFF by default +uint8_t noLED = 1; + +void sendbuf(){ + IWDG->KR = IWDG_REFRESH; + if(blen == 0) return; + *bptr = 0; + if(USBcmd) USB_send(buff); + else while(LINE_BUSY == usart_send(buff, blen)){IWDG->KR = IWDG_REFRESH;} + bptr = buff; + blen = 0; +} + +void addtobuf(const char *txt){ + IWDG->KR = IWDG_REFRESH; + int l = strlen(txt); + if(l > UARTBUFSZ){ + sendbuf(); + if(USBcmd) USB_send(txt); + else while(LINE_BUSY == usart_send_blocking(txt, l)){IWDG->KR = IWDG_REFRESH;} + }else{ + if(blen+l > UARTBUFSZ){ + sendbuf(); + } + strcpy(bptr, txt); + bptr += l; + } + *bptr = 0; + blen += l; +} + +void bufputchar(char ch){ + if(blen > UARTBUFSZ-1){ + sendbuf(); + } + *bptr++ = ch; + ++blen; +} + static void CANsend(uint16_t targetID, uint8_t cmd, char echo){ if(CAN_OK == can_send_cmd(targetID, cmd)){ - usart_putchar(echo); + bufputchar(echo); + bufputchar('\n'); + } +} + +// show all ADC values +static inline void showADCvals(){ + char msg[] = "ADCn="; + for(int n = 0; n < NUMBER_OF_ADC_CHANNELS; ++n){ + msg[3] = n + '0'; + addtobuf(msg); + printu(getADCval(n)); newline(); } } -void cmd_parser(){ - char *txt = NULL; - int16_t L = 0, ID = BCAST_ID; - L = usart_getline(&txt); +static inline void printmcut(){ + SEND("MCUT="); + int32_t T = getMCUtemp(); + if(T < 0){ + bufputchar('-'); + T = -T; + } + printu(T); + newline(); +} + +static inline void showUIvals(){ + uint16_t *vals = getUval(); + SEND("V12="); printu(vals[0]); + SEND("\nV5="); printu(vals[1]); + SEND("\nV33="); printu(vals[3]); + SEND("\nI12="); printu(vals[2]); + newline(); +} + +/** + * @brief cmd_parser - command parsing + * @param txt - buffer with commands & data + * @param isUSB - == 1 if data got from USB + */ +void cmd_parser(char *txt, uint8_t isUSB){ + USBcmd = isUSB; + int16_t L = strlen(txt), ID = BCAST_ID; char _1st = txt[0]; + sendbuf(); if(_1st >= '0' && _1st < '8'){ // send command to Nth controller, not broadcast if(L == 3){ // with '\n' at end! - if(_1st == '0'){ - usart_putchar(txt[1]); + /*if(_1st == '0'){ + bufputchar(txt[1]); _1st = txt[1] + 'a' - 'A'; // change network command to local - newline(); - }else{ + bufputchar('\n'); + }else */ + { ID = (CAN_ID_PREFIX & CAN_ID_MASK) | (_1st - '0'); _1st = txt[1]; } @@ -55,6 +137,9 @@ void cmd_parser(){ } }else if(L != 2) _1st = '?'; switch(_1st){ + case 'a': + showADCvals(); + break; case 'B': CANsend(ID, CMD_DUMMY0, _1st); break; @@ -93,12 +178,40 @@ void cmd_parser(){ printuhex(getCANID()); newline(); break; + case 'J': + CANsend(ID, CMD_GETMCUTEMP, _1st); + break; + case 'j': + printmcut(); + break; + case 'K': + CANsend(ID, CMD_GETUIVAL, _1st); + break; + case 'k': + showUIvals(); + break; case 'L': CANsend(ID, CMD_LOW_SPEED, _1st); break; case 'l': i2c_setup(LOW_SPEED); break; + case 'M': + CANsend(ID, CMD_CHANGE_MASTER_B, _1st); + break; + case 'm': + CANsend(ID, CMD_CHANGE_MASTER, _1st); + break; + case 'O': + noLED = 0; + SEND("LED on\n"); + break; + case 'o': + noLED = 1; + LED_off(LED0); + LED_off(LED1); + SEND("LED off\n"); + break; case 'P': CANsend(ID, CMD_PING, _1st); break; @@ -120,12 +233,13 @@ void cmd_parser(){ case 't': if(!sensors_scan_mode) sensors_start(); break; + break; case 'u': SEND("CANERROR="); if(canerror){ canerror = 0; - usart_putchar('1'); - }else usart_putchar('0'); + bufputchar('1'); + }else bufputchar('0'); newline(); break; case 'V': @@ -139,13 +253,14 @@ void cmd_parser(){ break; case 'z': SEND("SSTATE0="); - usart_putchar(sensors_get_state()); + printu(sensors_get_state()); newline(); break; default: // help SEND( "ALL little letters - without CAN messaging\n" "0..7 - send command to given controller (0 - this) instead of broadcast\n" + "a - get raw ADC values\n" "B - send broadcast CAN dummy message\n" "c - show coefficients (current)\n" "D - send CAN dummy message to master\n" @@ -154,8 +269,11 @@ void cmd_parser(){ "g - get last CAN address\n" "Hh- high I2C speed\n" "i - reinit CAN (with new address)\n" + "Jj- get MCU temperature\n" + "Kk- get U/I values\n" "Ll- low I2C speed\n" - // "o - turn On sensors\n" + "Mm- change master id to 0 (m) / broadcast (M)\n" + "Oo- turn onboard diagnostic LEDs *O*n or *o*ff (both commands are local)\n" "P - ping everyone over CAN\n" "Rr- reinit I2C\n" "Ss- Start themperature scan\n" @@ -166,4 +284,34 @@ void cmd_parser(){ ); break; } + sendbuf(); +} + +// print 32bit unsigned int +void printu(uint32_t val){ + char buf[11], *bufptr = &buf[10]; + *bufptr = 0; + if(!val){ + *(--bufptr) = '0'; + }else{ + while(val){ + *(--bufptr) = val % 10 + '0'; + val /= 10; + } + } + addtobuf(bufptr); +} + +// print 32bit unsigned int as hex +void printuhex(uint32_t val){ + addtobuf("0x"); + uint8_t *ptr = (uint8_t*)&val + 3; + int i, j; + for(i = 0; i < 4; ++i, --ptr){ + for(j = 1; j > -1; --j){ + uint8_t half = (*ptr >> (4*j)) & 0x0f; + if(half < 10) bufputchar(half + '0'); + else bufputchar(half - 10 + 'a'); + } + } } diff --git a/STM32/TSYS_controller/proto.h b/STM32/TSYS_controller/proto.h index b796d13..5b9f47b 100644 --- a/STM32/TSYS_controller/proto.h +++ b/STM32/TSYS_controller/proto.h @@ -24,6 +24,25 @@ #ifndef __PROTO_H__ #define __PROTO_H__ -void cmd_parser(); +#include "stm32f0.h" + +// macro for static strings +#define SEND(str) do{addtobuf(str);}while(0) + +#ifdef EBUG +#define MSG(str) do{addtobuf(__FILE__ " (L" STR(__LINE__) "): " str);}while(0) +#else +#define MSG(str) +#endif + +#define newline() do{bufputchar('\n');}while(0) + +extern uint8_t noLED; +void cmd_parser(char *buf, uint8_t isUSB); +void addtobuf(const char *txt); +void bufputchar(char ch); +void printu(uint32_t val); +void printuhex(uint32_t val); +void sendbuf(); #endif // __PROTO_H__ diff --git a/STM32/TSYS_controller/sensors_manage.c b/STM32/TSYS_controller/sensors_manage.c index fab0f0a..cdc2547 100644 --- a/STM32/TSYS_controller/sensors_manage.c +++ b/STM32/TSYS_controller/sensors_manage.c @@ -23,7 +23,7 @@ #include "sensors_manage.h" #include "can_process.h" #include "i2c.h" -#include "usart.h" +#include "proto.h" // addtobuf, bufputchar extern volatile uint32_t Tms; uint8_t sensors_scan_mode = 0; // infinite scan mode @@ -133,19 +133,14 @@ static void count_sensors(){ // procedure call each time @ resetting static uint8_t resetproc(){ uint8_t i, ctr = 0; - //SEND("pair "); printu(curr_mul_addr); - //SEND(" : "); for(i = 0; i < 2; ++i){ if(write_i2c(Taddr[i], TSYS01_RESET)){ - //usart_putchar('0' + i); ++ctr; sens_present[i] |= 1<reset\n"); i2c_setup(CURRENT_SPEED); Sstate = SENS_RESETING; lastSensT = Tms; @@ -365,10 +304,8 @@ void sensors_process(){ if(sensors_scan(resetproc)){ count_sensors(); // get total amount of sensors if(Nsens_present){ -//MSG("reset->getcoeff\n"); Sstate = SENS_GET_COEFFS; }else{ // no sensors found -//MSG("reset->off\n"); sensors_off(); } } @@ -376,25 +313,18 @@ void sensors_process(){ break; case SENS_GET_COEFFS: // get coefficients if(sensors_scan(getcoefsproc)){ -//MSG("got coeffs for "); -#if 0 -printu(Nsens_present); -SEND(" sensors ->start\n"); -#endif Sstate = SENS_START_MSRMNT; } break; case SENS_START_MSRMNT: // send all sensors command to start measurements if(sensors_scan(msrtempproc)){ lastSensT = Tms; -//MSG("->wait\n"); Sstate = SENS_WAITING; Ntemp_measured = 0; // reset value of good measurements } break; case SENS_WAITING: // wait for end of conversion if(Tms - lastSensT > CONV_TIME){ -//MSG("->gather\n"); Sstate = SENS_GATHERING; } break; @@ -403,16 +333,6 @@ SEND(" sensors ->start\n"); lastSensT = Tms; NsentOverCAN = 0; Sstate = SENS_SLEEPING; -//MSG("->sleep\n"); - /* - if(Nsens_present == Ntemp_measured){ // All OK, amount of T == amount of sensors -MSG("->sleep\n"); - Sstate = SENS_SLEEPING; - }else{ // reinit I2C & try to start measurements again -MSG("gather error ->start\n"); - i2c_setup(CURRENT_SPEED); - Sstate = SENS_START_MSRMNT; - }*/ } break; case SENS_SLEEPING: // wait for `SLEEP_TIME` till next measurements @@ -424,14 +344,12 @@ MSG("gather error ->start\n"); } if(sensors_scan_mode){ // sleep until next measurement start if(Tms - lastSensT > SLEEP_TIME){ -//MSG("sleep->start\n"); Sstate = SENS_START_MSRMNT; } } } break; case SENS_OVERCURNT: // try to reinit all after overcurrent -//MSG("try to turn on after overcurrent\n"); sensors_on(); break; default: // do nothing diff --git a/STM32/TSYS_controller/sensors_manage.h b/STM32/TSYS_controller/sensors_manage.h index 6ff4b68..4951960 100644 --- a/STM32/TSYS_controller/sensors_manage.h +++ b/STM32/TSYS_controller/sensors_manage.h @@ -40,16 +40,16 @@ extern int16_t Temperatures[MUL_MAX_ADDRESS+1][2]; extern uint8_t sens_present[2]; typedef enum{ - SENS_INITING // power on - ,SENS_RESETING // discovery sensors resetting them - ,SENS_GET_COEFFS // get coefficients from all sensors - ,SENS_SLEEPING // wait for a time to process measurements - ,SENS_START_MSRMNT // send command 2 start measurement - ,SENS_WAITING // wait for measurements end - ,SENS_GATHERING // collect information - ,SENS_OFF // sensors' power is off by external command - ,SENS_OVERCURNT // overcurrent detected @ any stage - ,SENS_OVERCURNT_OFF // sensors' power is off due to continuous overcurrent + SENS_INITING // 0 power on + ,SENS_RESETING // 1 discovery sensors resetting them + ,SENS_GET_COEFFS // 2 get coefficients from all sensors + ,SENS_SLEEPING // 3 wait for a time to process measurements + ,SENS_START_MSRMNT // 4 send command 2 start measurement + ,SENS_WAITING // 5 wait for measurements end + ,SENS_GATHERING // 6 collect information + ,SENS_OFF // 7 sensors' power is off by external command + ,SENS_OVERCURNT // 8 overcurrent detected @ any stage + ,SENS_OVERCURNT_OFF // 9 sensors' power is off due to continuous overcurrent } SensorsState; SensorsState sensors_get_state(); @@ -58,12 +58,7 @@ void sensors_process(); void sensors_off(); void sensors_on(); void sensors_start(); - void showcoeffs(); void showtemperature(); -#ifdef EBUG -void senstest(char cmd); -#endif - #endif // __SENSORS_MANAGE_H__ diff --git a/STM32/TSYS_controller/tsys01.bin b/STM32/TSYS_controller/tsys01.bin index f015d4d..634d0de 100755 Binary files a/STM32/TSYS_controller/tsys01.bin and b/STM32/TSYS_controller/tsys01.bin differ diff --git a/STM32/TSYS_controller/usart.c b/STM32/TSYS_controller/usart.c index 64b6fc3..b79aa84 100644 --- a/STM32/TSYS_controller/usart.c +++ b/STM32/TSYS_controller/usart.c @@ -1,4 +1,4 @@ -/*us +/* * usart.c * * Copyright 2017 Edward V. Emelianoff @@ -76,24 +76,20 @@ TXstatus usart_send_blocking(const char *str, int len){ bufovr = 0; for(i = 0; i < len; ++i){ USARTX -> TDR = *str++; - while(!(USARTX->ISR & USART_ISR_TXE)); + while(!(USARTX->ISR & USART_ISR_TXE)){IWDG->KR = IWDG_REFRESH;}; } return ALL_OK; } -void usart_putchar(const char ch){ - while(!txrdy); - USARTX -> TDR = ch; - while(!(USARTX->ISR & USART_ISR_TXE)); +void usart_send_blck(const char *str){ + while(!txrdy){IWDG->KR = IWDG_REFRESH;} + bufovr = 0; + while(*str){ + USARTX -> TDR = *str++; + while(!(USARTX->ISR & USART_ISR_TXE)){IWDG->KR = IWDG_REFRESH;}; + } } -void newline(){ - while(!txrdy); - USARTX -> TDR = '\n'; - while(!(USARTX->ISR & USART_ISR_TXE)); -} - - void usart_setup(){ // Nucleo's USART2 connected to VCP proxy of st-link #if USARTNUM == 2 @@ -197,41 +193,6 @@ void usart1_isr(){ } } -// print 32bit unsigned int -void printu(uint32_t val){ - char bufa[11], bufb[10]; - int l = 0, bpos = 0; - if(!val){ - bufa[0] = '0'; - l = 1; - }else{ - while(val){ - bufb[l++] = val % 10 + '0'; - val /= 10; - } - int i; - bpos += l; - for(i = 0; i < l; ++i){ - bufa[--bpos] = bufb[i]; - } - } - while(LINE_BUSY == usart_send_blocking(bufa, l+bpos)); -} - -// print 32bit unsigned int as hex -void printuhex(uint32_t val){ - SEND("0x"); - uint8_t *ptr = (uint8_t*)&val + 3; - int i, j; - for(i = 0; i < 4; ++i, --ptr){ - for(j = 1; j > -1; --j){ - uint8_t half = (*ptr >> (4*j)) & 0x0f; - if(half < 10) usart_putchar(half + '0'); - else usart_putchar(half - 10 + 'a'); - } - } -} - #if USARTNUM == 2 void dma1_channel4_5_isr(){ if(DMA1->ISR & DMA_ISR_TCIF4){ // Tx diff --git a/STM32/TSYS_controller/usart.h b/STM32/TSYS_controller/usart.h index ae2da2e..536fc71 100644 --- a/STM32/TSYS_controller/usart.h +++ b/STM32/TSYS_controller/usart.h @@ -29,15 +29,6 @@ #define TIMEOUT_MS (1500) #endif -// macro for static strings -#define SEND(str) do{}while(LINE_BUSY == usart_send_blocking(str, sizeof(str)-1)) - -#ifdef EBUG -#define MSG(str) do{SEND(__FILE__ " (L" STR(__LINE__) "): " str);}while(0) -#else -#define MSG(str) -#endif - typedef enum{ ALL_OK, LINE_BUSY, @@ -53,9 +44,6 @@ void usart_setup(); int usart_getline(char **line); TXstatus usart_send(const char *str, int len); TXstatus usart_send_blocking(const char *str, int len); -void newline(); -void usart_putchar(const char ch); -void printu(uint32_t val); -void printuhex(uint32_t val); +void usart_send_blck(const char *str); #endif // __USART_H__ diff --git a/STM32/TSYS_controller/usb.c b/STM32/TSYS_controller/usb.c new file mode 100644 index 0000000..f7aeb4c --- /dev/null +++ b/STM32/TSYS_controller/usb.c @@ -0,0 +1,182 @@ +/* + * geany_encoding=koi8-r + * usb.c - base functions for different USB types + * + * Copyright 2018 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ + +#include "usb.h" +#include "usb_lib.h" +#include // memcpy, memmove +#include "usart.h" + +// incoming buffer size +#define IDATASZ (256) +static uint8_t incoming_data[IDATASZ]; +static uint8_t ovfl = 0; +static uint16_t idatalen = 0; +static int8_t usbON = 0; // ==1 when USB fully configured +static volatile uint8_t tx_succesfull = 0; + +// interrupt IN handler (never used?) +static uint16_t EP1_Handler(ep_t ep){ + uint8_t ep0buf[11]; + if (ep.rx_flag){ + EP_Read(1, ep0buf); + ep.status = SET_VALID_TX(ep.status); + ep.status = KEEP_STAT_RX(ep.status); + }else if (ep.tx_flag){ + ep.status = SET_VALID_RX(ep.status); + ep.status = SET_STALL_TX(ep.status); + } + return ep.status; +} + +// data IN/OUT handler +static uint16_t EP23_Handler(ep_t ep){ + if(ep.rx_flag){ + int rd = ep.rx_cnt, rest = IDATASZ - idatalen; + if(rd){ + if(rd <= rest){ + idatalen += EP_Read(2, &incoming_data[idatalen]); + ovfl = 0; + }else{ + ep.status = SET_NAK_RX(ep.status); + ovfl = 1; + return ep.status; + } + } + ep.status = CLEAR_DTOG_RX(ep.status); + ep.status = CLEAR_DTOG_TX(ep.status); + ep.status = SET_STALL_TX(ep.status); + }else if (ep.tx_flag){ + ep.status = KEEP_STAT_TX(ep.status); + tx_succesfull = 1; + } + ep.status = SET_VALID_RX(ep.status); + return ep.status; +} + +void USB_setup(){ + RCC->APB1ENR |= RCC_APB1ENR_CRSEN | RCC_APB1ENR_USBEN; // enable CRS (hsi48 sync) & USB + RCC->CFGR3 &= ~RCC_CFGR3_USBSW; // reset USB + RCC->CR2 |= RCC_CR2_HSI48ON; // turn ON HSI48 + uint32_t tmout = 16000000; + while(!(RCC->CR2 & RCC_CR2_HSI48RDY)){if(--tmout == 0) break; IWDG->KR = IWDG_REFRESH;} + FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY; + CRS->CFGR &= ~CRS_CFGR_SYNCSRC; + CRS->CFGR |= CRS_CFGR_SYNCSRC_1; // USB SOF selected as sync source + CRS->CR |= CRS_CR_AUTOTRIMEN; // enable auto trim + CRS->CR |= CRS_CR_CEN; // enable freq counter & block CRS->CFGR as read-only + RCC->CFGR |= RCC_CFGR_SW; + // allow RESET and CTRM interrupts + USB->CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM; + // clear flags + USB->ISTR = 0; + // and activate pullup + USB->BCDR |= USB_BCDR_DPPU; + NVIC_EnableIRQ(USB_IRQn); +} + +void usb_proc(){ + if(USB_GetState() == USB_CONFIGURE_STATE){ // USB configured - activate other endpoints + if(!usbON){ // endpoints not activated + // make new BULK endpoint + // Buffer have 1024 bytes, but last 256 we use for CAN bus (30.2 of RM: USB main features) + EP_Init(1, EP_TYPE_INTERRUPT, 10, 0, EP1_Handler); // IN1 - transmit + EP_Init(2, EP_TYPE_BULK, 0, USB_RXBUFSZ, EP23_Handler); // OUT2 - receive data + EP_Init(3, EP_TYPE_BULK, USB_TXBUFSZ, 0, EP23_Handler); // IN3 - transmit data + usbON = 1; + } + }else{ + usbON = 0; + } +} + +void USB_send(const char *buf){ + uint16_t l = 0, ctr = 0; + const char *p = buf; + while(*p++) ++l; + while(l){ + IWDG->KR = IWDG_REFRESH; + uint16_t s = (l > USB_TXBUFSZ) ? USB_TXBUFSZ : l; + tx_succesfull = 0; + EP_Write(3, (uint8_t*)&buf[ctr], s); + uint32_t ctra = 1000000; + while(--ctra && tx_succesfull == 0){IWDG->KR = IWDG_REFRESH;} + l -= s; + ctr += s; + } +} + +/** + * @brief USB_receive - read first received text string + * @param buf (i) - buffer for received data + * @param bufsize - its size + * @return amount of received bytes + */ +int USB_receive(char *buf, int bufsize){ + if(bufsize<1 || !idatalen) return 0; + IWDG->KR = IWDG_REFRESH; + int stlen = 0, i; + for(i = 0; i < idatalen; ++i){ + if(incoming_data[i] == '\n'){ + stlen = i+1; + break; + } + } + if(i == idatalen || stlen == 0) return 0; + /* + char x[] = "USB got x:\n"; + x[8] = '0' + stlen; + usart_send_blck(x); + usart_send_blck((char*)incoming_data); + usart_send_blck("\n"); + */ + USB->CNTR = 0; + int sz = (stlen > bufsize) ? bufsize : stlen, rest = idatalen - sz; + memcpy(buf, incoming_data, sz); + buf[sz] = 0; + /* + usart_send_blck("buf:\n"); + usart_send_blck((char*)buf); + usart_send_blck("\n"); + */ + if(rest > 0){ + memmove(incoming_data, &incoming_data[sz], rest); + idatalen = rest; + }else idatalen = 0; + if(ovfl){ + EP23_Handler(endpoints[2]); + uint16_t epstatus = USB->EPnR[2]; + epstatus = CLEAR_DTOG_RX(epstatus); + epstatus = SET_VALID_RX(epstatus); + USB->EPnR[2] = epstatus; + } + USB->CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM; + return sz; +} + +/** + * @brief USB_configured + * @return 1 if USB is in configured state + */ +int USB_configured(){ + return usbON; +} diff --git a/STM32/TSYS_controller/usb.h b/STM32/TSYS_controller/usb.h new file mode 100644 index 0000000..75d22b4 --- /dev/null +++ b/STM32/TSYS_controller/usb.h @@ -0,0 +1,37 @@ +/* + * geany_encoding=koi8-r + * usb.h + * + * Copyright 2018 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ +#pragma once +#ifndef __USB_H__ +#define __USB_H__ + +#include "hardware.h" + +#define BUFFSIZE (64) + +void USB_setup(); +void usb_proc(); +void USB_send(const char *buf); +int USB_receive(char *buf, int bufsize); +int USB_configured(); + +#endif // __USB_H__ diff --git a/STM32/TSYS_controller/usb_defs.h b/STM32/TSYS_controller/usb_defs.h new file mode 100644 index 0000000..6ac673c --- /dev/null +++ b/STM32/TSYS_controller/usb_defs.h @@ -0,0 +1,106 @@ +/* + * geany_encoding=koi8-r + * usb_defs.h + * + * Copyright 2018 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ + +#pragma once +#ifndef __USB_DEFS_H__ +#define __USB_DEFS_H__ + +#include + +/** + * Buffers size definition + **/ +// !!! when working with CAN bus change USB_BTABLE_SIZE to 768 !!! +#define USB_BTABLE_SIZE 1024 +// first 64 bytes of USB_BTABLE are registers! +#define USB_EP0_BASEADDR 64 +// for USB FS EP0 buffers are from 8 to 64 bytes long (64 for PL2303) +#define USB_EP0_BUFSZ 64 +// USB transmit buffer size (64 for PL2303) +#define USB_TXBUFSZ 64 +// USB receive buffer size (64 for PL2303) +#define USB_RXBUFSZ 64 + +#define USB_BTABLE_BASE 0x40006000 +#undef USB_BTABLE +#define USB_BTABLE ((USB_BtableDef *)(USB_BTABLE_BASE)) +#define USB_ISTR_EPID 0x0000000F +#define USB_FNR_LSOF_0 0x00000800 +#define USB_FNR_lSOF_1 0x00001000 +#define USB_LPMCSR_BESL_0 0x00000010 +#define USB_LPMCSR_BESL_1 0x00000020 +#define USB_LPMCSR_BESL_2 0x00000040 +#define USB_LPMCSR_BESL_3 0x00000080 +#define USB_EPnR_CTR_RX 0x00008000 +#define USB_EPnR_DTOG_RX 0x00004000 +#define USB_EPnR_STAT_RX 0x00003000 +#define USB_EPnR_STAT_RX_0 0x00001000 +#define USB_EPnR_STAT_RX_1 0x00002000 +#define USB_EPnR_SETUP 0x00000800 +#define USB_EPnR_EP_TYPE 0x00000600 +#define USB_EPnR_EP_TYPE_0 0x00000200 +#define USB_EPnR_EP_TYPE_1 0x00000400 +#define USB_EPnR_EP_KIND 0x00000100 +#define USB_EPnR_CTR_TX 0x00000080 +#define USB_EPnR_DTOG_TX 0x00000040 +#define USB_EPnR_STAT_TX 0x00000030 +#define USB_EPnR_STAT_TX_0 0x00000010 +#define USB_EPnR_STAT_TX_1 0x00000020 +#define USB_EPnR_EA 0x0000000F +#define USB_COUNTn_RX_BLSIZE 0x00008000 +#define USB_COUNTn_NUM_BLOCK 0x00007C00 +#define USB_COUNTn_RX 0x0000003F + +#define USB_TypeDef USB_TypeDef_custom + +typedef struct{ + __IO uint32_t EPnR[8]; + __IO uint32_t RESERVED1; + __IO uint32_t RESERVED2; + __IO uint32_t RESERVED3; + __IO uint32_t RESERVED4; + __IO uint32_t RESERVED5; + __IO uint32_t RESERVED6; + __IO uint32_t RESERVED7; + __IO uint32_t RESERVED8; + __IO uint32_t CNTR; + __IO uint32_t ISTR; + __IO uint32_t FNR; + __IO uint32_t DADDR; + __IO uint32_t BTABLE; + __IO uint32_t LPMCSR; + __IO uint32_t BCDR; +} USB_TypeDef; + +typedef struct{ + __IO uint16_t USB_ADDR_TX; + __IO uint16_t USB_COUNT_TX; + __IO uint16_t USB_ADDR_RX; + __IO uint16_t USB_COUNT_RX; +} USB_EPDATA_TypeDef; + +typedef struct{ + __IO USB_EPDATA_TypeDef EP[8]; +} USB_BtableDef; + +#endif // __USB_DEFS_H__ diff --git a/STM32/TSYS_controller/usb_lib.c b/STM32/TSYS_controller/usb_lib.c new file mode 100644 index 0000000..cb3b9f1 --- /dev/null +++ b/STM32/TSYS_controller/usb_lib.c @@ -0,0 +1,532 @@ +/* + * geany_encoding=koi8-r + * usb_lib.c + * + * Copyright 2018 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ +#include "proto.h" +#include "usart.h" +#include "usb_lib.h" +#include +#include // memcpy + +ep_t endpoints[ENDPOINTS_NUM]; + +static usb_dev_t USB_Dev; +static usb_LineCoding lineCoding = {115200, 0, 0, 8}; +static config_pack_t setup_packet; +static uint8_t ep0databuf[EP0DATABUF_SIZE]; +static uint8_t ep0dbuflen = 0; + +usb_LineCoding getLineCoding(){return lineCoding;} + +// definition of parts common for USB_DeviceDescriptor & USB_DeviceQualifierDescriptor +#define bcdUSB_L 0x10 +#define bcdUSB_H 0x01 +#define bDeviceClass 0 +#define bDeviceSubClass 0 +#define bDeviceProtocol 0 +#define bNumConfigurations 1 + +static const uint8_t USB_DeviceDescriptor[] = { + 18, // bLength + 0x01, // bDescriptorType - Device descriptor + bcdUSB_L, // bcdUSB_L - 1.10 + bcdUSB_H, // bcdUSB_H + bDeviceClass, // bDeviceClass - USB_COMM + bDeviceSubClass, // bDeviceSubClass + bDeviceProtocol, // bDeviceProtocol + USB_EP0_BUFSZ, // bMaxPacketSize + 0x7b, // idVendor_L PL2303: VID=0x067b, PID=0x2303 + 0x06, // idVendor_H + 0x03, // idProduct_L + 0x23, // idProduct_H + 0x00, // bcdDevice_Ver_L + 0x03, // bcdDevice_Ver_H + 0x01, // iManufacturer + 0x02, // iProduct + 0x00, // iSerialNumber + bNumConfigurations // bNumConfigurations +}; + +static const uint8_t USB_DeviceQualifierDescriptor[] = { + 10, //bLength + 0x06, // bDescriptorType - Device qualifier + bcdUSB_L, // bcdUSB_L + bcdUSB_H, // bcdUSB_H + bDeviceClass, // bDeviceClass + bDeviceSubClass, // bDeviceSubClass + bDeviceProtocol, // bDeviceProtocol + USB_EP0_BUFSZ, // bMaxPacketSize0 + bNumConfigurations, // bNumConfigurations + 0x00 // Reserved +}; + +static const uint8_t USB_ConfigDescriptor[] = { + /*Configuration Descriptor*/ + 0x09, /* bLength: Configuration Descriptor size */ + 0x02, /* bDescriptorType: Configuration */ + 39, /* wTotalLength:no of returned bytes */ + 0x00, + 0x01, /* bNumInterfaces: 1 interface */ + 0x01, /* bConfigurationValue: Configuration value */ + 0x00, /* iConfiguration: Index of string descriptor describing the configuration */ + 0xa0, /* bmAttributes - Bus powered, Remote wakeup */ + 0x32, /* MaxPower 100 mA */ + + /*---------------------------------------------------------------------------*/ + + /*Interface Descriptor */ + 0x09, /* bLength: Interface Descriptor size */ + 0x04, /* bDescriptorType: Interface */ + 0x00, /* bInterfaceNumber: Number of Interface */ + 0x00, /* bAlternateSetting: Alternate setting */ + 0x03, /* bNumEndpoints: 3 endpoints used */ + 0xff, /* bInterfaceClass */ + 0x00, /* bInterfaceSubClass */ + 0x00, /* bInterfaceProtocol */ + 0x00, /* iInterface: */ +/////////////////////////////////////////////////// + /*Endpoint 1 Descriptor*/ + 0x07, /* bLength: Endpoint Descriptor size */ + 0x05, /* bDescriptorType: Endpoint */ + 0x81, /* bEndpointAddress IN1 */ + 0x03, /* bmAttributes: Interrupt */ + 0x0a, /* wMaxPacketSize LO: */ + 0x00, /* wMaxPacketSize HI: */ + 0x01, /* bInterval: */ + + /*Endpoint OUT2 Descriptor*/ + 0x07, /* bLength: Endpoint Descriptor size */ + 0x05, /* bDescriptorType: Endpoint */ + 0x02, /* bEndpointAddress: OUT2 */ + 0x02, /* bmAttributes: Bulk */ + (USB_RXBUFSZ & 0xff), /* wMaxPacketSize: 64 */ + (USB_RXBUFSZ >> 8), + 0x00, /* bInterval: ignore for Bulk transfer */ + + /*Endpoint IN3 Descriptor*/ + 0x07, /* bLength: Endpoint Descriptor size */ + 0x05, /* bDescriptorType: Endpoint */ + 0x83, /* bEndpointAddress IN3 */ + 0x02, /* bmAttributes: Bulk */ + (USB_TXBUFSZ & 0xff), /* wMaxPacketSize: 64 */ + (USB_TXBUFSZ >> 8), + 0x00, /* bInterval: ignore for Bulk transfer */ +}; + +_USB_LANG_ID_(USB_StringLangDescriptor, LANG_US); +// these descriptors are not used in PL2303 emulator! +_USB_STRING_(USB_StringSerialDescriptor, u"0"); +_USB_STRING_(USB_StringManufacturingDescriptor, u"Prolific Technology Inc."); +_USB_STRING_(USB_StringProdDescriptor, u"USB-Serial Controller"); + +/* + * default handlers + */ +// SET_LINE_CODING +void WEAK linecoding_handler(usb_LineCoding __attribute__((unused)) *lc){ + MSG("linecoding_handler\n"); +} + +// SET_CONTROL_LINE_STATE +void WEAK clstate_handler(uint16_t __attribute__((unused)) val){ + MSG("clstate_handler\n"); +} + +// SEND_BREAK +void WEAK break_handler(){ + MSG("break_handler\n"); +} + +// handler of vendor requests +void WEAK vendor_handler(config_pack_t *packet){ + if(packet->bmRequestType & 0x80){ // read + //SEND("Read"); + uint8_t c; + switch(packet->wValue){ + case 0x8484: + c = 2; + break; + case 0x0080: + c = 1; + break; + case 0x8686: + c = 0xaa; + break; + default: + c = 0; + } + EP_WriteIRQ(0, &c, 1); + }else{ // write ZLP + //SEND("Write"); + EP_WriteIRQ(0, (uint8_t *)0, 0); + } + /*SEND(" vendor, reqt="); + printuhex(packet->bmRequestType); + SEND(", wval="); + printuhex(packet->wValue); + usart_putchar('\n');*/ +} + + +#ifdef EBUG + uint8_t _2wr = 0; + #define WRITEDUMP(str) do{MSG(str); _2wr = 1;}while(0) +#else + #define WRITEDUMP(str) +#endif +static void wr0(const uint8_t *buf, uint16_t size){ + if(setup_packet.wLength < size) size = setup_packet.wLength; + EP_WriteIRQ(0, buf, size); +} + +static inline void get_descriptor(){ + switch(setup_packet.wValue){ + case DEVICE_DESCRIPTOR: + wr0(USB_DeviceDescriptor, sizeof(USB_DeviceDescriptor)); + break; + case CONFIGURATION_DESCRIPTOR: + wr0(USB_ConfigDescriptor, sizeof(USB_ConfigDescriptor)); + break; + case STRING_LANG_DESCRIPTOR: + wr0((const uint8_t *)&USB_StringLangDescriptor, STRING_LANG_DESCRIPTOR_SIZE_BYTE); + break; + case STRING_MAN_DESCRIPTOR: + wr0((const uint8_t *)&USB_StringManufacturingDescriptor, USB_StringManufacturingDescriptor.bLength); + break; + case STRING_PROD_DESCRIPTOR: + wr0((const uint8_t *)&USB_StringProdDescriptor, USB_StringProdDescriptor.bLength); + break; + case STRING_SN_DESCRIPTOR: + wr0((const uint8_t *)&USB_StringSerialDescriptor, USB_StringSerialDescriptor.bLength); + break; + case DEVICE_QUALIFIER_DESCRIPTOR: + wr0(USB_DeviceQualifierDescriptor, USB_DeviceQualifierDescriptor[0]); + break; + default: + WRITEDUMP("UNK_DES"); + break; + } +} + +static uint8_t configuration = 0; // reply for GET_CONFIGURATION (==1 if configured) +static inline void std_d2h_req(){ + uint16_t status = 0; // bus powered + switch(setup_packet.bRequest){ + case GET_DESCRIPTOR: + get_descriptor(); + break; + case GET_STATUS: + EP_WriteIRQ(0, (uint8_t *)&status, 2); // send status: Bus Powered + break; + case GET_CONFIGURATION: + WRITEDUMP("GET_CONFIGURATION"); + EP_WriteIRQ(0, &configuration, 1); + break; + default: + WRITEDUMP("80:WR_REQ"); + break; + } +} + +static inline void std_h2d_req(){ + switch(setup_packet.bRequest){ + case SET_ADDRESS: + // new address will be assigned later - after acknowlegement or request to host + USB_Dev.USB_Addr = setup_packet.wValue; + break; + case SET_CONFIGURATION: + // Now device configured + USB_Dev.USB_Status = USB_CONFIGURE_STATE; + configuration = setup_packet.wValue; + break; + default: + WRITEDUMP("0:WR_REQ"); + break; + } +} + +/* +bmRequestType: 76543210 +7 direction: 0 - host->device, 1 - device->host +65 type: 0 - standard, 1 - class, 2 - vendor +4..0 getter: 0 - device, 1 - interface, 2 - endpoint, 3 - other +*/ +/** + * Endpoint0 (control) handler + * @param ep - endpoint state + * @return data written to EP0R + */ +static uint16_t EP0_Handler(ep_t ep){ + uint16_t epstatus = ep.status; // EP0R on input -> return this value after modifications + uint8_t reqtype = setup_packet.bmRequestType & 0x7f; + uint8_t dev2host = (setup_packet.bmRequestType & 0x80) ? 1 : 0; + if ((ep.rx_flag) && (ep.setup_flag)){ + switch(reqtype){ + case STANDARD_DEVICE_REQUEST_TYPE: // standard device request + if(dev2host){ + std_d2h_req(); + }else{ + std_h2d_req(); + // send ZLP + EP_WriteIRQ(0, (uint8_t *)0, 0); + } + epstatus = SET_NAK_RX(epstatus); + epstatus = SET_VALID_TX(epstatus); + break; + case STANDARD_ENDPOINT_REQUEST_TYPE: // standard endpoint request + if (setup_packet.bRequest == CLEAR_FEATURE){ + // send ZLP + EP_WriteIRQ(0, (uint8_t *)0, 0); + epstatus = SET_NAK_RX(epstatus); + epstatus = SET_VALID_TX(epstatus); + }else{ + WRITEDUMP("02:WR_REQ"); + } + break; + case VENDOR_REQUEST_TYPE: + vendor_handler(&setup_packet); + epstatus = SET_NAK_RX(epstatus); + epstatus = SET_VALID_TX(epstatus); + break; + case CONTROL_REQUEST_TYPE: + switch(setup_packet.bRequest){ + case GET_LINE_CODING: + EP_WriteIRQ(0, (uint8_t*)&lineCoding, sizeof(lineCoding)); + break; + case SET_LINE_CODING: // omit this for next stage, when data will come + break; + case SET_CONTROL_LINE_STATE: + clstate_handler(setup_packet.wValue); + break; + case SEND_BREAK: + break_handler(); + break; + default: + WRITEDUMP("undef control req"); + } + if(!dev2host) EP_WriteIRQ(0, (uint8_t *)0, 0); // write acknowledgement + epstatus = SET_VALID_RX(epstatus); + epstatus = SET_VALID_TX(epstatus); + break; + default: + EP_WriteIRQ(0, (uint8_t *)0, 0); + epstatus = SET_NAK_RX(epstatus); + epstatus = SET_VALID_TX(epstatus); + } + }else if (ep.rx_flag){ // got data over EP0 or host acknowlegement + if(ep.rx_cnt){ + EP_WriteIRQ(0, (uint8_t *)0, 0); + if(setup_packet.bRequest == SET_LINE_CODING){ + //WRITEDUMP("SET_LINE_CODING"); + linecoding_handler((usb_LineCoding*)ep0databuf); + } + } + // Close transaction + epstatus = CLEAR_DTOG_RX(epstatus); + epstatus = CLEAR_DTOG_TX(epstatus); + // wait for new data from host + epstatus = SET_VALID_RX(epstatus); + epstatus = SET_STALL_TX(epstatus); + } else if (ep.tx_flag){ // package transmitted + // now we can change address after enumeration + if ((USB->DADDR & USB_DADDR_ADD) != USB_Dev.USB_Addr){ + USB->DADDR = USB_DADDR_EF | USB_Dev.USB_Addr; + // change state to ADRESSED + USB_Dev.USB_Status = USB_ADRESSED_STATE; + } + // end of transaction + epstatus = CLEAR_DTOG_RX(epstatus); + epstatus = CLEAR_DTOG_TX(epstatus); + epstatus = SET_VALID_RX(epstatus); + epstatus = SET_VALID_TX(epstatus); + } +#ifdef EBUG + if(_2wr){ + usart_putchar(' '); + if (ep.rx_flag) usart_putchar('r'); + else usart_putchar('t'); + printu(setup_packet.wLength); + if(ep.setup_flag) usart_putchar('s'); + usart_putchar(' '); + usart_putchar('I'); + printu(setup_packet.wIndex); + usart_putchar('V'); + printu(setup_packet.wValue); + usart_putchar('R'); + printu(setup_packet.bRequest); + usart_putchar('T'); + printu(setup_packet.bmRequestType); + usart_putchar(' '); + usart_putchar('0' + ep0dbuflen); + usart_putchar(' '); + hexdump(ep0databuf, ep0dbuflen); + usart_putchar('\n'); + } +#endif + return epstatus; +} +#undef WRITEDUMP + +static uint16_t lastaddr = USB_EP0_BASEADDR; +/** + * Endpoint initialisation + * !!! when working with CAN bus change USB_BTABLE_SIZE to 768 !!! + * @param number - EP num (0...7) + * @param type - EP type (EP_TYPE_BULK, EP_TYPE_CONTROL, EP_TYPE_ISO, EP_TYPE_INTERRUPT) + * @param txsz - transmission buffer size @ USB/CAN buffer + * @param rxsz - reception buffer size @ USB/CAN buffer + * @param uint16_t (*func)(ep_t *ep) - EP handler function + * @return 0 if all OK + */ +int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, uint16_t (*func)(ep_t ep)){ + if(number >= ENDPOINTS_NUM) return 4; // out of configured amount + if(txsz > USB_BTABLE_SIZE || rxsz > USB_BTABLE_SIZE) return 1; // buffer too large + if(lastaddr + txsz + rxsz >= USB_BTABLE_SIZE) return 2; // out of btable + USB->EPnR[number] = (type << 9) | (number & USB_EPnR_EA); + USB->EPnR[number] ^= USB_EPnR_STAT_RX | USB_EPnR_STAT_TX_1; + if(rxsz & 1 || rxsz > 992) return 3; // wrong rx buffer size + uint16_t countrx = 0; + if(rxsz < 64) countrx = rxsz / 2; + else{ + if(rxsz & 0x1f) return 3; // should be multiple of 32 + countrx = 31 + rxsz / 32; + } + USB_BTABLE->EP[number].USB_ADDR_TX = lastaddr; + endpoints[number].tx_buf = (uint16_t *)(USB_BTABLE_BASE + lastaddr); + lastaddr += txsz; + USB_BTABLE->EP[number].USB_COUNT_TX = 0; + USB_BTABLE->EP[number].USB_ADDR_RX = lastaddr; + endpoints[number].rx_buf = (uint8_t *)(USB_BTABLE_BASE + lastaddr); + lastaddr += rxsz; + // buffer size: Table127 of RM + USB_BTABLE->EP[number].USB_COUNT_RX = countrx << 10; + endpoints[number].func = func; + return 0; +} + +// standard IRQ handler +void usb_isr(){ + if (USB->ISTR & USB_ISTR_RESET){ + // Reinit registers + USB->CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM; + USB->ISTR = 0; + // Endpoint 0 - CONTROL + // ON USB LS size of EP0 may be 8 bytes, but on FS it should be 64 bytes! + lastaddr = USB_EP0_BASEADDR; // roll back to beginning of buffer + EP_Init(0, EP_TYPE_CONTROL, USB_EP0_BUFSZ, USB_EP0_BUFSZ, EP0_Handler); + // clear address, leave only enable bit + USB->DADDR = USB_DADDR_EF; + // state is default - wait for enumeration + USB_Dev.USB_Status = USB_DEFAULT_STATE; + } + if(USB->ISTR & USB_ISTR_CTR){ + // EP number + uint8_t n = USB->ISTR & USB_ISTR_EPID; + // copy status register + uint16_t epstatus = USB->EPnR[n]; + // Calculate flags + endpoints[n].rx_flag = (epstatus & USB_EPnR_CTR_RX) ? 1 : 0; + endpoints[n].setup_flag = (epstatus & USB_EPnR_SETUP) ? 1 : 0; + endpoints[n].tx_flag = (epstatus & USB_EPnR_CTR_TX) ? 1 : 0; + // copy received bytes amount + endpoints[n].rx_cnt = USB_BTABLE->EP[n].USB_COUNT_RX & 0x3FF; // low 10 bits is counter + // check direction + if(USB->ISTR & USB_ISTR_DIR){ // OUT interrupt - receive data, CTR_RX==1 (if CTR_TX == 1 - two pending transactions: receive following by transmit) + if(n == 0){ // control endpoint + if(epstatus & USB_EPnR_SETUP){ // setup packet -> copy data to conf_pack + memcpy(&setup_packet, endpoints[0].rx_buf, sizeof(setup_packet)); + ep0dbuflen = 0; + // interrupt handler will be called later + }else if(epstatus & USB_EPnR_CTR_RX){ // data packet -> push received data to ep0databuf + ep0dbuflen = endpoints[0].rx_cnt; + memcpy(ep0databuf, endpoints[0].rx_buf, ep0dbuflen); + } + } + }else{ // IN interrupt - transmit data, only CTR_TX == 1 + // enumeration end could be here (if EP0) + } + // prepare status field for EP handler + endpoints[n].status = epstatus; + // call EP handler (even if it will change EPnR, it should return new status) + epstatus = endpoints[n].func(endpoints[n]); + // keep DTOG state + epstatus = KEEP_DTOG_TX(epstatus); + epstatus = KEEP_DTOG_RX(epstatus); + // clear all RX/TX flags + epstatus = CLEAR_CTR_RX(epstatus); + epstatus = CLEAR_CTR_TX(epstatus); + // refresh EPnR + USB->EPnR[n] = epstatus; + } +} + +/** + * Write data to EP buffer (called from IRQ handler) + * @param number - EP number + * @param *buf - array with data + * @param size - its size + */ +void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size){ + uint8_t i; + if(size > USB_TXBUFSZ) size = USB_TXBUFSZ; + uint16_t N2 = (size + 1) >> 1; + // the buffer is 16-bit, so we should copy data as it would be uint16_t + uint16_t *buf16 = (uint16_t *)buf; + for (i = 0; i < N2; i++){ + endpoints[number].tx_buf[i] = buf16[i]; + } + USB_BTABLE->EP[number].USB_COUNT_TX = size; +} + +/** + * Write data to EP buffer (called outside IRQ handler) + * @param number - EP number + * @param *buf - array with data + * @param size - its size + */ +void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size){ + uint16_t status = USB->EPnR[number]; + EP_WriteIRQ(number, buf, size); + status = SET_NAK_RX(status); + status = SET_VALID_TX(status); + status = KEEP_DTOG_TX(status); + status = KEEP_DTOG_RX(status); + USB->EPnR[number] = status; +} + +/* + * Copy data from EP buffer into user buffer area + * @param *buf - user array for data + * @return amount of data read + */ +int EP_Read(uint8_t number, uint8_t *buf){ + int n = endpoints[number].rx_cnt; + if(n){ + for(int i = 0; i < n; ++i) + buf[i] = endpoints[number].rx_buf[i]; + } + return n; +} + +// USB status +uint8_t USB_GetState(){ + return USB_Dev.USB_Status; +} diff --git a/STM32/TSYS_controller/usb_lib.h b/STM32/TSYS_controller/usb_lib.h new file mode 100644 index 0000000..0651e5f --- /dev/null +++ b/STM32/TSYS_controller/usb_lib.h @@ -0,0 +1,202 @@ +/* + * geany_encoding=koi8-r + * usb_lib.h + * + * Copyright 2018 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ + +#pragma once +#ifndef __USB_LIB_H__ +#define __USB_LIB_H__ + +#include +#include "usb_defs.h" + +#define EP0DATABUF_SIZE (64) + +// Max EP amount (EP0 + other used) +#define ENDPOINTS_NUM 4 +// bmRequestType & 0x7f +#define STANDARD_DEVICE_REQUEST_TYPE 0 +#define STANDARD_ENDPOINT_REQUEST_TYPE 2 +#define VENDOR_REQUEST_TYPE 0x40 +#define CONTROL_REQUEST_TYPE 0x21 +// bRequest, standard; for bmRequestType == 0x80 +#define GET_STATUS 0x00 +#define GET_DESCRIPTOR 0x06 +#define GET_CONFIGURATION 0x08 +// for bmRequestType == 0 +#define CLEAR_FEATURE 0x01 +#define SET_FEATURE 0x03 // unused +#define SET_ADDRESS 0x05 +#define SET_DESCRIPTOR 0x07 // unused +#define SET_CONFIGURATION 0x09 +// for bmRequestType == 0x81, 1 or 0xB2 +#define GET_INTERFACE 0x0A // unused +#define SET_INTERFACE 0x0B // unused +#define SYNC_FRAME 0x0C // unused +#define VENDOR_REQUEST 0x01 // unused + +// Class-Specific Control Requests +#define SEND_ENCAPSULATED_COMMAND 0x00 // unused +#define GET_ENCAPSULATED_RESPONSE 0x01 // unused +#define SET_COMM_FEATURE 0x02 // unused +#define GET_COMM_FEATURE 0x03 // unused +#define CLEAR_COMM_FEATURE 0x04 // unused +#define SET_LINE_CODING 0x20 +#define GET_LINE_CODING 0x21 +#define SET_CONTROL_LINE_STATE 0x22 +#define SEND_BREAK 0x23 + +// control line states +#define CONTROL_DTR 0x01 +#define CONTROL_RTS 0x02 + +// wValue +#define DEVICE_DESCRIPTOR 0x100 +#define CONFIGURATION_DESCRIPTOR 0x200 +#define STRING_LANG_DESCRIPTOR 0x300 +#define STRING_MAN_DESCRIPTOR 0x301 +#define STRING_PROD_DESCRIPTOR 0x302 +#define STRING_SN_DESCRIPTOR 0x303 +#define DEVICE_QUALIFIER_DESCRIPTOR 0x600 + +// EPnR bits manipulation +#define CLEAR_DTOG_RX(R) (R & USB_EPnR_DTOG_RX) ? R : (R & (~USB_EPnR_DTOG_RX)) +#define SET_DTOG_RX(R) (R & USB_EPnR_DTOG_RX) ? (R & (~USB_EPnR_DTOG_RX)) : R +#define TOGGLE_DTOG_RX(R) (R | USB_EPnR_DTOG_RX) +#define KEEP_DTOG_RX(R) (R & (~USB_EPnR_DTOG_RX)) +#define CLEAR_DTOG_TX(R) (R & USB_EPnR_DTOG_TX) ? R : (R & (~USB_EPnR_DTOG_TX)) +#define SET_DTOG_TX(R) (R & USB_EPnR_DTOG_TX) ? (R & (~USB_EPnR_DTOG_TX)) : R +#define TOGGLE_DTOG_TX(R) (R | USB_EPnR_DTOG_TX) +#define KEEP_DTOG_TX(R) (R & (~USB_EPnR_DTOG_TX)) +#define SET_VALID_RX(R) ((R & USB_EPnR_STAT_RX) ^ USB_EPnR_STAT_RX) | (R & (~USB_EPnR_STAT_RX)) +#define SET_NAK_RX(R) ((R & USB_EPnR_STAT_RX) ^ USB_EPnR_STAT_RX_1) | (R & (~USB_EPnR_STAT_RX)) +#define SET_STALL_RX(R) ((R & USB_EPnR_STAT_RX) ^ USB_EPnR_STAT_RX_0) | (R & (~USB_EPnR_STAT_RX)) +#define KEEP_STAT_RX(R) (R & (~USB_EPnR_STAT_RX)) +#define SET_VALID_TX(R) ((R & USB_EPnR_STAT_TX) ^ USB_EPnR_STAT_TX) | (R & (~USB_EPnR_STAT_TX)) +#define SET_NAK_TX(R) ((R & USB_EPnR_STAT_TX) ^ USB_EPnR_STAT_TX_1) | (R & (~USB_EPnR_STAT_TX)) +#define SET_STALL_TX(R) ((R & USB_EPnR_STAT_TX) ^ USB_EPnR_STAT_TX_0) | (R & (~USB_EPnR_STAT_TX)) +#define KEEP_STAT_TX(R) (R & (~USB_EPnR_STAT_TX)) +#define CLEAR_CTR_RX(R) (R & (~USB_EPnR_CTR_RX)) +#define CLEAR_CTR_TX(R) (R & (~USB_EPnR_CTR_TX)) +#define CLEAR_CTR_RX_TX(R) (R & (~(USB_EPnR_CTR_TX | USB_EPnR_CTR_RX))) + +// USB state: uninitialized, addressed, ready for use +#define USB_DEFAULT_STATE 0 +#define USB_ADRESSED_STATE 1 +#define USB_CONFIGURE_STATE 2 + +// EP types +#define EP_TYPE_BULK 0x00 +#define EP_TYPE_CONTROL 0x01 +#define EP_TYPE_ISO 0x02 +#define EP_TYPE_INTERRUPT 0x03 + +#define LANG_US (uint16_t)0x0409 + +#define _USB_STRING_(name, str) \ +static const struct name \ +{ \ + uint8_t bLength; \ + uint8_t bDescriptorType; \ + uint16_t bString[(sizeof(str) - 2) / 2]; \ + \ +} \ +name = {sizeof(name), 0x03, str} + +#define _USB_LANG_ID_(name, lng_id) \ + \ +static const struct name \ +{ \ + uint8_t bLength; \ + uint8_t bDescriptorType; \ + uint16_t bString; \ + \ +} \ +name = {0x04, 0x03, lng_id} +#define STRING_LANG_DESCRIPTOR_SIZE_BYTE (4) + +// EP0 configuration packet +typedef struct { + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} config_pack_t; + +// endpoints state +typedef struct __ep_t{ + uint16_t *tx_buf; // transmission buffer address + uint8_t *rx_buf; // reception buffer address + uint16_t (*func)(); // endpoint action function + uint16_t status; // status flags + unsigned rx_cnt : 10; // received data counter + unsigned tx_flag : 1; // transmission flag + unsigned rx_flag : 1; // reception flag + unsigned setup_flag : 1; // this is setup packet (only for EP0) +} ep_t; + +// USB status & its address +typedef struct { + uint8_t USB_Status; + uint16_t USB_Addr; +}usb_dev_t; + +typedef struct { + uint32_t dwDTERate; + uint8_t bCharFormat; + #define USB_CDC_1_STOP_BITS 0 + #define USB_CDC_1_5_STOP_BITS 1 + #define USB_CDC_2_STOP_BITS 2 + uint8_t bParityType; + #define USB_CDC_NO_PARITY 0 + #define USB_CDC_ODD_PARITY 1 + #define USB_CDC_EVEN_PARITY 2 + #define USB_CDC_MARK_PARITY 3 + #define USB_CDC_SPACE_PARITY 4 + uint8_t bDataBits; +} __attribute__ ((packed)) usb_LineCoding; + +typedef struct { + uint8_t bmRequestType; + uint8_t bNotificationType; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} __attribute__ ((packed)) usb_cdc_notification; + +extern ep_t endpoints[]; + +void USB_Init(); +uint8_t USB_GetState(); +int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, uint16_t (*func)(ep_t ep)); +void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size); +void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size); +int EP_Read(uint8_t number, uint8_t *buf); +usb_LineCoding getLineCoding(); + + +void WEAK linecoding_handler(usb_LineCoding *lc); +void WEAK clstate_handler(uint16_t val); +void WEAK break_handler(); +void WEAK vendor_handler(config_pack_t *packet); + +#endif // __USB_LIB_H__