I am trying to code the stm32h743 for spi communication and i can't seem to transmit. I am using PA5 as SCK and PD7 as MOSI. I read the procedure required that is in the reference manual (49.4.9 reference manual ) and wrote the code below but it doesn't transmit 0x3 (checked with oscilloscope).
RCC->AHB4ENR |= RCC_AHB4ENR_GPIOAEN; // clock enable for gpio A,B,D
RCC->AHB4ENR |= RCC_AHB4ENR_GPIODEN;
RCC->AHB4ENR |= RCC_AHB4ENR_GPIOBEN;
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; // enable SPI clock
GPIOA->MODER &= ~GPIO_MODER_MODER5; // enable alternate functions for gpioa and d
GPIOA->MODER |= GPIO_MODER_MODER5_1; // 0b10 for alternate function, (PIN5)
GPIOA->AFR[0] |= (0x05 << 5 * 4); // AF5 for SPI which is 0101 (it's a 4bit variation)
GPIOA->MODER &= ~GPIO_MODER_MODER6; // PIN6
GPIOA->MODER |= GPIO_MODER_MODER6_1;
GPIOA->AFR[0] |= (0x05 << 6 * 4);
GPIOD->MODER &= ~GPIO_MODER_MODER7; // PIN7
GPIOD->MODER |= GPIO_MODER_MODER7_1;
GPIOD->AFR[0] |= (0x05 << 7 * 4);
SPI1->CFG1=(7u << SPI_CFG1_DSIZE_Pos); // 8bit data size
SPI1->CFG2|=SPI_CFG2_MASTER ; // master mode
SPI1->CR1|=SPI_CR1_SPE; // spi enable
SPI1->CR1|=SPI_CR1_CSTART; // transfer start
*(volatile uint8_t *)&SPI1->TXDR = 0x3;
I am obviously missing some configuration settings. Any ideas?
Related
I'm learning to use DMA on a STM32F446 and tried to send data over USART. The goal is to do some calculations and send the results to a PC via RS232.
Here is my MWE:
#include <stdint.h>
#include <stdio.h>
#include "stm32f446xx.h"
#define BAUDRATE ( 9600 )
#define USART2_TX_PIN (2)
#define USART2_RX_PIN (3)
int main(void) {
volatile uint32_t core_clock_hz = 16000000;
uint16_t uartdiv = core_clock_hz / BAUDRATE;
uint8_t TX_buffer[2];
TX_buffer[0] = 48; // this is a "0" in ASCII
TX_buffer[1] = 49; // this is a "1" in ASCII
// configure peripheral clocks
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; // Enable the SYSCFG peripheral
RCC->APB1ENR |= ( RCC_APB1ENR_USART2EN ); // Enable peripheral clocks: USART2
RCC->AHB1ENR |= ( RCC_AHB1ENR_GPIOAEN ); // Enable peripheral clocks: GPIOA
RCC->AHB1ENR |= ( RCC_AHB1ENR_DMA1EN ); // Enable peripheral clock: DMA1
// Configure pins A2 (TX), A3 (RX) for USART2. TX: alternate out push-pull, RX: in floating
// TX
GPIOA->MODER &= ~(0x3 << (USART2_TX_PIN*2)); // reset all bits
GPIOA->MODER |= (0x2 << (USART2_TX_PIN*2)); // 10 = alternate
GPIOA->OSPEEDR &= ~(0x3 << (USART2_TX_PIN*2)); // reset all bits
GPIOA->OSPEEDR |= (0x0 << (USART2_TX_PIN*2)); // 00 = low speed
GPIOA->OTYPER &= ~(0x1 << USART2_TX_PIN); // 0 = push-pull
GPIOA->PUPDR &= ~(0x3 << (USART2_TX_PIN*2)); // 00 = no pull-up / pull-down
// RX
GPIOA->MODER &= ~(0x3 << (USART2_RX_PIN*2)); // reset all bits
GPIOA->MODER |= (0x2 << (USART2_RX_PIN*2)); // 10 = alternate
GPIOA->PUPDR &= ~(0x3 << (USART2_RX_PIN*2)); // reset all bits
GPIOA->PUPDR |= (0x0 << (USART2_RX_PIN*2)); // 00 = no pull-up / pull-down , 01 = pull-up
// set alternate pin function AF7 for PA2 and PA3. AFR[0] = AFRL
GPIOA->AFR[0] &= ~(0xF << USART2_TX_PIN*4); // clear all bits
GPIOA->AFR[0] |= (0x7 << USART2_TX_PIN*4); // set AF7
GPIOA->AFR[0] &= ~(0xF << USART2_RX_PIN*4); // clear all bits
GPIOA->AFR[0] |= (0x7 << USART2_RX_PIN*4); // set AF7
USART2->BRR = ( ( ( uartdiv / 16 ) << USART_BRR_DIV_Mantissa_Pos ) | ( ( uartdiv % 16 ) << USART_BRR_DIV_Fraction_Pos ) ); // configure USART baudrate
USART2->CR1 |= ( USART_CR1_RE | USART_CR1_TE | USART_CR1_UE ); // Enable the USART peripheral
// Main loop
while ( 1 ) {
DMA1_Stream6->CR &= ~(DMA_SxCR_EN); // deactivate DMA stream for configuration
DMA1_Stream6->CR &= ~(DMA_SxCR_CHSEL); // clear bits
DMA1_Stream6->CR |= (DMA_SxCR_CHSEL_2); // 100 = channel 4
DMA1_Stream6->CR &= ~(DMA_SxCR_PL); // priority 00 = low
DMA1_Stream6->CR &= ~(DMA_SxCR_PSIZE); // size 00 = 8bit
DMA1_Stream6->CR |= (DMA_SxCR_MINC); // increment memory pointer with each DMA transfer
DMA1_Stream6->CR &= ~(DMA_SxCR_DIR); // clear bits
DMA1_Stream6->CR |= (DMA_SxCR_DIR_0); // 01 = memory-to-peripheral
DMA1_Stream6->PAR = ( uint32_t )&USART2->DR; // peripheral memory address
DMA1_Stream6->M0AR = ( uint32_t )&TX_buffer; // data memory address
DMA1_Stream6->NDTR = ( uint16_t ) 2; // number of bytes to transfer
DMA1->HISR &= ~(DMA_HISR_TCIF6 | DMA_HISR_HTIF6 | DMA_HISR_TEIF6 | DMA_HISR_DMEIF6 | DMA_HISR_FEIF6); // clear DMA flags
USART2->SR &= ~(USART_SR_TC); // clear USART transfer complete flag
DMA1_Stream6->CR |= (DMA_SxCR_EN); // set EN bit to activate DMA stream
// does not help: USART2->CR1 |= ( USART_CR1_RE | USART_CR1_TE | USART_CR1_UE ); // Enable the USART peripheral
USART2->CR3 |= (USART_CR3_DMAT); // enable USART DMA mode
// wait for end of transfer
while ( !(DMA1->HISR && DMA_HISR_TCIF6) ) {}
while ( !(USART2->SR && USART_SR_TC) ) {}
//
// do calculations here, modify TX_buffer for next transfer cycle
//
} // while (1)
} // main
The code should send the data in TX_buffer in an endless loop, thus I was expecting to receive a sequence of 01010101... in the PC's terminal. However, I only get a single 01 and then the transmission stops. As data is generally sent, GPIOs, clocks, ... seem to be configured correctly.
I guess, after one loop cycle of the while(1), the DMA or the USART are not reset to a state where they accept new transfers, but I couldn't figure out what exactly is missing. I already thought about missing ISR routines and IRQs. Many examples on the net use them, but I could not find any functionality in them which is not already in my main loop. Thus, my MWE does not use any interrupts or interrupt routines. All interrupts are deactivated in the DMA configuration register.
In circular mode of the DMA, the endless transmission is working, but this seems not to be the appropriate solution for my scenario of calculate -> send > calculate -> send -> ...
How do USART and DMA have to be configured in this case to allow multiple subsequent transmissions?
EDIT:
Added a compileable MWE.
Additional information which might be helpful:
There are also no interrupts configured for the USART.
My compiler options are:
CFLAGS += -mcpu=$(MCU_SPEC)
CFLAGS += -mthumb
CFLAGS += -Wall
# (Set error messages to appear on a single line.)
CFLAGS += -fmessage-length=0
CFLAGS += --specs=nosys.specs
CFLAGS += -ffunction-sections
CFLAGS += -fdata-sections
CFLAGS += -lm
# (Custom flags sent to the compiler)
CFLAGS += -D$(ST_MCU_DEF)
CFLAGS += -DVVC_$(MCU_CLASS)
#CFLAGS += -DVVC_$(MCU)
# FPU config
ifeq ($(MCU_CLASS), $(filter $(MCU_CLASS), L4 G4 WB F4))
CFLAGS += -mhard-float
CFLAGS += -mfloat-abi=hard
CFLAGS += -mfpu=fpv4-sp-d16
else
CFLAGS += -msoft-float
CFLAGS += -mfloat-abi=soft
endif
Let's look at the reference manual of the MCU together. You are not clearing the flags of DMA.
DMA1->HISR &= ~(DMA_HISR_TCIF6 | DMA_HISR_HTIF6 | DMA_HISR_TEIF6 | DMA_HISR_DMEIF6 | DMA_HISR_FEIF6); // clear DMA flags
USART2->SR &= ~(USART_SR_TC); // clear USART transfer complete flag
One of these lines works, the other one doesn't do anything, because
USART SR TC bit says it's rc_w0, while DMA's HISR is all "r" - read only bits. Writing to that register doesn't do anything. You need to use dedicated clear flag register of DMA.
So instead, this should work (the register is write-only):
DMA1->HIFCR = DMA_HIFCR_CTCIF6 | DMA_HIFCR_CHTIF6 | DMA_HIFCR_CTEIF6 | DMA_HIFCR_CDMEIF6 | DMA_HIFCR_CFEIF6; // clear DMA flags
Notice I'm not using |=, because |= will mean that we need to read the register first (like x |= y means x = x | y), and the register is not readable. So you prepare value for it and write it straight there without reading anything from it.
I am adapting this bootloader for STM32F373CC to my device. To indicate that the device is powered but in bootloader mode, I'd like to turn on some of the status LEDs. However, this bootloader doesn't use the STM Cube MX libraries, so I have to code it low-level. The header file stm32f373xc.h is included, so I can use expressions like GPIOB_BASE.
I tried the following first thing in main(), but unfortunately it doesn't work:
// turn on GPIOB clock: SET_BIT(RCC->AHBENR, RCC_AHBENR_GPIOBEN);
uint32_t* rcc = (uint32_t*)RCC_BASE;
*(rcc+0x14) |= RCC_AHBENR_GPIOBEN; // AHBENR is at offset 0x14
// configure Port B, pins 4 and 5 to GPIO, Open Drain, low.
uint32_t* gpiob = (uint32_t*)GPIOB_BASE;
*(gpiob) |= 0x500; // GPIO output mode --- GPIOB_MODER = 0x500; (bits 11:8 = 0101), offset 0
*(gpiob) &= ~0xA00;
*(gpiob+0x04) |= 0x30; // output type open drain --- GPIOB_OTYPER = 0x30; (bits 5:4 = 11), offset 0x04
*(gpiob+0x0c) &= ~0xF00; // pull up/down off --- GPIOB_PUPDR = 0x0; (bits 11:8 = 0000), offset 0x0c
*(gpiob+0x14) &= ~0x30; // output low --- GPIOB_ODR = 0x0; (bits 5:4 = 00), offset 0x14
Any ideas what I'm missing? How can I find out if the problem is the clocking of the Port B, or the pin configuration?
I found this similar post, but the first answer requires the entire CMSIS, and the second answer lacks comments, so I don't fully understand what they are doing.
I hope that you know that open-drain outputs require pull-up (internal or external)
Use CMSIS definitions, not magic numbers and operations.
requires the entire CMSIS
And what is the problem? CMSIS does not add any overhead to your code, only handy definitions and inline functions, which do not change the size of the code if not used.
Also, HAL has very handy macros useful even if you do not use HAL library itself (it also will not increase the code size even by a single byte)
I will not check your magic offsets and numbers.
First error: after enabling the peripheral clock you need to wait. It is described in the Reference Manual. You do not wait and your first MODER operation has no effect. HAL macros read back the register to make sure that the operation has completed.
Example from STM32L4:
#define __HAL_RCC_GPIOB_CLK_ENABLE() do { \
__IO uint32_t tmpreg; \
SET_BIT(RCC->AHB2ENR, RCC_AHB2ENR_GPIOBEN); \
/* Delay after an RCC peripheral clock enabling */ \
tmpreg = READ_BIT(RCC->AHB2ENR, RCC_AHB2ENR_GPIOBEN); \
UNUSED(tmpreg); \
} while(0)
Then use the CMSIS registers typedefs and definitions.
#define PIN4 4
#define PIN5 5
GPIOB -> MODER &= ~((0b11 << (2 * PIN5)) | (0b11 << (2 * PIN4)));
GPIOB -> MODER |= ((0b01 << (2 * PIN5)) | (0b01 << (2 * PIN4)));
GPIOB -> OTYPER &= ~((1 << PIN4) | (1 << PIN5));
GPIOB -> OTYPER |= (1 << PIN4) | (1 << PIN5);
GPIOB -> BSRR = (1 << (PIN4 + 16)) | (1 << (PIN5 + 16)); // set the pins low
I'm trying to use the I2C interface of the STM32F3 Nucleo-Board to communicate with a EEPROM.
Unfortunately I don't have a clock signal.
I tried to get a clock signal by setting the bits in the registers and also by using CubeMX. Both times I've got the same result: no clock signal.
Thanks for your help!
Here my code...
void I2C_Init(void){
RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
GPIOA->MODER |= GPIO_MODER_MODER15_1;
GPIOA->MODER &= ~GPIO_MODER_MODER15_0; //AF
GPIOB->MODER |= GPIO_MODER_MODER7_1;
GPIOB->MODER &= ~GPIO_MODER_MODER7_0; //AF
GPIOA->OTYPER |= GPIO_OTYPER_OT_15;
GPIOB->OTYPER |= GPIO_OTYPER_OT_7; //Open drain
GPIOA->OSPEEDR |= (GPIO_OSPEEDER_OSPEEDR15_0 | GPIO_OSPEEDER_OSPEEDR15_1); //speed high
GPIOB->OSPEEDR |= (GPIO_OSPEEDER_OSPEEDR7_0 | GPIO_OSPEEDER_OSPEEDR7_1);
GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPDR15_0 | GPIO_PUPDR_PUPDR15_1);
GPIOB->PUPDR &= ~(GPIO_PUPDR_PUPDR7_0 | GPIO_PUPDR_PUPDR_1); //no pull -> external pull up resistor used
PA15_AF4();
PB7_AF4(); //alternate function 4 used
RCC->CFGR3 |= (1<<I2C1SW); //SYSCLK
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; //clock enable
I2C1->TIMINGR = 0x10707DBC; //with CubeMX
I2C1->CR1 |= I2C_CR1_PE; //peripheral enable
}
I use PA15 for SCL and PB7 for SDA.
I2C will only generate a clock on the line if it is sending or expecting data. Try sending data and then using a scope to probe the data and clock line.
I try to make master and slave stm32f103 (bluepills) communicate. But I am having trouble with receiving. When I connect my master to logic analyser I can see MOSI as in the picture
Logic Analyser
In the picture, MOSI is sending "Y" letter. But not all clock pulses same.(I don't know if this is the reason of communication fail)
here is my schematics and my code I simplified as much as I can.
Master Code:
int i;
RCC ->APB2ENR |= 0x00001004; //SPI1,GPIOA clock en
GPIOA ->CRL &= 0x00000000;
GPIOA ->CRL |= 0xb0b33333;
SPI1->CR1 = SPI_CR1_SSM| SPI_CR1_SSI| SPI_CR1_MSTR|SPI_CR1_BR_2;
SPI1->CR1 |= SPI_CR1_SPE; // enable SPI
while(1){
SPI1 -> DR = 'A';
for(int i = 0 ;i<400000;i++);
while( !(SPI1->SR & SPI_SR_TXE) ); // wait until transmit buffer empty
}
and Slave
int i;
RCC ->APB2ENR |= 0x0000100c; //SPI1,GPIOA,GPIOB clock en
GPIOB ->CRH &= 0x00000000;
GPIOB ->CRH |= 0x33333333;
GPIOA ->CRL &= 0x00000000;
GPIOA ->CRL |= 0x4b443333;
GPIOA ->CRH &= 0x00000000;
GPIOA ->CRH |= 0x33333333;
SPI1->CR1 = SPI_CR1_SSM| SPI_CR1_SSI| SPI_CR1_BR_2;
SPI1->CR1 |= SPI_CR1_SPE; // enable SPI
SPI1->CR1 &=~SPI_CR1_MSTR; //disable master
for(int c=0;c<5;c++){
LCD_INIT(cmd[c]);
}
while(1){
while( !(SPI1->SR & SPI_SR_RXNE));
char a = SPI1 ->DR;
for (i=0;i<400000;i++);
LCD_DATA(a);
for (i=0;i<400000;i++);
}
}
My Schematic:
Schematic
The problem is slave is not receiving any data.
It stucks in the loop while( !(SPI1->SR & SPI_SR_RXNE));
First, what are your HCLK and APB2 bus frequencies? If I'm not mistaken, you seem to use (fPLCK / 32) for SPI clock and your logic analyzer shows ~2 or 3 MHz clock. If your APB2 frequency is higher than the 72 MHz limit, you may experience clock problems.
In the slave, you use SSM (software slave management) and activate SSI (internal slave select). The name of the SSI bit is misleading: It mimics the physical NSS pin. So when SSI = 1, the slave is not selected. This is probably the reason why the slave ignores the incoming bytes.
I want to implement dual regular simultaneous mode of ADC1,ADC2 and two DMA ADC channels of stm32f303 discovery.
In CubeMX examples:
Usage of two DMA channels (one for ADC master, one for ADC slave) is
also possible: this is the recommended configuration in case of high
ADC conversions rates and applications using other DMA channels
intensively.
According to AN4195
When using the DMA, there are two possible cases: • Use of two
separate DMA channels for master and slave. Each ADC (in this case,
the MDMA[1:0]) must be kept cleared. The first DMA channel is used to
read the master ADC converted data from ADC_DR, and the DMA requests
are generated at each EOC event of the master ADC. The second DMA
channel is used to read the slave ADC converted data from ADC_DR, and
the DMA requests are generated at each EOC event of the slave ADC.
For 1 channel the code:
HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
HAL_ADCEx_Calibration_Start(&hadc2, ADC_SINGLE_ENDED);
HAL_ADC_Start(&hadc2);
HAL_ADCEx_MultiModeStart_DMA(&hadc1, (uint32_t*)buffer, 3);
But how can we run 2 channels? HAL_ADCEx_MultiModeStart_DMA is for 1 channel as I can understand
Something like for independent mode is not working
HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
HAL_ADCEx_Calibration_Start(&hadc2, ADC_SINGLE_ENDED);
HAL_ADC_Start(&hadc2);
HAL_ADC_Start_DMA(&hadc1,(uint32_t*)ADC1_data,sizeof(ADC1_data)/sizeof(ADC1_data[0]));
HAL_ADC_Start_DMA(&hadc2,(uint32_t*)ADC2_data,sizeof(ADC2_data)/sizeof(ADC2_data[0]));
I'm not 100% sure, if this also applies to the F3 series, but I've written this toturial for the F103C8 regarding ADC dual regular simultanous mode:
http://www.bepat.de/2020/11/27/stm32f103c8-adc-dual-regular-simultaneous-mode/
maybe you'll find it helpfull.
To cut a long story short: I guess you are starting the ADCs the wrong way.
ADC2 needs to be started in normal mode BEFORE ADC1 starts with
HAL_ADC_Start(&hadc2);
ADC1 is started afterwards with:
HAL_ADCEx_MultiModeStart_DMA(&hadc1, ADC_Buffer, ADC_BufferSize);
Funny - great HAL library :). This is my working code: interleaved mode - one DMA transfer half word per two conversions (master and slave). 8-bit resolution. Register version.
DMA1_Channel1 -> CPAR = (uint32_t)&(ADC12_COMMON -> CDR);
DMA1_Channel1 -> CMAR = (uint32_t)&obuff[0][0];
DMA1_Channel1 -> CNDTR = 1 * 1024;
DMA1_Channel1 -> CCR = DMA_CCR_MINC | DMA_CCR_TCIE | DMA_CCR_EN | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0 | DMA_CCR_TEIE | (DMA_CCR_PL_Msk);
ADC12_COMMON -> CCR = (0b11 << ADC12_CCR_MDMA_Pos) | (0b111 << ADC12_CCR_MULTI_Pos);
ADC1 -> CFGR = ADC_CFGR_DMAEN | (0b10 << ADC_CFGR_RES_Pos);
ADC1 -> CFGR &= ~(ADC_CFGR_EXTEN_Msk | ADC_CFGR_EXTSEL_Msk); // software trigger only, converting as fast as possible
ADC1 -> CFGR |= ADC_CFGR_CONT;
ADC1 -> SMPR1 = 0;
ADC1 -> SMPR2 = 0;
ADC1 -> SQR1 &= ~(ADC_SQR1_L_Msk);
ADC1 -> SQR1 &= ~(ADC_SQR1_SQ1_Msk);
ADC1 -> SQR1 |= (1 << ADC_SQR1_SQ1_Pos);
ADC2 -> CFGR = ADC_CFGR_DMAEN | (0b10 << ADC_CFGR_RES_Pos);
ADC2 -> SMPR1 = 0;
ADC2 -> SMPR2 = 0;
ADC2 -> SQR1 &= ~(ADC_SQR1_L_Msk);
ADC2 -> SQR1 &= ~(ADC_SQR1_SQ1_Msk);
ADC2 -> SQR1 |= (1 << ADC_SQR1_SQ1_Pos);
ADC1 -> CR |= ADC_CR_ADSTART;
DMA1_Channel1 interrupt is called when DMA finishes transfers.