STM32: I2S input not working when using DMA - c

I am trying to interface a 'STM32F401RET6 Nucleo-64' microcontroller with an Adafruit I2S microphone in a mono setup. To accomplish this task, I would like to have DMA enabled.
I used the Device Configuration Tool in STM32 Cube IDE to activate I2S3 using the following parameters:
I2S3
Full Duplex Master
Transmission mode: Mode Master Receive;
Communication standard: MSB First (Left Justified);
Data and Frame Format: 24 Bits Data on 32 Bits Frame;
Selected Audio Frequency: 48 kHz;
Clock Source: I2S PLL Clock;
Clock Polarity: Low;
Master clock output disabled.
DMA
SPI_RX3, DMA 1 Stream 2, Peripheral to Memory, High Priority;
FIFO, Threshold = Full, Data Width = Half Word, Burst Size = Single;
In the NVIC settings, interrupts are enabled for both DMA 1 Stream 2 and SPI3.
Next, the Code Generator Tool was used to automatically generate starting code. Some changes were made to this starting code:
Set GPIO_PULL_DOWN so that tri-state always reads in 0;
I already used an oscilloscope to plot the digital data waveform coming from the microphone. This seemed to be correct, i.e., sound triggered the microphone and this was visible in the most significant bits. This makes that the error is in reading in the data into the correct format, if I'm correct.
To perform mono measurements, the datasheet states that one should use a 100k Ohm resistor, which is present in my setup.
In my main.c program, I'm using the HAL function 'HAL_I2S_Receive_DMA' to try to fill my array of 500 samples.
main.c:
/* Includes ------------------------------------------------------------------*/
#include "main.h"
I2S_HandleTypeDef hi2s3;
DMA_HandleTypeDef hdma_spi3_rx;
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2S3_Init(void);
static void MX_DMA_Init(void);
int main(void)
{
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2S3_Init();
MX_DMA_Init();
/* Infinite loop */
HAL_StatusTypeDef retval; // return value
volatile int16_t data[500] = {0};
int16_t data_shifted[500];
while (1)
{
retval = HAL_I2S_Receive_DMA(&hi2s3, data, 500);
// for(short i=0; i<500; i++){
// data_shifted[i] = data[i] >> 14;
// }
HAL_Delay(1000);
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/* Configure the main internal regulator output voltage */
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);
/* Initializes the RCC Oscillators according to the specified parameters
in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 8;
RCC_OscInitStruct.PLL.PLLN = 84;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/* Initializes the CPU, AHB and APB buses clocks */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
static void MX_I2S3_Init(void)
{
hi2s3.Instance = SPI3;
hi2s3.Init.Mode = I2S_MODE_MASTER_RX;
hi2s3.Init.Standard = I2S_STANDARD_MSB;
hi2s3.Init.DataFormat = I2S_DATAFORMAT_24B;
hi2s3.Init.MCLKOutput = I2S_MCLKOUTPUT_DISABLE;
hi2s3.Init.AudioFreq = I2S_AUDIOFREQ_48K;
hi2s3.Init.CPOL = I2S_CPOL_LOW;
hi2s3.Init.ClockSource = I2S_CLOCK_PLL;
hi2s3.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_ENABLE;
if (HAL_I2S_Init(&hi2s3) != HAL_OK)
{
Error_Handler();
}
}
// Enable DMA controller clock
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Stream2_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream2_IRQn);
}
/*GPIO Initialization Function */
static void MX_GPIO_Init(void)
{
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
}
/* This function is executed in case of error occurrence. */
void Error_Handler(void)
{
/* Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
}
#ifdef USE_FULL_ASSERT
/**
Reports the name of the source file and the source line number
where the assert_param error has occurred.
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
}
#endif /* USE_FULL_ASSERT */
When I debug my code and put a breakpoint on line 34 in 'main.c', then the microcontroller memory does not update its values accordingly. All values stay equal to zero. Both DMA Status Flags are set to 1.
I guessing that the problem has something to do with timings, but I wasn't able to solve this problem until now.
Link to complete source code on GitHub
Link to HAL_I2S_Receive_DMA function on GitHub
Thanks in advance.

DMA must be initialized before ADC peripheral, but generated code performs initialization in wrong order. It is well known minor flaw in default configuration of current STM32CubeIDE. Try to change
MX_GPIO_Init();
MX_I2S3_Init();
MX_DMA_Init(); // wrong order
to
MX_GPIO_Init();
MX_DMA_Init(); // right order
MX_I2S3_Init();
To make permanent changes,
Open "Project Manager" (tab near Pinout / Clock Configuration)
Project Manager → Advanced Settings → Generated Function Calls
Use tiny arrow buttons near this list to move MX_DMA_Init() above
MX_I2S3_Init()
Save the project, generated code will have correct order

for my experience,
HAL_I2S_Receive_DMA() function is to configure the address
(if you learn the LL API, you need manually set source/dest address and length of data: [LL_DMA_SetDataLength()])
So, you can move it before While(1) loop.
if you want to read/process the buffer "data", you can use dma interrupt callback function, in HAL API is : HAL_I2S_RxHalfCpltCallback(), HAL_I2S_RxCpltCallback()
=====================================================================
update:
// init method and buff
...
xx_init()
volatile int16_t data[500] = {0};
int16_t data_shifted[500];
...
//
HAL_I2S_Receive_DMA(&hi2s3, &data[0], 250);
while(1){
if (FLAG_half){
FLAG_half=0;
// add your code: data shift [0:250]...
}
if (FLAG_comp){
FLAG_comp=0;
// add your code: data shift [250:500]...
}
} // end while
} end main
HAL_I2S_RxHalfCpltCallback(){
FLAG_half=1;
}
HAL_I2S_RxCpltCallback(){
FLAG_comp=1;
}

