I am using a STM32F103 chip and I am trying to configure and use one of the timers. I have used STM32CubeMX to generation code which initializes Timer 2. I start the timer by calling HAL_TIM_Base_Start. Then, in a loop, I print out the current timer value via a call to htim2.Instance->CNT, or alternately by calling the macro __HAL_TIM_GetCounter (which I believe just returns the same value). However, no matter what I do, the count value shows up as zero. I have also tried calling __TIM2_CLK_ENABLE() at the beginning, but it makes no difference.
I have searched for a solution and have found a couple of questions about this issue, but have not found the solution.
Does anyone know what I am doing wrong?
Thanks.
Here is the routine that initializes the timer. This code was generated by STM32CubeMX and I have not modified it:
/* TIM2 init function */
static void MX_TIM2_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
htim2.Instance = TIM2;
htim2.Init.Prescaler = 0;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 0;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
Then in the main I attempt to start the timer and attempt to print out it's value. This is the code that I use to do that:
__TIM2_CLK_ENABLE();;
HAL_TIM_Base_Start(&htim2);
while (true)
{
Serial.println((long) __HAL_TIM_GetCounter(&htim2));
delay(100);
}
The 'Serial' class is a class that I wrote which communicates with my PC via a USB serial port.
Try it without HAL, it's not complicated.
void start_TIM2() {
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
TIM2->CR1 |= TIM_CR1_EN;
}
uint16_t read_TIM2() {
return TIM2->CNT;
}
Setting the value of
htim2.Init.Period = 0;
zero initializes the auto reload register (ARR) not a very handy default choice of cubeMX, you probably want an ARR value of 0xFFFFFFFF
The accepted answer relies on the ARR value after reset of 0xFFFFFFFF without mentioning it, more luck than wisdom if you ask me
The ARR (htim2.Init.Period) shouldn't be 0. It will try to count uptil...0! Put some 16 bit integer there. Good Luck!
I will show you some formulas to keep in mind: (refer: AN4776)
. Frequency = Fclk(timer) / (prescaler + 1)
For example, you chose prescaler = 0, it implies that your timer finishes counting [0: count) in a period of 1 / 80MHz, in case the SystemClock is configured like this (you should check the value of the clock and the prescaler to determine the Update frequency).
. #1_count(s) = 1 / Frequency
. period(s) = #1_count * (counter + 1)
--> Let's see it with an example:
SystemClock = 72MHz, requires a 1us resolution timer and a 1ms period.
. for a 1us of resolution -> Frequency = 1Mhz
'1MHz = 72MHz / (prescaler + 1)' -> prescaler = 71
. so that the timer interrupts every one millisecond we must calculate how much it should count:
'1ms = 1us * (counter + 1)' -> count = 999
with these values we configure the following:
htim2.Init.Prescaler = 71;
htim2.Init.Period = 999;
where the timer will count from 0: 999 in steps of 1uS.
Your code has two problems, the timer frequency is too high and it only counts from 0: 1, so you will never be able to see the increase in counts.
Related
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;
}
For my master thesis I am trying to receive data over DCMI with the STM32H742. As an image sensor I am using the NOIV2SN1300A. It sends data over 10 data lines, this data differs (so not always 0xA603A603). To reduce data size I have activated binning and subsampling.
I am using DMA to transfer the data received by DCMI into the internal SRAM at address 0x24000000. After a few telegrams (of 0xA603A603) the uC goes into hard fault and infinite loop. I am not sure why this error occurs. Here is my config for DMA and DCMI:
/* DCMI DMA Init */
/* DCMI Init */
hdma_dcmi.Instance = DMA1_Stream0;
hdma_dcmi.Init.Request = DMA_REQUEST_DCMI;
hdma_dcmi.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_dcmi.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_dcmi.Init.MemInc = DMA_MINC_ENABLE;
hdma_dcmi.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;//WORD;
hdma_dcmi.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_dcmi.Init.Mode = DMA_DOUBLE_BUFFER_M0;
hdma_dcmi.Init.Priority = DMA_PRIORITY_VERY_HIGH;
hdma_dcmi.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
hdma_dcmi.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
hdma_dcmi.Init.MemBurst = DMA_MBURST_SINGLE;
//hdma_dcmi.Init.PeriphBurst = DMA_PBURST_SINGLE;
if (HAL_DMA_Init(&hdma_dcmi) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(hdcmi,DMA_Handle,hdma_dcmi);
HAL_NVIC_SetPriority(DCMI_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DCMI_IRQn);
HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
hdcmi.Instance = DCMI;
hdcmi.Init.SynchroMode = DCMI_SYNCHRO_HARDWARE;
hdcmi.Init.PCKPolarity = DCMI_PCKPOLARITY_FALLING; //Todo:???
hdcmi.Init.VSPolarity = DCMI_VSPOLARITY_HIGH;
hdcmi.Init.HSPolarity = DCMI_HSPOLARITY_HIGH;
hdcmi.Init.CaptureRate = DCMI_CR_ALL_FRAME;
hdcmi.Init.ExtendedDataMode = DCMI_EXTEND_DATA_10B;
hdcmi.Init.JPEGMode = DCMI_JPEG_DISABLE;
hdcmi.Init.ByteSelectMode = DCMI_BSM_OTHER;//DCMI_BSM_ALL;
hdcmi.Init.ByteSelectStart = DCMI_OEBS_ODD;
hdcmi.Init.LineSelectMode = DCMI_LSM_ALL;
hdcmi.Init.LineSelectStart = DCMI_OELS_ODD;
if (HAL_DCMI_Init(&hdcmi) != HAL_OK)
{
Error_Handler();
}
uint32_t DataSizeImageSensor = 0xB160;
DMA_BUFFER uint32_t AddressFrameBuffer[0xB160];
HAL_DCMI_Start_DMA(&hdcmi, DCMI_MODE_SNAPSHOT, (uint32_t)AddressFrameBuffer, DataSizeImageSensor);
void MPU_Config (void)
{
MPU_Region_InitTypeDef MPU_InitStruct;
/* Disable the MPU */
HAL_MPU_Disable();
/* Configure the MPU attributes for SDRAM */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x24000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; //Cache is incoherent with CPU for DMA usage
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* Enable the MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
Any idea why this error occurs? Like I said, I receive data, I am sure the sensor sends viable data (so something else then 0xA603A603), but I get only the same data by DCMI before hard fault. At this current setup I transfer 64 telegrams, then the hard fault occurs. Furthermore I disabled the cache for the SRAM1, as there are problems regarding cache/DMA/CPU coherence. I am not quite sure how to proceed, as it looks to me like it is a setup error. Maybe an overrun by DCMI?
Note: The code above doesn't actually occur in this order. That are just the relevant parts (at least from what I expect to be relevant).
I found other posts about this problem, where the error was a slow system clock. As my system clock is 475 MHz and the HCLK3 Clock (which is used by DCMI for sampling) is 237.5 MHz I can rule out a Clock problem. The datasheet says the HCLK Clock has to be 2.5 times faster then DCMI Clock (which is set by the image sensor with 62 Mhz).
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;
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.
I'm currently working on an embedded system which is meant to output a specific pulse sequence at equidistant timings. Therefore, I use the STM32 - DISCO board with the FreeRTOS kernel, as a first test.
I configured TIM3 as Master triggering TIM1. TIM1 is triggered with a frequency of 1Hz or every second. TIM3 then generates a pulse sequence on it's output.
I configured the TIM3 output to PB4, and TIM1 to PA9. This configuration works as expected, but now I wanted to change the configuration on the fly with a structure that stores my settings and can be passed as pointer to a function that configures both timers.
As a first step, I generated a data structure and initialized it in my timer function to configure TIM3.
PG_ERR TIM_Master_Init(void){
PG_HandleTypeDef hPG_timer;
hPG_timer.PLS.Prescaler = 139;
hPG_timer.PLS.Period = 60000;
hPG_timer.PLS.DutyCycle = 30000;
hPG_timer.PLS.RepetitionCounter = 5;
hPG_timer.PLS.PercentChange = 0;
/* Timer3 handler declaration: Master */
TIM_HandleTypeDef TimMasterHandle;
/* Master configuration structure */
TIM_MasterConfigTypeDef sMasterConfig;
/* Output compare structure */
TIM_OC_InitTypeDef sOCConfig;
__TIM3_CLK_ENABLE();
PG_ERR xPGERR = PG_ERR_NONE;
/* Compute the prescaler value to have TIM3 counter clock equal to 60 KHz */
/* Set TIMx instance */
TimMasterHandle.Instance = MASTER_TIM;
/* Master configuration: TIM3 */
TimMasterHandle.Init.Period = 60000 - 1;
TimMasterHandle.Init.Prescaler = 1399;
TimMasterHandle.Init.ClockDivision = 0;
TimMasterHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
if (HAL_TIM_PWM_Init(&TimMasterHandle) != HAL_OK){
xPGERR = PG_ERR_TIM;
}
/* Configure the PWM_channel_1 */
sOCConfig.OCMode = TIM_OCMODE_PWM1;
sOCConfig.OCPolarity = TIM_OCPOLARITY_HIGH;
sOCConfig.OCFastMode = TIM_OCFAST_ENABLE;
sOCConfig.Pulse = 30000;
if (HAL_TIM_PWM_ConfigChannel(&TimMasterHandle, &sOCConfig, TIM_CHANNEL_1) != HAL_OK){
xPGERR = PG_ERR_TIM;
}
/* Configure TIM3 as master & use the update event as Trigger Output (TRGO) */
sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC1REF;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
if(HAL_TIMEx_MasterConfigSynchronization(&TimMasterHandle, &sMasterConfig) != HAL_OK){
xPGERR = PG_ERR_TIM;
}
/* Start PWM Timer3*/
if(HAL_TIM_PWM_Start(&TimMasterHandle, TIM_CHANNEL_1) != HAL_OK){
xPGERR = PG_ERR_TIM;
}
if(HAL_TIM_Base_Start_IT(&TimMasterHandle) != HAL_OK){
xPGERR = PG_ERR_TIM;
}
return xPGERR;
}
1) With this configuration i get some really odd behaviour. After including the data structure:
PG_HandleTypeDef hPG_timer;
hPG_timer.PLS.Prescaler = 139;
hPG_timer.PLS.Period = 60000;
hPG_timer.PLS.DutyCycle = 30000;
hPG_timer.PLS.RepetitionCounter = 5;
hPG_timer.PLS.PercentChange = 0;
After that code snipped, I don't get any output on PIN PB4 (Master - TIM3) which should still be toggled at 1Hz.
2) To get even more confusing, when I substitute the code block with a handle:
PG_HandleTypeDef hPG_timer;
PG_HandleTypeDef *hPG_timer;
hPG_timer = &hPG_timer_config;
hPG_timer.PLS.Prescaler = 139;
hPG_timer.PLS.Period = 60000;
hPG_timer.PLS.DutyCycle = 30000;
hPG_timer.PLS.RepetitionCounter = 5;
hPG_timer.PLS.PercentChange = 0;
Now I can see the output on PB4 (Master - TIM3) with 1Hz but the output polarity of PA9 (Slave - TIM1) is reversed.
I tried to investigate the problem, I focused on the stack/heap of the FreeRTOS. I tested the system with large heap/stack = (uint32_t) 65535; I couldn't observe any changes in the behaviour.
I hope somebody came across a similar problem or has an idea how to resolve this. I'm thankful for any input in this, I'm at the end of my knowledge unfortunately.
Edit:
I spend some more time with the problem, and I think I can be more specific. In case of the pointer use, the TimMasterHandle is locked right after the initialization. If I unlock the handle TimMasterHandle.lock = HAL_UNLOCK; all works well, but that is just masking the problem and i would like to know where this is coming from.
It some how looks still like an heap or stack problem. Is there any way, I can check for heap or stack overflow. I am using Keil uVision 5.10.
Thank you for your time and help,
eimer
So I contacted the E-Mail support of ST.
The rather simple answer I got was -
Initialize your timer handles e.g.:
PG_HandleTypeDef hPG_timer = {0};
Seems to resolve the odd behaviour.
I hope somebody finds this usefull.
enjoy your day
eimer