diff --git a/F3:F303/NitrogenFlooding/Readme.md b/F3:F303/NitrogenFlooding/Readme.md index 19388ee..063860b 100644 --- a/F3:F303/NitrogenFlooding/Readme.md +++ b/F3:F303/NitrogenFlooding/Readme.md @@ -202,3 +202,15 @@ Automated liquid nitrogen flooding machine | 10 | PF9 | - | - | | | 11 | PF10 | VadcON | slow out | turn ON ADC power| |---------|-------------|-------------|-------------|------------------| + + +## DMA usage +### DMA1 + +- Channel 1 - ADC1 +- Channel 6 - I2C1 Tx +- Channel 7 - I2C1 Rx + +### DMA2 + +- Channel 2 - ADC2 diff --git a/F3:F303/NitrogenFlooding/adc.c b/F3:F303/NitrogenFlooding/adc.c index e28f50b..9247386 100644 --- a/F3:F303/NitrogenFlooding/adc.c +++ b/F3:F303/NitrogenFlooding/adc.c @@ -25,9 +25,9 @@ * @brief ADCx_array - arrays for ADC channels with median filtering: * ADC1: * 0..9 - AIN0..9 (ADC1_IN1..10) - * 4 - internal Tsens - ADC1_IN16 + * 10 - internal Tsens - ADC1_IN16 * ADC2: - * 6 - AINext - (ADC2 in 1) + * 11 - AINext - (ADC2 in 1) */ static uint16_t ADC_array[NUMBER_OF_ADC_CHANNELS*9]; @@ -66,7 +66,7 @@ TRUE_INLINE void enADC(ADC_TypeDef *chnl){ * ADC1 - DMA1_ch1 * ADC2 - DMA2_ch1 */ -// Setup ADC and DAC +// Setup ADC void adc_setup(){ RCC->AHBENR |= RCC_AHBENR_ADC12EN; // Enable clocking ADC12_COMMON->CCR = ADC_CCR_TSEN | ADC_CCR_CKMODE; // enable Tsens, HCLK/4 @@ -83,7 +83,6 @@ void adc_setup(){ ADC2->SMPR1 = ADC_SMPR1_SMP1; ADC2->SQR1 = (1<<6) | (NUMBER_OF_ADC2_CHANNELS-1); // configure DMA for ADC - RCC->AHBENR |= RCC_AHBENR_DMA1EN | RCC_AHBENR_DMA2EN; ADC1->CFGR = ADC_CFGR_CONT | ADC_CFGR_DMAEN | ADC_CFGR_DMACFG; ADC2->CFGR = ADC_CFGR_CONT | ADC_CFGR_DMAEN | ADC_CFGR_DMACFG; DMA1_Channel1->CPAR = (uint32_t) (&(ADC1->DR)); @@ -112,8 +111,11 @@ uint16_t getADCval(int nch){ #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]; - int adval = (nch >= NUMBER_OF_ADC1_CHANNELS) ? NUMBER_OF_ADC2_CHANNELS : NUMBER_OF_ADC1_CHANNELS; - int addr = (nch >= NUMBER_OF_ADC1_CHANNELS) ? nch - NUMBER_OF_ADC2_CHANNELS + ADC2START: nch; + int addr = nch, adval = NUMBER_OF_ADC1_CHANNELS; + if(nch >= NUMBER_OF_ADC1_CHANNELS){ + adval = NUMBER_OF_ADC2_CHANNELS; + addr += ADC2START; + } for(int i = 0; i < 9; ++i, addr += adval) // 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]) ; diff --git a/F3:F303/NitrogenFlooding/hardware.c b/F3:F303/NitrogenFlooding/hardware.c index fdd6b9f..3d2da1f 100644 --- a/F3:F303/NitrogenFlooding/hardware.c +++ b/F3:F303/NitrogenFlooding/hardware.c @@ -17,19 +17,23 @@ */ #include "hardware.h" +#include "i2c.h" int LEDsON = 0; // setup here ALL GPIO pins (due to table in Readme.md) // leave SWD as default AF; high speed for CLK and some other AF; med speed for some another AF TRUE_INLINE void gpio_setup(){ + BUZZER_OFF(); + ADCON(0); + pin_set(LEDs_port, 0xf<<8); // turn off LEDs RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOCEN | RCC_AHBENR_GPIODEN | RCC_AHBENR_GPIOEEN | RCC_AHBENR_GPIOFEN; // enable PWM timer TIM3 RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; for(int i = 0; i < 10000; ++i) nop(); // PORT A (PA13/14 - SWDIO/SWCLK - AF0) - GPIOA->ODR = 0; + //GPIOA->ODR = 0; GPIOA->AFR[0] = 0; GPIOA->AFR[1] = AFRf(4, 9) | AFRf(4, 10) | AFRf(14, 11) | AFRf(14,12); GPIOA->MODER = MODER_AI(0) | MODER_AI(1) | MODER_AI(2) | MODER_AI(3) | MODER_AI(4) | @@ -39,7 +43,7 @@ TRUE_INLINE void gpio_setup(){ GPIOA->PUPDR = PUPD_PU(13) | PUPD_PD(14); // SWDIO - pullup, SDCLK - pulldown // PORT B - GPIOB->ODR = 0; + //GPIOB->ODR = 0; GPIOB->AFR[0] = AFRf(4, 6) | AFRf(4, 7); GPIOB->AFR[1] = AFRf(5, 13) | AFRf(5, 14) | AFRf(5, 15); GPIOB->MODER = MODER_O(0) | MODER_AF(6) | MODER_AF(7) | MODER_O(10) | MODER_O(11) | MODER_O(12) | MODER_AF(13) @@ -49,7 +53,7 @@ TRUE_INLINE void gpio_setup(){ GPIOB->PUPDR = 0; // PORT C - GPIOC->ODR = 0; + //GPIOC->ODR = 0; GPIOC->AFR[0] = 0; GPIOC->AFR[1] = AFRf(7, 10) | AFRf(7, 11); GPIOC->MODER = MODER_AI(0) | MODER_AI(1) | MODER_AI(2) | MODER_AI(3) | MODER_O(9) | MODER_AF(10) | MODER_AF(11); @@ -58,7 +62,7 @@ TRUE_INLINE void gpio_setup(){ GPIOC->PUPDR = 0; // PORT D - GPIOD->ODR = 0; + //GPIOD->ODR = 0; GPIOD->AFR[0] = AFRf(7, 0) | AFRf(7, 1) | AFRf(7, 5) | AFRf(7, 6); GPIOD->AFR[1] = 0; GPIOD->MODER = MODER_AF(0) | MODER_AF(1) | MODER_O(4) | MODER_AF(5) | MODER_AF(6) | MODER_I(9) @@ -68,7 +72,7 @@ TRUE_INLINE void gpio_setup(){ GPIOD->PUPDR = PUPD_PU(9) | PUPD_PU(10) | PUPD_PU(11) | PUPD_PU(12) | PUPD_PU(13) | PUPD_PU(14) | PUPD_PU(15); // PORT E - GPIOE->ODR = 0; + //GPIOE->ODR = 0; GPIOE->AFR[0] = 0; GPIOE->AFR[1] = AFRf(2, 2) | AFRf(2, 3) | AFRf(2, 4) | AFRf(2, 5); GPIOE->MODER = MODER_AF(2) | MODER_AF(3) | MODER_AF(4) | MODER_AF(5) | MODER_O(8) | MODER_O(9) | MODER_O(10) | MODER_O(11); @@ -77,7 +81,7 @@ TRUE_INLINE void gpio_setup(){ GPIOE->PUPDR = 0; // PORT F - GPIOF->ODR = 0; + //GPIOF->ODR = 0; GPIOF->AFR[0] = 0; GPIOF->AFR[1] = 0; GPIOF->MODER = MODER_AI(2) | MODER_O(10); @@ -86,6 +90,19 @@ TRUE_INLINE void gpio_setup(){ GPIOF->PUPDR = 0; } +TRUE_INLINE void pwm_setup(){ + TIM3->CR1 = TIM_CR1_ARPE; + TIM3->PSC = 1999; // 48M/2000 = 24kHz + // PWM mode 1 (active -> inactive) + TIM3->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1; + TIM3->CCMR2 = TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1; + TIM3->CCR1 = 0; + TIM3->ARR = 255; // 8bit PWM + TIM3->BDTR |= TIM_BDTR_MOE; // enable main output + TIM3->CCER = TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E | TIM_CCER_CC4E; + TIM3->CR1 |= TIM_CR1_CEN; +} + #ifndef EBUG TRUE_INLINE void iwdg_setup(){ uint32_t tmout = 16000000; @@ -112,9 +129,50 @@ TRUE_INLINE void iwdg_setup(){ #endif void hw_setup(){ + RCC->AHBENR |= RCC_AHBENR_DMA1EN | RCC_AHBENR_DMA2EN; gpio_setup(); + i2c_setup(HIGH_SPEED); + pwm_setup(); #ifndef EBUG iwdg_setup(); #endif } +void setPWM(int nch, uint8_t val){ + switch(nch){ + case 0: + TIM3->CCR1 = val; + break; + case 1: + TIM3->CCR2 = val; + break; + case 2: + TIM3->CCR3 = val; + break; + case 3: + TIM3->CCR4 = val; + break; + default: + break; + } +} + +uint8_t getPWM(int nch){ + switch(nch){ + case 0: + return TIM3->CCR1; + break; + case 1: + return TIM3->CCR2; + break; + case 2: + return TIM3->CCR3; + break; + case 3: + return TIM3->CCR4; + break; + default: + break; + } + return 0; +} diff --git a/F3:F303/NitrogenFlooding/hardware.h b/F3:F303/NitrogenFlooding/hardware.h index 40f55ec..53792e9 100644 --- a/F3:F303/NitrogenFlooding/hardware.h +++ b/F3:F303/NitrogenFlooding/hardware.h @@ -60,8 +60,22 @@ // state 0 - pressed, 1 - released #define BTN_state(x) (BTNs_port->IDR & 1<<(9+x) ? 0 : 1) +// buzzer, ADC voltage +#define BUZZER_port GPIOB +#define BUZZER_pin (1<<0) +#define BUZZER_ON() do{pin_clear(BUZZER_port, BUZZER_pin);}while(0) +#define BUZZER_OFF() do{pin_set(BUZZER_port, BUZZER_pin);}while(0) +#define BUZZER_STATE() (BUZZER_port->IDR & BUZZER_pin ? 0 : 1) +#define ADCON_port GPIOF +#define ADCON_pin (1<<10) +#define ADCON(x) do{if(x==0) pin_set(ADCON_port, ADCON_pin); else pin_clear(ADCON_port, ADCON_pin);}while(0) +#define ADCONSTATE() (ADCON_port->IDR & ADCON_pin ? 0 : 1) + extern volatile uint32_t Tms; extern int LEDsON; uint8_t MSB(uint16_t val); void hw_setup(); + +void setPWM(int nch, uint8_t val); +uint8_t getPWM(int nch); diff --git a/F3:F303/NitrogenFlooding/i2c.c b/F3:F303/NitrogenFlooding/i2c.c new file mode 100644 index 0000000..41e85c9 --- /dev/null +++ b/F3:F303/NitrogenFlooding/i2c.c @@ -0,0 +1,313 @@ +/* + * This file is part of the nitrogen project. + * Copyright 2023 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "i2c.h" +#include "strfunc.h" // hexdump +#include "usb.h" + +I2C_SPEED curI2Cspeed = LOW_SPEED; +extern volatile uint32_t Tms; +static uint32_t cntr; +static uint8_t i2c_got_DMA_Rx = 0; +volatile uint8_t I2C_scan_mode = 0; // == 1 when I2C is in scan mode +static uint8_t i2caddr = I2C_ADDREND; // current address in scan mode +static volatile int I2Cbusy = 0, goterr = 0; // busy==1 when DMA active, goterr==1 if 't was error @ last sent +static uint8_t I2Cbuf[256], i2cbuflen = 0; // buffer for DMA tx/rx and its len + +// macros for I2C rx/tx +#define DMARXCCR (DMA_CCR_MINC | DMA_CCR_TCIE | DMA_CCR_TEIE) +#define DMATXCCR (DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE | DMA_CCR_TEIE) +// macro for I2CCR1 +#define I2CCR1 (I2C_CR1_PE | I2C_CR1_RXDMAEN | I2C_CR1_TXDMAEN) + +// return 1 if I2Cbusy is set & timeout reached +static inline int isI2Cbusy(){ + cntr = Tms; + do{ + if(Tms - cntr > I2C_TIMEOUT){ USND("Timeout, DMA transfer in progress?\n"); return 1;} + }while(I2Cbusy); + return 0; +} + +// GPIO Resources: I2C1_SCL - PB6 (AF4), I2C1_SDA - PB7 (AF4) +void i2c_setup(I2C_SPEED speed){ + if(speed >= CURRENT_SPEED){ + speed = curI2Cspeed; + }else{ + curI2Cspeed = speed; + } + RCC->AHBENR |= RCC_AHBENR_GPIOBEN; + I2C1->CR1 = 0; + I2C1->ICR = 0x3f38; // clear all errors + GPIOB->AFR[0] = (GPIOB->AFR[0] & ~(GPIO_AFRL_AFRL6 | GPIO_AFRL_AFRL7)) | + AFRf(4, 6) | AFRf(4, 7); + GPIOB->MODER = (GPIOB->MODER & ~(GPIO_MODER_MODER6 | GPIO_MODER_MODER7)) | + GPIO_MODER_MODER6_AF | GPIO_MODER_MODER7_AF; + GPIOB->PUPDR = (GPIOB->PUPDR & !(GPIO_PUPDR_PUPDR6 | GPIO_PUPDR_PUPDR7)) | + GPIO_PUPDR6_PU | GPIO_PUPDR7_PU; // pullup (what if there's no external pullup?) + GPIOB->OTYPER |= GPIO_OTYPER_OT_6 | GPIO_OTYPER_OT_7; // both open-drain outputs + // I2C (default timing from PCLK - 64MHz) + RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; // clocking + if(speed == LOW_SPEED){ // 10kHz + // PRESC=F, SCLDEL=4, SDADEL=2, SCLH=0xC3, SCLL=0xC7 + I2C1->TIMINGR = (0xF<<28) | (4<<20) | (2<<16) | (0xC3<<8) | (0xC7); + }else if(speed == HIGH_SPEED){ // 100kHz + I2C1->TIMINGR = (0xF<<28) | (4<<20) | (2<<16) | (0xF<<8) | (0x13); + }else{ // VERYLOW_SPEED - the lowest speed by STM register: ~7.7kHz + I2C1->TIMINGR = (0xF<<28) | (4<<20) | (2<<16) | (0xff<<8) | (0xff); + } + I2C1->CR1 = I2CCR1; + RCC->AHBENR |= RCC_AHBENR_DMA1EN; + NVIC_EnableIRQ(DMA1_Channel6_IRQn); + NVIC_EnableIRQ(DMA1_Channel7_IRQn); + I2Cbusy = 0; +} + +// setup DMA for rx (tx==0) or tx (tx==1) +// DMA channels: 7 - I2C1_Rx, 6 - I2C1_Tx +static void i2cDMAsetup(int tx, uint8_t len){ + if(tx){ + DMA1_Channel6->CCR = DMATXCCR; + DMA1_Channel6->CPAR = (uint32_t) &I2C1->TXDR; + DMA1_Channel6->CMAR = (uint32_t) I2Cbuf; + DMA1_Channel6->CNDTR = i2cbuflen = len; + }else{ + DMA1_Channel7->CCR = DMARXCCR; + DMA1_Channel7->CPAR = (uint32_t) &I2C1->RXDR; + DMA1_Channel7->CMAR = (uint32_t) I2Cbuf; + DMA1_Channel7->CNDTR = i2cbuflen = len; + } +} + +static uint8_t i2c_start(uint8_t busychk){ + if(busychk){ + cntr = Tms; + while(I2C1->ISR & I2C_ISR_BUSY){ + IWDG->KR = IWDG_REFRESH; + if(Tms - cntr > I2C_TIMEOUT){ + USND("Line busy\n"); + return 0; // check busy + }} + } + cntr = Tms; + while(I2C1->CR2 & I2C_CR2_START){ + IWDG->KR = IWDG_REFRESH; + if(Tms - cntr > I2C_TIMEOUT){ + USND("No start\n"); + return 0; // check start + }} + return 1; +} + +// start writing +static uint8_t i2c_startw(uint8_t addr, uint8_t nbytes, uint8_t stop){ + if(!i2c_start(1)) return 0; + I2C1->CR2 = nbytes << 16 | addr; + if(stop) I2C1->CR2 |= I2C_CR2_AUTOEND; // autoend + // now start transfer + I2C1->CR2 |= I2C_CR2_START; + return 1; +} + +/** + * write command byte to I2C + * @param addr - device address (TSYS01_ADDR0 or TSYS01_ADDR1) + * @param data - bytes to write + * @param nbytes - amount of bytes to write + * @param stop - to set STOP + * @return 0 if error + */ +static uint8_t write_i2cs(uint8_t addr, uint8_t *data, uint8_t nbytes, uint8_t stop){ + if(!i2c_startw(addr, nbytes, stop)) return 0; + for(int i = 0; i < nbytes; ++i){ + 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; + //USND("NAK\n"); + return 0; + } + if(Tms - cntr > I2C_TIMEOUT){ + //USND("Timeout\n"); + return 0; + } + } + I2C1->TXDR = data[i]; // send data + } + cntr = Tms; + // wait for data gone + while(I2C1->ISR & I2C_ISR_BUSY){ + IWDG->KR = IWDG_REFRESH; + if(Tms - cntr > I2C_TIMEOUT){break;} + } + return 1; +} + +uint8_t write_i2c(uint8_t addr, uint8_t *data, uint8_t nbytes){ + if(isI2Cbusy()) return 0; + return write_i2cs(addr, data, nbytes, 1); +} + +uint8_t write_i2c_dma(uint8_t addr, uint8_t *data, uint8_t nbytes){ + if(!data || nbytes < 1) return 0; + if(isI2Cbusy()) return 0; + memcpy((char*)I2Cbuf, (char*)data, nbytes); + i2cDMAsetup(1, nbytes); + goterr = 0; + if(!i2c_startw(addr, nbytes, 1)) return 0; + I2Cbusy = 1; + DMA1_Channel6->CCR = DMATXCCR | DMA_CCR_EN; // start transfer + return 1; +} + +// start reading +static uint8_t i2c_startr(uint8_t addr, uint8_t nbytes, uint8_t busychk){ + if(!i2c_start(busychk)) return 0; + // read N bytes + I2C1->CR2 = (nbytes<<16) | addr | 1 | I2C_CR2_AUTOEND | I2C_CR2_RD_WRN; + I2C1->CR2 |= I2C_CR2_START; + return 1; +} + +/** + * read nbytes of data from I2C line + * all functions with `addr` should have addr = address << 1 + * `data` should be an array with at least `nbytes` length + * @return 1 if all OK, 0 if NACK or no device found + */ +static uint8_t read_i2cb(uint8_t addr, uint8_t *data, uint8_t nbytes, uint8_t busychk){ + if(!i2c_startr(addr, nbytes, busychk)) return 0; + uint8_t i; + for(i = 0; i < nbytes; ++i){ + cntr = Tms; + while(!(I2C1->ISR & I2C_ISR_RXNE)){ // wait for data + IWDG->KR = IWDG_REFRESH; + if(I2C1->ISR & I2C_ISR_NACKF){ + I2C1->ICR |= I2C_ICR_NACKCF; + //USND("NAK\n"); + return 0; + } + if(Tms - cntr > I2C_TIMEOUT){ + //USND("Timeout\n"); + return 0; + } + } + *data++ = I2C1->RXDR; + } + return 1; + } + +uint8_t read_i2c(uint8_t addr, uint8_t *data, uint8_t nbytes){ + if(isI2Cbusy()) return 0; + return read_i2cb(addr, data, nbytes, 1); +} + +uint8_t read_i2c_dma(uint8_t addr, uint8_t nbytes){ + if(nbytes < 1) return 0; + if(isI2Cbusy()) return 0; + i2cDMAsetup(0, nbytes); + goterr = 0; + if(!i2c_startr(addr, nbytes, 1)) return 0; + I2Cbusy = 1; + DMA1_Channel7->CCR = DMARXCCR | DMA_CCR_EN; // start transfer + return 1; +} + + +// read register reg +uint8_t read_i2c_reg(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t nbytes){ + if(isI2Cbusy()) return 0; + if(!write_i2cs(addr, ®, 1, 0)) return 0; + return read_i2cb(addr, data, nbytes, 0); +} + +// read 16bit register reg +uint8_t read_i2c_reg16(uint8_t addr, uint16_t reg16, uint8_t *data, uint8_t nbytes){ + if(isI2Cbusy()) return 0; + if(!write_i2cs(addr, (uint8_t*)®16, 2, 0)) return 0; + return read_i2cb(addr, data, nbytes, 0); +} + +void i2c_init_scan_mode(){ + i2caddr = 1; // start from 1 as 0 is a broadcast address + I2C_scan_mode = 1; +} + +// return 1 if next addr is active & return in as `addr` +// if addresses are over, return 1 and set addr to I2C_NOADDR +// if scan mode inactive, return 0 and set addr to I2C_NOADDR +int i2c_scan_next_addr(uint8_t *addr){ + if(isI2Cbusy()) return 0; + *addr = i2caddr; + if(i2caddr == I2C_ADDREND){ + *addr = I2C_ADDREND; + I2C_scan_mode = 0; + return 0; + } + /*while(!u3txrdy); + USND("Addr: "); USND(uhex2str(i2caddr)); USND("\n"); + usart3_sendbuf();*/ + uint8_t byte; + if(!read_i2c((i2caddr++)<<1, &byte, 1)) return 0; + return 1; +} + +// dump I2Cbuf +void i2c_bufdudump(){ + if(goterr){ + USND("Last transfer ends with error!\n"); + goterr = 0; + } + USND("I2C buffer:\n"); + hexdump(USB_sendstr, I2Cbuf, i2cbuflen); +} + +void i2c_have_DMA_Rx(){ + if(!i2c_got_DMA_Rx) return; + i2c_got_DMA_Rx = 0; + i2c_bufdudump(); +} + +int i2cdma_haderr(){ + int r = goterr; + goterr = 0; + return r; +} + +// Rx (7) /Tx (6) interrupts +static void I2C_isr(int rx){ + uint32_t isr = DMA1->ISR; + DMA_Channel_TypeDef *ch = (rx) ? DMA1_Channel7 : DMA1_Channel6; + if(isr & (DMA_ISR_TEIF6 | DMA_ISR_TEIF6)) goterr = 1; + if(rx) i2c_got_DMA_Rx = 1; // last transfer was Rx + ch->CCR = 0; + I2Cbusy = 0; + DMA1->IFCR = 0x0ff00000; // clear all flags for channel6/7 +} + +void dma1_channel6_isr(){ + I2C_isr(0); +} + +void dma1_channel7_isr(){ + I2C_isr(1); +} diff --git a/F3:F303/NitrogenFlooding/i2c.h b/F3:F303/NitrogenFlooding/i2c.h new file mode 100644 index 0000000..3545af7 --- /dev/null +++ b/F3:F303/NitrogenFlooding/i2c.h @@ -0,0 +1,50 @@ +/* + * This file is part of the nitrogen project. + * Copyright 2023 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +#define I2C_ADDREND (0x80) + +typedef enum{ + VERYLOW_SPEED, + LOW_SPEED, + HIGH_SPEED, + CURRENT_SPEED +} I2C_SPEED; + +extern I2C_SPEED curI2Cspeed; +extern volatile uint8_t I2C_scan_mode; + +// timeout of I2C bus in ms +#define I2C_TIMEOUT (100) + +void i2c_setup(I2C_SPEED speed); +uint8_t read_i2c(uint8_t addr, uint8_t *data, uint8_t nbytes); +uint8_t read_i2c_reg(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t nbytes); +uint8_t read_i2c_reg16(uint8_t addr, uint16_t reg16, uint8_t *data, uint8_t nbytes); +uint8_t write_i2c(uint8_t addr, uint8_t *data, uint8_t nbytes); +uint8_t write_i2c_dma(uint8_t addr, uint8_t *data, uint8_t nbytes); +uint8_t read_i2c_dma(uint8_t addr, uint8_t nbytes); + +void i2c_bufdudump(); +void i2c_have_DMA_Rx(); +int i2cdma_haderr(); + +void i2c_init_scan_mode(); +int i2c_scan_next_addr(uint8_t *addr); diff --git a/F3:F303/NitrogenFlooding/main.c b/F3:F303/NitrogenFlooding/main.c index 5998afc..04857b7 100644 --- a/F3:F303/NitrogenFlooding/main.c +++ b/F3:F303/NitrogenFlooding/main.c @@ -21,6 +21,7 @@ //#include "can.h" //#include "flash.h" #include "hardware.h" +#include "i2c.h" #include "proto.h" #include "usb.h" @@ -75,6 +76,17 @@ int main(void){ } } }*/ + if(I2C_scan_mode){ + uint8_t addr; + int ok = i2c_scan_next_addr(&addr); + if(addr == I2C_ADDREND) USND("Scan ends"); + else if(ok){ + USB_sendstr(uhex2str(addr)); + USB_sendstr(" ("); USB_sendstr(u2str(addr)); + USB_sendstr(") - found device\n"); + } + } + i2c_have_DMA_Rx(); // check if there's DMA Rx complete USB_proc(); int l = USB_receivestr(inbuff, MAXSTRLEN); if(l < 0) USB_sendstr("ERROR: USB buffer overflow or string was too long\n"); diff --git a/F3:F303/NitrogenFlooding/nitrogen.bin b/F3:F303/NitrogenFlooding/nitrogen.bin index ec501e0..4f0e0ee 100755 Binary files a/F3:F303/NitrogenFlooding/nitrogen.bin and b/F3:F303/NitrogenFlooding/nitrogen.bin differ diff --git a/F3:F303/NitrogenFlooding/nitrogen.creator.user b/F3:F303/NitrogenFlooding/nitrogen.creator.user index 4a03d2d..6472914 100644 --- a/F3:F303/NitrogenFlooding/nitrogen.creator.user +++ b/F3:F303/NitrogenFlooding/nitrogen.creator.user @@ -1,10 +1,10 @@ - + EnvironmentId - {cf63021e-ef53-49b0-b03b-2f2570cdf3b6} + {7bd84e39-ca37-46d3-be9d-99ebea85bc0d} ProjectExplorer.Project.ActiveTarget @@ -38,32 +38,43 @@ true 1 false - false + true false - 1 + 0 true true 0 8 true false - 2 + 1 true - true + false true *.md, *.MD, Makefile - true + false true ProjectExplorer.Project.PluginSettings + + true + false + true + true + true + true + + + 0 + true true true Builtin.DefaultTidyAndClazy - 4 + 2 @@ -77,12 +88,12 @@ Desktop Desktop Desktop - {91347f2c-5221-46a7-80b1-0a054ca02f79} + {65a14f9e-e008-4c1b-89df-4eaa4774b6e3} 0 0 0 - /home/eddy/Docs/SAO/ELECTRONICS/STM32/F3-srcs/NitrogenFlooding + /Big/Data/00__Electronics/STM32/F303-nolib/NitrogenFlooding diff --git a/F3:F303/NitrogenFlooding/nitrogen.files b/F3:F303/NitrogenFlooding/nitrogen.files index 6a5d842..0b934c7 100644 --- a/F3:F303/NitrogenFlooding/nitrogen.files +++ b/F3:F303/NitrogenFlooding/nitrogen.files @@ -14,6 +14,8 @@ hashgen/hashgen.c hashgen/hdr.c hashgen/hdr.h hashgen/test.c +i2c.c +i2c.h main.c pdnuart.c pdnuart.h diff --git a/F3:F303/NitrogenFlooding/proto.c b/F3:F303/NitrogenFlooding/proto.c index 3612d17..5f702f5 100644 --- a/F3:F303/NitrogenFlooding/proto.c +++ b/F3:F303/NitrogenFlooding/proto.c @@ -16,7 +16,9 @@ * along with this program. If not, see . */ +#include "adc.h" #include "hardware.h" +#include "i2c.h" #include "proto.h" #include "strfunc.h" #include "version.inc" @@ -24,15 +26,36 @@ #include #include +static uint8_t I2Caddress = 0; + // parno - number of parameter (or -1); cargs - string with arguments (after '=') (==NULL for getter), iarg - integer argument -static int goodstub(int _U_ parno, const char _U_ *carg, int32_t _U_ iarg){ +static int goodstub(const char _U_ *cmd, int _U_ parno, const char _U_ *carg, int32_t _U_ iarg){ return RET_GOOD; } -static int leds(int parno, const char *c, int32_t i){ +static void sendkey(const char *cmd, int parno, int32_t i){ + USB_sendstr(cmd); + if(parno > -1) USB_sendstr(u2str((uint32_t)parno)); + USB_putbyte('='); USB_sendstr(i2str(i)); newline(); +} +static void sendkeyf(const char *cmd, int parno, float f){ + USB_sendstr(cmd); + if(parno > -1) USB_sendstr(u2str((uint32_t)parno)); + USB_putbyte('='); USB_sendstr(float2str(f, 2)); newline(); +} +static void sendkeyu(const char *cmd, int parno, uint32_t u){ + USB_sendstr(cmd); + if(parno > -1) USB_sendstr(u2str((uint32_t)parno)); + USB_putbyte('='); USB_sendstr(u2str(u)); newline(); +} + +static int leds(const char *cmd, int parno, const char *c, int32_t i){ if(parno < 0){ // enable/disable all - if(c){ LEDsON = i ? 1 : 0;} - USB_sendstr("LED="); USB_sendstr(u2str(LEDsON)); + if(c){ + LEDsON = (i ? 1 : 0); + if(!LEDsON) for(int _ = 0; _ < 4; ++_) LED_off(_); + } + sendkey("LEDon", -1, LEDsON); }else{ if(parno >= LEDS_AMOUNT) return RET_WRONGARG; if(c) switch(i){ @@ -40,32 +63,116 @@ static int leds(int parno, const char *c, int32_t i){ case 1: LED_on(parno); break; default: LED_off(parno); } - USB_sendstr("LED"); USB_putbyte('0' + parno); - USB_putbyte('='); USB_sendstr(u2str(LED_get(parno))); newline(); + sendkey(cmd, parno, LED_get(parno)); } - newline(); + return RET_GOOD; +} + +static int buzzer(const char *cmd, int _U_ parno, const char _U_ *c, int32_t i){ + if(c){ + if(i > 0) BUZZER_ON(); + else BUZZER_OFF(); + } + sendkey(cmd, -1, BUZZER_STATE()); + return RET_GOOD; +} + +static int i2scan(const char _U_ *cmd, int _U_ parno, const char _U_ *c, int32_t _U_ i){ + i2c_init_scan_mode(); + return RET_GOOD; +} +static int i2addr(const char *cmd, int _U_ parno, const char *c, int32_t i){ + if(c){ + if(i < 0 || i>= I2C_ADDREND) return RET_WRONGARG; + I2Caddress = (uint8_t) i; + } + sendkey(cmd, -1, I2Caddress); + return RET_GOOD; +} + +static int adcon(const char *cmd, int _U_ parno, const char *c, int32_t i){ + if(c) ADCON(i); + sendkey(cmd, -1, ADCONSTATE()); + return RET_GOOD; +} +static int adcval(const char *cmd, int parno, const char _U_ *c, int32_t i){ + if(parno >= NUMBER_OF_ADC_CHANNELS) return RET_WRONGPARNO; + if(parno < 0){ // all channels + for(i = 0; i < NUMBER_OF_ADC_CHANNELS; ++i) sendkey(cmd, i, getADCval(i)); + }else + sendkey(cmd, parno, getADCval(parno)); + return RET_GOOD; +} +static int adcvoltage(const char *cmd, int parno, const char _U_ *c, int32_t i){ + if(parno >= NUMBER_OF_ADC_CHANNELS) return RET_WRONGPARNO; + if(parno < 0){ // all channels + for(i = 0; i < NUMBER_OF_ADC_CHANNELS; ++i) sendkeyf(cmd, i, getADCvoltage(i)); + }else + sendkeyf(cmd, parno, getADCvoltage(parno)); + return RET_GOOD; +} +static int mcut(const char *cmd, int _U_ parno, const char _U_ *c, int32_t _U_ i){ + sendkeyf(cmd, -1, getMCUtemp()); + return RET_GOOD; +} + +static int reset(const char _U_ *cmd, int _U_ parno, const char _U_ *c, int32_t _U_ i){ + USB_sendstr("RESET!!!\n"); + USB_sendall(); + NVIC_SystemReset(); + return RET_GOOD; // never reached +} + +static int tms(const char _U_ *cmd, int _U_ parno, const char _U_ *c, int32_t _U_ i){ + sendkeyu(cmd, -1, Tms); + return RET_GOOD; +} + +static int pwm(const char *cmd, int parno, const char *c, int32_t i){ + if(parno < 0 || parno > 3) return RET_WRONGPARNO; + if(c) setPWM(parno, (uint8_t)i); + sendkeyu(cmd, -1, getPWM(parno)); return RET_GOOD; } typedef struct{ - int (*fn)(int, const char*, int32_t); + int (*fn)(const char*, int, const char*, int32_t); const char *cmd; const char *help; } commands; commands cmdlist[] = { {goodstub, "stub", "simple stub"}, + {NULL, "Different commands", NULL}, + {buzzer, "buzzer", "get/set (0 - off, 1 - on) buzzer"}, {leds, "LED", "LEDx=y; where x=0..3 to work with single LED (then y=1-set, 0-reset, 2-toggle), absent to work with all (y=0 - disable, 1-enable)"}, + {pwm, "pwm", "set/get x channel (0..3) pwm value (0..255)"}, + {reset, "reset", "reset MCU"}, + {tms, "tms", "print Tms"}, + {NULL, "I2C commands", NULL}, + {i2addr, "iicaddr", "set/get I2C address"}, + {i2scan, "iicscan", "scan I2C bus"}, + {NULL, "ADC commands", NULL}, + {adcval, "ADC", "get ADCx value (without x - for all)"}, + {adcvoltage, "ADCv", "get ADCx voltage (without x - for all)"}, + {mcut, "mcut", "get MCU temperature"}, + {adcon, "sensv", "turn on (1) or off (0) Tsens voltage"}, {NULL, NULL, NULL} }; static void printhelp(){ commands *c = cmdlist; USB_sendstr("https://github.com/eddyem/stm32samples/tree/master/F3:F303/NitrogenFlooding build#" BUILD_NUMBER " @ " BUILD_DATE "\n"); - while(c->fn){ - USB_sendstr(c->cmd); - USB_sendstr(" - "); - USB_sendstr(c->help); + while(c->cmd){ + if(!c->fn){ // header + USB_sendstr("\n "); + USB_sendstr(c->cmd); + USB_putbyte(':'); + }else{ + USB_sendstr(c->cmd); + USB_sendstr(" - "); + USB_sendstr(c->help); + } newline(); ++c; } @@ -73,6 +180,7 @@ static void printhelp(){ static int parsecmd(const char *str){ char cmd[CMD_MAXLEN + 1]; + //USB_sendstr("cmd="); USB_sendstr(str); USB_sendstr("__\n"); if(!str || !*str) return RET_CMDNOTFOUND; int i = 0; while(*str > '@' && i < CMD_MAXLEN){ cmd[i++] = *str++; } @@ -88,10 +196,13 @@ static int parsecmd(const char *str){ str = omit_spaces(++str); getint(str, &iarg); } - } + }else str = NULL; commands *c = cmdlist; - while(c->fn){ - if(strcmp(c->cmd, cmd) == 0) return c->fn(parno, str, iarg); + while(c->cmd){ + if(strcmp(c->cmd, cmd) == 0){ + if(!c->fn) return RET_CMDNOTFOUND; + return c->fn(cmd, parno, str, iarg); + } ++c; } return RET_CMDNOTFOUND; @@ -105,6 +216,7 @@ static int parsecmd(const char *str){ const char *cmd_parser(const char *txt){ int ret = parsecmd(txt); switch(ret){ + case RET_WRONGPARNO: return "Wrong parameter number\n"; break; case RET_CMDNOTFOUND: printhelp(); return NULL; break; case RET_WRONGARG: return "Wrong command parameters\n"; break; case RET_GOOD: return NULL; break; diff --git a/F3:F303/NitrogenFlooding/proto.h b/F3:F303/NitrogenFlooding/proto.h index cbe22cc..6cd9e1a 100644 --- a/F3:F303/NitrogenFlooding/proto.h +++ b/F3:F303/NitrogenFlooding/proto.h @@ -30,10 +30,11 @@ #define CMD_MAXLEN (32) enum{ - RET_CMDNOTFOUND = -2, - RET_WRONGARG = -1, - RET_GOOD = 0, - RET_BAD = 1 + RET_WRONGPARNO = -3, // wrong parameter number + RET_CMDNOTFOUND = -2, // command not found + RET_WRONGARG = -1, // wrong argument + RET_GOOD = 0, // all OK + RET_BAD = 1 // something wrong }; #define printu(x) do{USB_sendstr(u2str(x));}while(0) diff --git a/F3:F303/NitrogenFlooding/version.inc b/F3:F303/NitrogenFlooding/version.inc index f53810e..230edd2 100644 --- a/F3:F303/NitrogenFlooding/version.inc +++ b/F3:F303/NitrogenFlooding/version.inc @@ -1,2 +1,2 @@ -#define BUILD_NUMBER "2" +#define BUILD_NUMBER "24" #define BUILD_DATE "2023-03-22" diff --git a/F3:F303/inc/ld/stm32f302xB.ld b/F3:F303/inc/ld/stm32f302xB.ld index 41dfe30..0fd92d5 100644 --- a/F3:F303/inc/ld/stm32f302xB.ld +++ b/F3:F303/inc/ld/stm32f302xB.ld @@ -2,6 +2,7 @@ MEMORY { rom (rx) : ORIGIN = 0x08000000, LENGTH = 128K ram (rwx) : ORIGIN = 0x20000000, LENGTH = 40K + ccmram (rwx) : ORIGIN = 0x10000000, LENGTH = 0 } PROVIDE(_BLOCKSIZE = 2048);