Related

STM32 Timer Interrupts causing Debug problems

I'm using STM32MP157A-DK1 Discovery kit with STM32MP157A MPU.
My problem is that when I terminate the debug and launch again my code stop in void HardFault_Handler(void); (in stm32mp1xx_it.c). The first debugging time works ok, the problem was in the second debug.
I tried removing HAL_TIM_Base_Start_IT(&htim14); and the problem stopped, but I need the timer. Also tried switching timers and the others also give me the same debugging problem.
I checked the hardware configuration (.ioc file) and it's all in default values except the timers that I'm using.
int main(void) {
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
if(IS_ENGINEERING_BOOT_MODE()){
/* Configure the system clock */
SystemClock_Config();
}
if(IS_ENGINEERING_BOOT_MODE()){
/* Configure the peripherals common clocks */
PeriphCommonClock_Config();
}else{
/* IPCC initialisation */
MX_IPCC_Init();
/* OpenAmp initialisation ---------------------------------*/
MX_OPENAMP_Init(RPMSG_REMOTE, NULL);
}
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_TIM14_Init();
/* USER CODE BEGIN 2 */
/*
* Create Virtual UART device
* defined by a rpmsg channel attached to the remote device
*/
log_info("Virtual UART0 OpenAMP-rpmsg channel creation\r\n");
if(VIRT_UART_Init(&huart0) != VIRT_UART_OK){
log_err("VIRT_UART_Init UART0 failed.\r\n");
Error_Handler();
}
log_info("Virtual UART1 OpenAMP-rpmsg channel creation\r\n");
if(VIRT_UART_Init(&huart1) != VIRT_UART_OK){
log_err("VIRT_UART_Init UART1 failed.\r\n");
Error_Handler();
}
//Need to register callback for message reception by channels
if(VIRT_UART_RegisterCallback(&huart0, VIRT_UART_RXCPLT_CB_ID, VIRT_UART0_RxCpltCallback) != VIRT_UART_OK){ Error_Handler();}
if(VIRT_UART_RegisterCallback(&huart1, VIRT_UART_RXCPLT_CB_ID, VIRT_UART1_RxCpltCallback) != VIRT_UART_OK){ Error_Handler();}
HAL_TIM_Base_Start_IT(&htim14); //Here is the trouble maker
while(1){
OPENAMP_check_for_message();
}
}
/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if(htim == &htim14){
//do something
}
}
void VIRT_UART0_RxCpltCallback(VIRT_UART_HandleTypeDef *huart) {
//log_info("Msg received on VIRTUAL UART0 channel: %s \n\r", (char*) huart->pRxBuffPtr);
}
void VIRT_UART1_RxCpltCallback(VIRT_UART_HandleTypeDef *huart) {
log_info("Msg received on VIRTUAL UART1 channel: %s \n\r", (char*) huart->pRxBuffPtr);
}
I'm using Windows 10, STM32CubeIDE (Version: 1.8.0, Build: 11526_20211125_0815 (UTC)), STM32CubeMX (Version: 6.4.0-RC4, Build: 20211122-2105 (UTC)).
I just put
TIM14->DIER &= ~(TIM_IT_UPDATE);
TIM14->CR1 &= ~(TIM_CR1_CEN);
before call timer initialization
MX_TIM14_Init();
The problem was in Production Mode the A7 core controls all M4's peripherical, so it only reset the interruption when I reset the core A7. This code I use turn off the interrupt to prevent a interrupt call before initialization. You must call it for every peripheric interrupt (check how to disable it, for timers just change the TIM number. Ex: TIM3, TIM17, ...)
Don't know if it's the best way, it's working until now though.
Final main() code:
int main(void) {
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
if(IS_ENGINEERING_BOOT_MODE()){
/* Configure the system clock */
SystemClock_Config();
}
if(IS_ENGINEERING_BOOT_MODE()){
/* Configure the peripherals common clocks */
PeriphCommonClock_Config();
}else{
/* IPCC initialisation */
MX_IPCC_Init();
/* OpenAmp initialisation ---------------------------------*/
MX_OPENAMP_Init(RPMSG_REMOTE, NULL);
}
/* USER CODE BEGIN SysInit */
TIM14->DIER &= ~(TIM_IT_UPDATE);
TIM14->CR1 &= ~(TIM_CR1_CEN);
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_TIM14_Init();
/* USER CODE BEGIN 2 */
/*
* Create Virtual UART device
* defined by a rpmsg channel attached to the remote device
*/
log_info("Virtual UART0 OpenAMP-rpmsg channel creation\r\n");
if(VIRT_UART_Init(&huart0) != VIRT_UART_OK){
log_err("VIRT_UART_Init UART0 failed.\r\n");
Error_Handler();
}
log_info("Virtual UART1 OpenAMP-rpmsg channel creation\r\n");
if(VIRT_UART_Init(&huart1) != VIRT_UART_OK){
log_err("VIRT_UART_Init UART1 failed.\r\n");
Error_Handler();
}
//Need to register callback for message reception by channels
if(VIRT_UART_RegisterCallback(&huart0, VIRT_UART_RXCPLT_CB_ID, VIRT_UART0_RxCpltCallback) != VIRT_UART_OK){ Error_Handler();}
if(VIRT_UART_RegisterCallback(&huart1, VIRT_UART_RXCPLT_CB_ID, VIRT_UART1_RxCpltCallback) != VIRT_UART_OK){ Error_Handler();}
HAL_TIM_Base_Start_IT(&htim14); //Here is the trouble maker
while(1){
OPENAMP_check_for_message();
}
}

