I'm using STM32F746ZG and use five UARTs. All UARTs are working fine.
Can someone tell me the procedure to change the baud rate on the USART once it has already been initialized? I'm using USART6 and initialized with 9600 baud rate. After booting, there is no any communication through USART. I want to change the baud rate from 9600 to 57600 or 115200. For this changing, I called HAL_UART_DeInit() and MX_USART6_UART_Init_57600() but it doesn't work.
If I didn't change the baud rate, it works fine. But if I change the baud rate, I can't receive the data through USART.
If somebody knows the solution, please let me know.
The followings are my code.
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_UART7_Init();
MX_UART8_Init();
MX_USART2_UART_Init();
MX_USART3_UART_Init();
MX_USART6_UART_Init();
}
void MX_USART6_UART_Init(void)
{
huart6.Instance = USART6;
huart6.Init.BaudRate = 9600;
huart6.Init.WordLength = UART_WORDLENGTH_8B;
huart6.Init.StopBits = UART_STOPBITS_1;
huart6.Init.Parity = UART_PARITY_NONE;
huart6.Init.Mode = UART_MODE_TX_RX;
huart6.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart6.Init.OverSampling = UART_OVERSAMPLING_16;
huart6.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart6.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart6) != HAL_OK)
{
Error_Handler();
}
}
void MX_USART6_UART_Init_57600(void)
{
huart6.Instance = USART6;
huart6.Init.BaudRate = 57600; // change from 9600 to 57600
huart6.Init.WordLength = UART_WORDLENGTH_8B;
huart6.Init.StopBits = UART_STOPBITS_1;
huart6.Init.Parity = UART_PARITY_NONE;
huart6.Init.Mode = UART_MODE_TX_RX;
huart6.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart6.Init.OverSampling = UART_OVERSAMPLING_16;
huart6.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart6.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart6) != HAL_OK)
{
Error_Handler();
}
}
int Change_UART(void)
{
HAL_UART_DeInit(&huart6);
MX_USART6_UART_Init_57600();
}
I called Change_UART() but it doesn't work.
Your question should be: how to change the bautrate using the bloatware HAL?
I do not know.
But it can be archived in 3 lines of the simple code.
USART6 -> CR1 &= ~(USART_CR1_UE);
USART6 -> BRR = NEWVALUE;
USART6 -> CR1 |= USART_CR1_UE;
For changing baudrate you don't need to reset UART peripheral, just stop any active transfers (polling/IT/DMA). I use a mix of both:
huart.Instance->BRR = UART_BRR_SAMPLING8(HAL_RCC_GetPCLK2Freq(), new_baudrate);
Where UART_BRR_SAMPLING8() is a macro from stm32f4xx_hal_uart.h and HAL_RCC_GetPCLK2Freq() function comes from _hal_rcc.c.
This way I don't have to calculate BRR values manually, nor execute the whole initialization procedure, which actually toggles GPIO states, thus generating noise on serial line for whatever is sitting on other end of it.
You have to abort all running HAL_UART funttions, then de-initialize the uart, change the baudrate init value and initialize it again:
HAL_UART_Abort_IT(&huart1);
HAL_UART_DeInit(&huart1);
huart1.Init.BaudRate = 57600;
if (HAL_UART_Init(&huart1) != HAL_OK) {
Error_Handler();
}
if (HAL_UART_Receive_IT(&huart1, BUFFER, YOUR_BUFFER_SIZE) != HAL_OK) {
Error_Handler();
}
Originally I was really excited by P__J__'s simple answer, but it turns out you can't just put the desired baud rate into BRR - it has to be a function of oversampling and the clock rate.
I used more or less the same method but with "LL_USART_SetBaudRate" to fill the register
Related
I have added ADC functionality to my Nucleo-F446RE development board. 4 channels, DMA enabled, scan and continuous conversion mode enabled, DMA continuous requests enabled, varying sample time per channel. I'll post code at the bottom of this post (all HAL, all done in STM32CubeMX).
I have found some strange behaviour when the channels are unpopulated (e.g., analog channel pin left open). All four channels will hover at around 0.9V with no channels connected. If I add a 3.3V source to channel 0, it'll show 3.3V, but CH1 will show 2.5V, CH2 will show 1.9V, CH3 1.6V. A waterfall effect. That waterfall effect is the same if I move the 3.3V source to CH1 and leave the rest unpopulated, and the waterfall effect loops back around to CH0.
If I give each channel their own source, they'll all show them correctly, but when unpopulated the channels are influenced by the populated channel. Why is this? I have found some sources saying that this is because of the sample+hold capacitor, and the solution is to correct the sampling times, but I have played a lot with the times going from very fast to as slow as possible sampling (I am only interested in sampling the data at 1kHz, but the ADC conversion seems to be, at a minimum, a magnitude above this), but it doesn't make a change. I wondered if changing the analog channel pin configuration to pull-down would help, but again no change.
I am hoping that this isn't something to be too concerned about, as the channels appear correct when populated, but perhaps there is some background influence that I am not seeing even when populated that I want to avoid. I am certain I haven't optimised my circuit, so any advice on that would also be great. There are lots of tutorials and examples online for STM32 ADC DMA with a single channel, but not so many with multi-channel. I also don't find the STM32 provided examples to be too helpful and often seem very inefficient.
ADC definitions
(main clock 180MHz, APB2 prescaler 2 = 90MHz, although I have also dropped it to a prescaler of 16 (11.25MHz) which didn't help)
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV8;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 4;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = 2;
sConfig.SamplingTime = ADC_SAMPLETIME_112CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_4;
sConfig.Rank = 3;
sConfig.SamplingTime = ADC_SAMPLETIME_56CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_8;
sConfig.Rank = 4;
sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
DMA definition
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**ADC1 GPIO Configuration
PA0-WKUP ------> ADC1_IN0
PA1 ------> ADC1_IN1
PA4 ------> ADC1_IN4
PB0 ------> ADC1_IN8
*/
GPIO_InitStruct.Pin = analog1_Pin|analog2_Pin|analog3_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = analog4_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(analog4_GPIO_Port, &GPIO_InitStruct);
/* ADC1 DMA Init */
/* ADC1 Init */
hdma_adc1.Instance = DMA2_Stream0;
hdma_adc1.Init.Channel = DMA_CHANNEL_0;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_adc1.Init.Mode = DMA_NORMAL;
hdma_adc1.Init.Priority = DMA_PRIORITY_MEDIUM;
hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);
/* ADC1 interrupt Init */
HAL_NVIC_SetPriority(ADC_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(ADC_IRQn);
Analog read code
(analog_scale is called once per channel every 1kHz)
#include "dma.h"
#include "adc.h"
#include "analog.h"
volatile uint32_t analogBuffer[4];
void analog_init()
{
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&analogBuffer, 4);
}
uint16_t analog_scale(char ch)
{
return (uint16_t)(((analogBuffer[ch] * 3.3) / 4096.0) * 1000.0);
}
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&analogBuffer, 4);
HAL_GPIO_TogglePin(test4_GPIO_Port, test4_Pin);
}
That's not a Software issue, it's normal hardware behavior.
If ADC pins are floating, they "gather" stray voltages, e.g. from adjacent Sample and Hold Capacitors, from the Voltage Reference or any voltage that is induced in the traces on the PCB or attached cables.
The "Waterfall" effect you see, is simply your input voltage on Channel 0 or 1 coupling through the sample and hold capacitors and resistors from one channel to the next, transferred by the multiplexers parasitic capacitances: a small amount of charge is transferred from one voltage path to the next while switching through the channels, and this charge has no path to flow when the connections are open, except through the ADC, resulting in a pseudo-voltage reading.
To prevent this, tie all unused channels to ground, using appropriate pull down resistors (10 kOhm should be OK …), or if you want a software solution: multiply all unused channels with 0.
sorry in advance, i am new at this kind of projects.
I tried to implement a connection to an ADC (LTC1609) via SPI. I just want to get the data from the ADC as quick as possible. The ADC has just a output-line, so i chose the "read only mode". But i also tried to use the HAL_SPI_TransmitReceive funktion because i remembered that the spi exchange starts with writeing in the transmit register (at least with the HCS12).
The ADC needs also some additional signals changes between pulling down the NSS and starting the spi connection so i put the NSS in software mode.
The Cofiguration looks like this:
static void MX_SPI1_Init(void)
{
/* SPI1 parameter configuration*/
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES_RXONLY;
hspi1.Init.DataSize = SPI_DATASIZE_16BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
}
I write a funktion to read from ADC and store the data.
void LTC1609_ADU_Read(uint8_t *data)
{
uint8_t buffer_rx[2];
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, 0); // NSS low (Pin PA4) Start
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, 0); // ADU R/NC low (Pin PG6) Start conversion
delay_hns (1); // wait 1us (10 x 100ns)
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, 1); // ADU R/NC high (Pin PG6)
if(HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_8) == 1) // /BUSY is high? -> start SPI
{
if(HAL_SPI_Receive(&hspi1, buffer_rx,2,10)!= HAL_OK);
{
SPI_error = HAL_SPI_GetError(&hspi1); // get the SPI error
Error_Handler(); // stop in error
}
}
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, 1); // Set ADU /CS high (Pin PA4) fin
*data = buffer_rx[1]; // store received data
}
In the while(1) i called it like this with a delay of 100 ms for testing:
LTC1609_ADU_Read(&buffer_A_rx);
Problem:
The uC (STM32L4R5ZIT6P) dont start the SPI clk to trigger out the data from ADC. Also the Clock polarity is somtimes low (like i want) and sometimes high [could also be a messuring problem, i messure it with a sheep logic analyser 24Mhz 8CH from amazon].
If i start the programm, it runs into the error handler and the errorcode is 0x20. I found this explaining:
HAL_SPI_ERROR_FLAG 0x00000020U /*!< Flag: RXNE,TXE, BSY */
Can anyone give me a tip on what could be the reason? Did iam doing something wrong or could the hardware be damaged? Thanks in advance!
screenshot signals without clk
One problem that I see in your code is that you check the BUSY pin immediately after setting R/C high and abort the transaction if BUSY is low.
According to the datasheet, BUSY line may be low for up to 3 us (t3).
Try waiting for the busy pin to go high, for example by adding a loop as shown below.
void LTC1609_ADU_Read(uint8_t *data)
{
uint8_t buffer_rx[2];
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, 0); // NSS low (Pin PA4) Start
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, 0); // ADU R/NC low (Pin PG6) Start conversion
delay_hns(1); // wait 1us (10 x 100ns)
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, 1); // ADU R/NC high (Pin PG6)
uint8_t retries = 0;
do {
delay_hns(1);
} while (HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_8) == 0 && ++retries < 5);
if (HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_8) == 1) // /BUSY is high? -> start SPI
{
if (HAL_SPI_Receive(&hspi1, buffer_rx, 2, 10) != HAL_OK)
{
SPI_error = HAL_SPI_GetError(&hspi1); // get the SPI error
Error_Handler(); // stop in error
}
}
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, 1); // Set ADU /CS high (Pin PA4) fin
*data = buffer_rx[1]; // store received data
}
Also, there is a stray ; in your code, on this line:
if(HAL_SPI_Receive(&hspi1, buffer_rx,2,10)!= HAL_OK);
I am trying to do uart communication. However, HAL_UART_TxCpltCallback function does not work.
Also, USART1_IRQHandler is not executed. I think there is a setting that enables uart and interrupt, but
I don't know. The source code is as follows. If you have more information, please tell me. Thanks.
/// main source
HW_UART_Init( );
uint8_t init_ment[60] = "\n\rWelcome to RF Test program\n\r";
strcpy(uart1_txbuffer,init_ment);
HAL_UART_Transmit_IT(&huart1, uart1_txbuffer, 30);
HAL_Delay(50);
InitQueue(&queue);
HAL_UART_Receive_IT(&huart1, uart1_rxbuffer, 1);
HAL_Delay(1);
void HW_UART_Init( void )
{
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 19200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
}
If you are using cubeMX for initializing your MCU, there is a tab in USART section "NVIC Settings" which you can enable USART1 global interrupt over there.
It adds these two lines in your code, in stm32f4xx_hal_msp.c file.
HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
I suggest you to use cubeMX.
If you do your own configuration without cube mx, first you should check if you add convenient uart files into your project. STM32 Cube defines IRQ Handlers inside the "xxxx_it.c" file, so possibly you dont have it. On the other hand necessary NVIC configurations are also done by Cube MX.
I suggest you to use Cube MX, then you you can find what is wrong here. In your code snippet there seem nothing wrong.
I'm using stwinkt1 board connected to encoder to timer 3.
I want to get a interrupt every time that the encoder count is 300, so the code in the interrupt is:
void TIM3_IRQHandler(void){
// do things here
//reset the encoder so ill start counting again
__HAL_TIM_SET_COUNTER(&encoderTimer, 0);
//clear the interrupt
HAL_TIM_IRQHandler(&encoderTimer);
}
but no matter how i define the encoder I get a interrupt each pulse- this is useless and consumes CPU without any reason.
this is how I define the encoder & start it:
void Init_Encoder_TIM(void){
TIM_Encoder_InitTypeDef Config = {0};
TIM_MasterConfigTypeDef MasterConfig = {0};
//define the encoder clock
encoderTimer.Instance = TIM_ENCODER;
encoderTimer.Init.Prescaler = 0;
encoderTimer.Init.CounterMode = TIM_COUNTERMODE_UP;
encoderTimer.Init.Period = 0xFFFF;
encoderTimer.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
encoderTimer.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
Config.EncoderMode = TIM_ENCODERMODE_TI1;
Config.IC1Polarity = TIM_ICPOLARITY_RISING;
Config.IC1Selection = TIM_ICSELECTION_DIRECTTI;
Config.IC1Prescaler = TIM_ICPSC_DIV1;
Config.IC1Filter = 0;
Config.IC2Polarity = TIM_ICPOLARITY_RISING;
Config.IC2Selection = TIM_ICSELECTION_DIRECTTI;
Config.IC2Prescaler = TIM_ICPSC_DIV1;
Config.IC2Filter = 0;
if (HAL_TIM_Encoder_Init(&encoderTimer, &Config) != HAL_OK)
{
Error_Handler();
}
MasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
MasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&encoderTimer, &MasterConfig) != HAL_OK)
{
Error_Handler();
}
HAL_TIM_Encoder_Start_IT(&encoderTimer, TIM_CHANNEL_ALL);
}
I tried to set the prescaler to 300 it didnt work.
I tried to set the Period to 300 it didnt work as well.
I tried to set the CCR3 manually to 300 and activate the interrupt channel and it didnt work as well.
To be clear- The interrupt works, I get interrupt but for each encoder pulse instead of every 300 pulses.
Hope you will know how to help me
Itay
I'm using one STM32F4 and I want to communicate with my LSM303 accelerometer. For that I'm using I2C, and just using I2C works fine but when I try to use DMA with it, it stops working.
When I use HAL_I2C_Master_Transmit_DMA it works and I got the IRQHandler and . But when after that I want to use HAL_I2C_Master_Receive_DMA it says that the State of the I2C is not ready...
I read that the I2C was kind of messed up with the STM32FX but I don't understand why it's working fine without DMA.
Also when it hits the callback I2C_DMAXferCplt for the Master_Transmit_DMA it says that the CurrentState of the I2C_HandleTypeDef is still equal to HAL_I2C_STATE_BUSY_TX and therefor it does not put the state back to READY. That why it does not receive anything when I call the Master_Receive_DMA.
Here's my I2C init :
void MX_I2C2_Init(void)
{
I2C_ST_INS.Instance = I2C2;
I2C_ST_INS.Init.ClockSpeed = 400000;
I2C_ST_INS.Init.DutyCycle = I2C_DUTYCYCLE_2;
I2C_ST_INS.Init.OwnAddress1 = 0;
I2C_ST_INS.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
I2C_ST_INS.Init.DualAddressMode = I2C_DUALADDRESS_DISABLED;
I2C_ST_INS.Init.OwnAddress2 = 0;
I2C_ST_INS.Init.GeneralCallMode = I2C_GENERALCALL_DISABLED;
I2C_ST_INS.Init.NoStretchMode = I2C_NOSTRETCH_DISABLED;
HAL_I2C_Init(&I2C_ST_INS);
}
void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(i2cHandle->Instance==I2C1)
{
//Not useful for this post
}
else if(i2cHandle->Instance==I2C2)
{
GPIO_InitStruct.Pin = MASTER_IMUB_I2C_SDA_Pin|MASTER_IMUB_I2C_SCL_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C2;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
__HAL_RCC_I2C2_CLK_ENABLE();
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
hdma_i2c2_rx.Instance = DMA1_Stream2;
hdma_i2c2_rx.Init.Channel = DMA_CHANNEL_7;
hdma_i2c2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_i2c2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_i2c2_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_i2c2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_i2c2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_i2c2_rx.Init.Mode = DMA_NORMAL;
hdma_i2c2_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
hdma_i2c2_rx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
hdma_i2c2_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
hdma_i2c2_rx.Init.MemBurst = DMA_MBURST_SINGLE;
hdma_i2c2_rx.Init.PeriphBurst = DMA_PBURST_SINGLE;
if (HAL_DMA_Init(&hdma_i2c2_rx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(i2cHandle,hdmarx,hdma_i2c2_rx);
HAL_NVIC_SetPriority(DMA1_Stream2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream2_IRQn);
hdma_i2c2_tx.Instance = DMA1_Stream7;
hdma_i2c2_tx.Init.Channel = DMA_CHANNEL_7;
hdma_i2c2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_i2c2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_i2c2_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_i2c2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_i2c2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_i2c2_tx.Init.Mode = DMA_NORMAL;
hdma_i2c2_tx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
hdma_i2c2_tx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
hdma_i2c2_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
hdma_i2c2_tx.Init.MemBurst = DMA_MBURST_SINGLE;
hdma_i2c2_tx.Init.PeriphBurst = DMA_PBURST_SINGLE;
if (HAL_DMA_Init(&hdma_i2c2_tx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(i2cHandle,hdmatx,hdma_i2c2_tx);
HAL_NVIC_SetPriority(DMA1_Stream7_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream7_IRQn);
}
}
Do you have any ideas why it does not work when I'm using DMA with I2C ?
Thanks,
Victor
It worked for me when I enabled the I2C_event interrupt on top of the DMA interrupt, see generated code and CubeMX config below
HAL_NVIC_SetPriority(I2C1_EV_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
CubeMX does not automatically check the I2C1 event global interrupt when selecting DMA, I think it should (STmicro please fix this) as I dont see how it can work without it.
I've had the same issue. I've solved it by lowering the frequency.
ST Errata document says you have to step down the I2C frequency to 88kHz to fix some other problem.
I know it doesn't explain why this error doesn't occur in blocking mode but happens with DMA, but I hope it helps.
I had been struggling with the same problem on STM32F407 and I2C1.
After searching for potential bugs in the program flow, I found out that the function HAL_I2C_Master_Transmit_DMA leads to following line:
dmaxferstatus = HAL_DMA_Start_IT(hi2c->hdmatx, (uint32_t)hi2c->pBuffPtr, (uint32_t)&hi2c->Instance->DR, hi2c->XferSize);
After the first transfer, this won't return HAL_OK, which is necessary for the transmission to continue.
So my solution was simply abort the previous DMA interrupt in the callback function which is called after the transmission has completed. The same can be implied with HAL_I2C_Master_Receive_DMA. To resolve the problem, I added the following callback functions in main.c:
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
if (hi2c->Instance==hi2c1.Instance)
{
HAL_DMA_Abort_IT(hi2c->hdmatx);
}
}
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
if (hi2c->Instance==hi2c1.Instance)
{
HAL_DMA_Abort_IT(hi2c->hdmarx);
}
}
Please consider this is only a workaround. If someone finds out, I would like to know more about the underlying reason for this bug.