From 5b9eb4d3bab522e9710d73b1a4a1c0f73d506941 Mon Sep 17 00:00:00 2001 From: eddyem Date: Fri, 7 Jun 2019 18:50:55 +0300 Subject: [PATCH] Add USB, ADC and change address to 4 bits --- .gitignore | 6 + STM32/TSYS_controller/2DO | 3 - STM32/TSYS_controller/Readme.md | 44 +- STM32/TSYS_controller/adc.c | 167 ++++++++ STM32/TSYS_controller/adc.h | 31 ++ STM32/TSYS_controller/can.c | 65 +-- STM32/TSYS_controller/can.h | 4 +- STM32/TSYS_controller/can_process.c | 111 +++++- STM32/TSYS_controller/can_process.h | 6 + STM32/TSYS_controller/hardware.c | 6 +- STM32/TSYS_controller/hardware.h | 2 +- STM32/TSYS_controller/i2c.c | 45 +-- STM32/TSYS_controller/i2c.h | 2 + STM32/TSYS_controller/main.c | 34 +- STM32/TSYS_controller/proto.c | 178 ++++++++- STM32/TSYS_controller/proto.h | 21 +- STM32/TSYS_controller/sensors_manage.c | 98 +---- STM32/TSYS_controller/sensors_manage.h | 25 +- STM32/TSYS_controller/tsys01.bin | Bin 14436 -> 20396 bytes STM32/TSYS_controller/usart.c | 57 +-- STM32/TSYS_controller/usart.h | 14 +- STM32/TSYS_controller/usb.c | 182 +++++++++ STM32/TSYS_controller/usb.h | 37 ++ STM32/TSYS_controller/usb_defs.h | 106 +++++ STM32/TSYS_controller/usb_lib.c | 532 +++++++++++++++++++++++++ STM32/TSYS_controller/usb_lib.h | 202 ++++++++++ 26 files changed, 1679 insertions(+), 299 deletions(-) delete mode 100644 STM32/TSYS_controller/2DO create mode 100644 STM32/TSYS_controller/adc.c create mode 100644 STM32/TSYS_controller/adc.h create mode 100644 STM32/TSYS_controller/usb.c create mode 100644 STM32/TSYS_controller/usb.h create mode 100644 STM32/TSYS_controller/usb_defs.h create mode 100644 STM32/TSYS_controller/usb_lib.c create mode 100644 STM32/TSYS_controller/usb_lib.h 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 f015d4dc6a7703378593632ec5992164b2c77aed..634d0def8135b8444d7b2289d4ac15b89c86c77a 100755 GIT binary patch literal 20396 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 @@ -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__