Using DMA controller to transmit UART

I have been trying exhaustively to program my STM32F7xx microcontroller to use DMA to transmit to UART. Three things are going on and I cannot explain or understand why this is happening, and hope somebody can help me out with this issue.
In the main while loop, I am printing three interrupt status flags. These flags are set if the corresponding ISR has been called. I added this to check if the ISR was called without adding blocking statements in the ISRs. None of the interrupts, however, are called.
The DMA only transmits 1 sequence of 513 bytes. When I modify the while loop in my main to only contain HAL_UART_Transmit_DMA(&handleUart4, dmxBuffer, 513);, nothing changes, the function is only called/executed once.
In the while loop, I print the status of the ISR flags. After printing, the CPU stops/locks/shutdown/exits the while loop. At first, I thought I was congesting the AHB by using the UART to my terminal and the UART for the DMA controller. I disabled my terminal, and used LEDs, this didn't change anything.
Currently, the only running hypothesis I have is that my CPU somehow has interrupts disabled.
#include "stm32f7xx.h"
#include "mbed.h"
uint8_t dmxBuffer[513];
volatile bool irqA = false;
volatile bool irqB = false;
volatile bool irqC = false;
Serial pc(USBTX, USBRX, 115200);
UART_HandleTypeDef handleUart4;
DMA_HandleTypeDef handleDma;
void initialiseGPIO()
{
GPIO_InitTypeDef GPIO_InitStruct;
__GPIOA_CLK_ENABLE();
/**UART4 GPIO Configuration
PA0 ------> USART4_TX
*/
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF8_UART4;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
void initialiseDMAController()
{
/* DMA controller clock enable */
__DMA1_CLK_ENABLE();
/* Peripheral DMA init*/
handleDma.Instance = DMA1_Stream4;
handleDma.Init.Channel = DMA_CHANNEL_4;
handleDma.Init.Direction = DMA_MEMORY_TO_PERIPH;
handleDma.Init.PeriphInc = DMA_PINC_DISABLE;
handleDma.Init.MemInc = DMA_MINC_ENABLE;
handleDma.Init.PeriphDataAlignment = DMA_MDATAALIGN_BYTE;
handleDma.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
handleDma.Init.Mode = DMA_NORMAL;
handleDma.Init.Priority = DMA_PRIORITY_MEDIUM;
handleDma.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
HAL_DMA_Init(&handleDma);
//Define
__HAL_LINKDMA(&handleUart4,hdmatx,handleDma);
/* DMA interrupt init */
HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);
}
void initialiseUart()
{
__UART4_CLK_ENABLE();
handleUart4.Instance = UART4;
handleUart4.Init.BaudRate = 250000;
handleUart4.Init.WordLength = UART_WORDLENGTH_8B;
handleUart4.Init.StopBits = UART_STOPBITS_2;
handleUart4.Init.Parity = UART_PARITY_NONE;
handleUart4.Init.Mode = UART_MODE_TX;
handleUart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
handleUart4.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&handleUart4);
/* Peripheral interrupt init*/
HAL_NVIC_SetPriority(UART4_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(UART4_IRQn);
}
/* This function handles DMA1 stream4 global interrupt. */
void DMA1_Stream4_IRQHandler(void)
{
irqA = true;
HAL_DMA_IRQHandler(&handleDma);
}
/* This function handles the UART4 interups */
void UART4_IRQHandler(void)
{
irqB = true;
HAL_UART_IRQHandler(&handleUart4);
}
//HAL_UART_TxCpltCallback
/* This callback function is called when the DMA successfully transmits all scheduled bytes. */
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
irqC = true;
}
int main(void)
{
/* Reset of all peripherals */
HAL_Init();
//Initialise peripherals
initialiseGPIO();
initialiseDMAController();
initialiseUart();
//Fill buffer with test data
for (int x = 0; x < 100; x++)
{
dmxBuffer[x] = x;
}
//Now instruct the UART peripheral to transmit 513 bytes using the DMA controller.
HAL_UART_Transmit_DMA(&handleUart4, dmxBuffer, 513);
while(1)
{
pc.printf("irqA: %d - irqB: %d - irqC: %d\r\n", irqA, irqB, irqC);
wait_ms(100); //Wait to see if any of the interupt handlers / callback functions are called
//Check if all bytes are sent, if so, retransmit
if (irqC)
{
irqC = false;
HAL_UART_Transmit_DMA(&handleUart4, dmxBuffer, 513);
}
}
}
Check the interrupt vector table
Verify that the vector table does indeed contain a pointer to your handler function, not to some generic placeholder with an infinite loop (that makes the program hang).
Search for the name of the interrupt handler function in the entire source code. Is there any other object or #define that could interfere with the function definition, or the vector table entry?
Change the name of the handler, both the function definition and the vector table entry. Does it still compile? When not, does adding extern "C" to the function prototype help?
Look up the address of the handler in the .map file, and the offset entry for the interrupt in the vector table provided in the Reference Manual (Nested vectored interrupt controller (NVIC) / Interrupt and exception vectors). Check the contents of the compiled program binary file at the given offset. Does it match the address found in the .map file + 1?
Check the value at NVIC->VTOR plus the offset while running the program. It should be the same as the one found in the binary. If not, see that the VTOR register is set to the beginning of the right vector table.

STM32 Timer Interrupts

I am trying to use timer peripheral in STM32L073 but i have problems.
I've generated setup code in STM32Cube and using only functions that HAL API provides. Problem is that timer interrupt occurs only once and it should occur always then it overflows. I don't know if timer is even running or my setup is wrong (which I believe it shouldn't be since I am using Cube generated code). Anyone can help?
This is called in main before while loop
void MX_TIM7_Init(void){
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
htim7.Instance = TIM7;
htim7.Init.Prescaler = 00;
htim7.Init.CounterMode = TIM_COUNTERMODE_UP;
htim7.Init.Period = 00;
if (HAL_TIM_Base_Init(&htim7) != HAL_OK) {
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim7, &sMasterConfig) != HAL_OK){
_Error_Handler(__FILE__, __LINE__);
}
}
BaseMspInit Method
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle){
if(tim_baseHandle->Instance==TIM7){
/* USER CODE BEGIN TIM7_MspInit 0 */
/* USER CODE END TIM7_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_TIM7_CLK_ENABLE();
/* TIM7 interrupt Init */
HAL_NVIC_SetPriority(TIM7_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM7_IRQn);
/* USER CODE BEGIN TIM7_MspInit 1 */
/* USER CODE END TIM7_MspInit 1 */
}
}
Then in main I call this
HAL_TIM_Base_Start_IT(&htim7);
which is doing this
HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim){
/* Check the parameters */
assert_param(IS_TIM_INSTANCE(htim->Instance));
/* Enable the TIM Update interrupt */
__HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE);
/* Enable the Peripheral */
__HAL_TIM_ENABLE(htim);
/* Return function status */
return HAL_OK;
}
and my Interrupt handler calls PeriodElapsedCallback
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
HAL_GPIO_TogglePin(led_GPIO_Port, led_Pin);
}
It compiles there are no errors no warning, in debugger I see this interrupt is triggered only once and then never. Looking forward for answers
P.S. using SEGGER Embedded Studio
Your overflow happens every clock as you did not set ARR
Firstly you need to set these parameters to your destination period
Prescaler for the clock source (check the which bus):
htim7.Init.Prescaler = 00;
How much timer needs to count for ISR occurs:
htim7.Init.Period = 00;

STM32F429 External Interrupt Edge

I am using the STM32F429I-Discovery board, the board has a pushbutton connected to PA0, which in turn is connected to External Interrupt Line 0 (EXTI0).
Using the HAL Libraries, I can toggle a LED either on the Falling Edge or on the Rising edge using external interrupts. Eg, the LED either changes state as soon as I press the pushbutton, or only once I release the pushbutton.
What I want to do is to Interrupt on the Rising edge, start a timer, and then interrupt on the falling edge again, to stop the timer. I have got no idea how to achieve this?
There is also an option to trigger on both rising and falling edges. I do not know if there should only be one interrupt, and I then figure out if it was a rising or falling edge (probably by accessing the registers directly), or should there be two configured interrupts - One as Rising Edge and one as Falling Edge?
Below are the external interrupt code; firstly to set a GPIO up as an external interrupt, then to detect the interrupt and then to handle the interrupt (callback).
static void EXTILine0_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable GPIOA clock */
__HAL_RCC_GPIOA_CLK_ENABLE();
/* Configure PA0 pin as input floating */
GPIO_InitStructure.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Pin = GPIO_PIN_0;
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Enable and set EXTI Line0 Interrupt to the lowest priority */
HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}
/* Clears the interrupt after calling this I think */
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(KEY_BUTTON_PIN);
}
/**
* #brief EXTI line detection callbacks
* #param GPIO_Pin: Specifies the pins connected EXTI line
* #retval None
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == KEY_BUTTON_PIN)
{
/* Toggle LED3 */
BSP_LED_Toggle(LED3);
}
}
Can someone please point out how I can achieve this?
What you're looking for is called "input capture" and can be achieved with a timer directly without the need for external interrupts. On the STM32F429 PA0 is internally mapped to Timer 2 Channel 1.
The sConfigIC structure is responsible for handling input capture related configuration stuff.
The initialization looks something like this:
/* TIM2 init function */
void MX_TIM2_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
TIM_IC_InitTypeDef sConfigIC;
/* Peripheral clock enable */
__TIM2_CLK_ENABLE();
/* Peripheral interrupt init*/
HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
/**TIM2 GPIO Configuration
PA0/WKUP ------> TIM2_CH1
*/
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
htim2.Instance = TIM2;
htim2.Init.Prescaler = 0;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 0xFFFFFFFF;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htim2);
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig);
HAL_TIM_IC_Init(&htim2);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig);
/* Input capture stuff HERE
Change polarity as needed */
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1);
}
And further the interrupt function:
/* IRQ */
void TIM2_IRQHandler(void)
{
// Check for interrupt flags here
}
Within the interrupt you've got to check for the CC1IF flag. The timer value gets stored in the capture and compare register called CCR1.
/edit
Don't forget to start the timer and input capture channel with:
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
This enables the timer, the according channel for input capture and the interrupt.
First, you have to setup a timer, e.g.
TIM_HandleTypeDef htim1;
void TIM1_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
htim1.Instance = TIM1;
htim1.Init.Prescaler = 71;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 65535;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
HAL_TIM_Base_Init(&htim1);
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig);
}
Code is taken from an STM32F1, so maybe you have to adapt a little, just have a look in the HAL handbook.
The interrupt for rising and falling edge is the same, so you have to check the state of the pin in your interrupt handler.
To start the timer
HAL_TIM_Base_Start(&htim1);
and to stop
HAL_TIM_Base_Stop(&htim1);
the counter value is stored in
TIM1->CNT
A bit of background: Using the STM32F429I the code below is to display how long you press the blue user button for, the count is given as milliseconds. The PCB has a hardware debounce circuit so the fastest response I was able to get is around 50ms.
As stated earlier PA0 is connected to EXTILine0.
I set the PA0 line up to interrupt on both rising and falling edges.
static void EXTILine0_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable GPIOA clock */
__HAL_RCC_GPIOA_CLK_ENABLE();
/* Configure PA0 pin as input floating */
GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING_FALLING;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Pin = GPIO_PIN_0;
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Enable and set EXTI Line0 Interrupt to the lowest priority */
HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}
When a interrupt then occurs, I read the amount of counts that are currently stored in HAL_GetTick(), this function clocks every 1 millisecond. I also read whether the pin is high or low to determine if the interrupt was on a falling or on a rising edge.
uint16_t beginCount;
uint16_t stopCount;
void EXTI0_IRQHandler(void)
{
uint16_t var;
var = HAL_GetTick();
uint16_t calcCount = 0;
unsigned char buffer[10];
BSP_LCD_Clear(LCD_COLOR_WHITE);
// The Pin Goes high when the pushbutton is pressed.
if (HAL_GPIO_ReadPin(GPIOA, KEY_BUTTON_PIN) == 0x01)
{
beginCount = 0;
beginCount = var;
BSP_LCD_SetTextColor(LCD_COLOR_GREEN);
BSP_LCD_DisplayStringAtLine(6, "Rising Edge" );
}
else
{
stopCount = 0;
stopCount = var;
BSP_LCD_SetTextColor(LCD_COLOR_RED);
BSP_LCD_DisplayStringAtLine(7, (uint8_t*)"Falling Edge");
// Calculate Counts and covert to seconds - What if the counter overflows?
calcCount = stopCount - beginCount;
sprintf(buffer, "%d", calcCount); // Convert the integer to string and put it in variable buffer
BSP_LCD_DisplayStringAtLine(8, (&buffer) ); // Display the value stored at buffer's location
}
HAL_GPIO_EXTI_IRQHandler(KEY_BUTTON_PIN);
}
Lastly the interrupt callback triggers and just toggles the LED on the board.
The counter can only go to something like 65 seconds, after that it would overflow and my 'calculated' time would be incorrect. This methods works OK for what I intend to do with it. I want to measure 20-300 milliseconds with a accuracy of a few milliseconds. I still have to put in a catch for if the timer overflows between two measurements.
Anything fundamentally wrong with this approach? I am not very experienced with C, and not at all with STM32.

Communication betwen STM32 microcontroller and SIM20 module with USART device

I'm trying to make a STM32f1 microcontroller communicate with a SIM20 module.
I hope that all hardware setting were well made.
Speaking of the software, my C program is composed of the following components:
RCC configuration
GPIO configuration
USART configuration
Send of the string "AT+SRDFIRM"
Storing the received buffer in a file "test.txt"
Turn on the LED3
However nothing is received from the SIM20. Nothing was stored in the file and LED3 doesn't turn on.
My C code is the following:
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "stm32_eval.h"
#include <stdio.h>
#include <stdlib.h>
/* Private typedef -----------------------------------------------------------*/
typedef enum { FAILED = 0, PASSED = !FAILED} TestStatus;
/* Private define ------------------------------------------------------------*/
#define USARTy USART1
#define USARTy_GPIO GPIOA /* PORT name*/
#define USARTy_CLK RCC_APB2Periph_USART1
#define USARTy_GPIO_CLK RCC_APB2Periph_GPIOA
#define USARTy_RxPin GPIO_Pin_10/* pin Rx name*/
#define USARTy_TxPin GPIO_Pin_9 /* pin Tx name*/
#define USARTz USART2
#define USARTz_GPIO GPIOA/* PORT name*/
#define USARTz_CLK RCC_APB1Periph_USART2
#define USARTz_GPIO_CLK RCC_APB2Periph_GPIOA
#define USARTz_RxPin GPIO_Pin_3/* pin Rx name*/
#define USARTz_TxPin GPIO_Pin_2/* pin Tx name*/
#define TxBufferSize (countof(TxBuffer))
/* Private macro -------------------------------------------------------------*/
#define countof(a) (sizeof(a) / sizeof(*(a)))
/* Private variables ---------------------------------------------------------*/
USART_InitTypeDef USART_InitStructure;
uint8_t TxBuffer[] = "AT+SRDFIRM";
uint8_t RxBuffer[TxBufferSize];
__IO uint8_t TxConteur = 0, RxConteur = 0;
uint8_t Bin[16];
/* Private function prototypes -----------------------------------------------*/
void RCC_Configuration(void);
void GPIO_Configuration(void);
void Delay(__IO uint32_t);
TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength);
__IO uint8_t index = 0;
volatile TestStatus TransferStatus = FAILED;
GPIO_InitTypeDef GPIO_InitStructure;
int main(void)
{
STM_EVAL_LEDInit(LED1);
STM_EVAL_LEDInit(LED2);
STM_EVAL_LEDInit(LED3);
STM_EVAL_LEDInit(LED4);
int i;
/*TxBuffer[0] = 'B';
RxBuffer[0] ='\0';*/
/* System Clocks Configuration */
RCC_Configuration();
/* Configure the GPIO ports */
GPIO_Configuration();
USART_InitStructure.USART_BaudRate = 115200; // configuration vitesse
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // configuration longueur mot
USART_InitStructure.USART_StopBits = USART_StopBits_1; // bit de stop
USART_InitStructure.USART_Parity = USART_Parity_No; // bit de parite
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // hardware control
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // half duplex
/* Configure USARTy */
USART_Init(USART1,&USART_InitStructure);
/* Enable the USARTy */
USART_Cmd(USART1,ENABLE);
uint16_t reciv;
/*********************************************************/
FILE* fichier = NULL;
fichier = fopen("test.txt", "w");
while(TxConteur < TxBufferSize)
{
/* Send one byte from USARTy to USARTz */
USART_SendData(USARTy, TxBuffer[TxConteur++]);
}
/* Loop until USARTy DR register is empty */
while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
while(TxConteur < TxBufferSize)
{
RxBuffer[RxConteur] = USART_ReceiveData(USARTy) & 0xFF;
RxConteur++;
}
fprintf(fichier,"%s","RxBuffer");
fclose(fichier); // On ferme le fichier qui a été ouvert
TransferStatus = Buffercmp(TxBuffer, RxBuffer, TxBufferSize);
STM_EVAL_LEDOn(LED3);
while (1)
{
}
}
void RCC_Configuration(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC , ENABLE);
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure1,GPIO_InitStructure2;
/* Configure USARTy Rx as input floating */
GPIO_InitStructure1.GPIO_Pin =GPIO_Pin_10;
GPIO_InitStructure1.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure1.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure1);
/* Configure USARTy Tx as alternate function push-pull */
GPIO_InitStructure2.GPIO_Pin =GPIO_Pin_9;
GPIO_InitStructure2.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure2.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure2);
}
TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength)
{
while(BufferLength--)
{
if(*pBuffer1 != *pBuffer2)
{
return FAILED;
}
pBuffer1++;
pBuffer2++;
}
return PASSED;
}
#H2CO3 : this is the part of the program which contain the problem:
while(TxConteur < TxBufferSize-1)
{
/* Send one byte from USARTy to USARTz */
USART_SendData(USARTy, TxBuffer[TxConteur++]);
while(USART_GetFlagStatus(USART1, USART_FLAG_IDLE) == RESET);
}
RxConteur=0;
/* Store the received byte in RxBuffer */
while(RxConteur < TxBufferSize-1)
{
RxBuffer[RxConteur] = USART_ReceiveData(USARTy) & 0xFF;
RxConteur++;
}
Some things to look at:
Is the UART in reset?
I don't recall off-hand if the STM32 leaves the UARTs in reset by default. You enable the clock, but do not explicitly take it out of reset. If it is still in reset, then when you spin waiting for the RXNE flag to set, it may always read as reset. This would stop execution and you wouldn't reach the LED enable.
Do USART_SendData and USART_ReceiveData check the status of the data register?
If these functions don't check the state of the data register, then your transmit is likely not coming out properly. It takes 80 microseconds to transmit a character at 115200. The first write to the DR would be loaded into the shift register quickly, and the second write to the DR would be held, but unless the DR status is checked in USART_SendData further attempts to send would stomp on the previous bytes loaded. The end result transmitted would likely come out on the serial line as AM.
Similarly, USART_ReceiveData would likely fill up the receive buffer with repeats of the same byte until the next byte comes in (though the STM32 may clear the DR after the value is read).
Files. What compiler and runtime are you using?
Since this is your main() function, we can see what is being initialized at startup. I don't see any initialization of a filesystem. It could be happening before main, depending on the runtime. What tool are you using to build this and does it support file access? I know IAR's runtime supports standard file calls, but by default they will return failure unless you implement low level functions. You aren't checking that the file opened successfully, and the write with fprintf may be crashing.
Ensure that your runtime supports file access and does something meaningful with the calls, and add error checking to your file calls.

Resources