Minimizing Interrupt Time, Maximizing Compiler Processing Speed, Reaching STM32F3 Operator Speed - c

I am currently working on STM32F3 Discovery Board. I wrote a basic program in C to see if it works at 72 MHz which is about 14 ns in time. Hopefully it worked at this frequency, but when I want to toggle just one random pin on the device, turning it ON or OFF takes 660 ns in an infinite while loop. Then I checked with an oscilloscope, how much each command takes. Values might not be exact, Here is what I found;
a while loop(only for checking) = 120 ns, a "for" loop(only for checking) = 160 ns
One turn of a while loop = 280 ns, one turn of a for loop = 320 ns, which means each turn takes 160 ns.
asm("NOP") = 14 ns(which must be equal to operator frequency(72 MHz, as expected), an empty function call = 225 ns
I don't think these values are same for other compilers, or other computers, but I am not even close to 72 MHz. Minimum interrupt I got was 2 microsecond. Is there any way to minimize these values? Is there anyway to increase the gcc compiler speed?
#include "stm32f30x.h"
#include <common.h>
void InitializeTimer(void) ;
void EnableTimerInterrupt(void);
void TIM2_IRQHandler(void);
GPIO_InitTypeDef configuration;
void dly(int dly_nmbr);
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//NVIC_InitTypeDef NVIC_InitStructure;
RCC_ClocksTypeDef RCC_ClockFreq;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
/* Configure PA10 in output pushpull mode */
/*****I called init_pin function from common.c because it was easier
but I guess it takes some time too*********/
init_pin(GPIOA,GPIO_Pin_10,GPIO_Mode_OUT,GPIO_Speed_50MHz,GPIO_OType_PP,GPIO_PuPd_NOPULL);
/****I tried usual way to configure the pins too****/
//configuration.GPIO_Pin = GPIO_Pin_10;
//configuration.GPIO_Mode = GPIO_Mode_OUT;
//configuration.GPIO_OType = GPIO_OType_PP;
//configuration.GPIO_Speed = GPIO_Speed_50MHz;
//configuration.GPIO_PuPd = GPIO_PuPd_NOPULL;
//GPIO_Init(GPIOA, &configuration);
//InitializeTimer();
/*** This function fills the RCC_ClockFreq structure with the current
frequencies of different on chip clocks (for debug purpose) **************/
RCC_GetClocksFreq(&RCC_ClockFreq);
/* Enable Clock Security System(CSS): this will generate an NMI exception
when HSE clock fails *****************************************************/
RCC_ClockSecuritySystemCmd(ENABLE);
/* Enable and configure RCC global IRQ channel, will be used to manage HSE ready
and PLL ready interrupts.
These interrupts are enabled in stm32f0xx_it.c file **********************/
/* I disabled the interrupts for the purpose of maximizing the program speed*/
// NVIC_InitStructure.NVIC_IRQChannel = RCC_IRQn;
// NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
// NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
// NVIC_Init(&NVIC_InitStructure);
/************* Output HSE clock on MCO1 pin(PA8) ************/
/****************** Enable the GPIOA Clock ******************/
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
/* MCO pin configuration: PA8 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Output System Clock on MCO pin */
RCC_MCOConfig(RCC_MCOSource_SYSCLK);
/************** This is the toggling part. I tried each command here ***************/
while(1)
{
// volatile int i = 0;
// dly(0);
/*I called an empty function called dly before and after the toggle
to see function call time*/
GPIO_Toggle(GPIOA,GPIO_Pin_10);
// dly(0);
// while(i<=0)
// {
// i++;
// }
// for(i=0;i<=0;i++);
}
}
/*Empty Function*/
void dly(int dly_nmbr)
{
}
/************** Interrupt Functions **************/
// void TIM2_IRQHandler()
// {
// if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
// {
// TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
// }
// }
// void InitializeTimer()
// {
// RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//
// TIM_TimeBaseInitTypeDef timerInitStructure;
// timerInitStructure.TIM_Prescaler = 0;
// timerInitStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned2;
// timerInitStructure.TIM_Period = 2;
// timerInitStructure.TIM_ClockDivision = 0x0000;
// timerInitStructure.TIM_RepetitionCounter = 100;
// TIM_TimeBaseInit(TIM2, &timerInitStructure);
//
// TIM_Cmd(TIM2, ENABLE);
// TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
// EnableTimerInterrupt();
// }
// void EnableTimerInterrupt()
// {
// NVIC_InitTypeDef nvicStructure;
// nvicStructure.NVIC_IRQChannel = TIM2_IRQn;
// nvicStructure.NVIC_IRQChannelCmd = ENABLE;
// // TIM2->ARR = 1;
// // TIM2->PSC = 1;
// NVIC_Init(&nvicStructure);
//
// }
/****************** EOF ********************/

Related

There is a problem with TIM2_CH1 on stm32f103c8t6

I capture input PWM signal from AURORA9x using 3 timers (TIM4_CH1, TIM3_CH1, TIM2_CH1). All works great except TIM2_CH1. I can't to fix that issue for 3 days. May be some of You came across to this problem.
Here code for three timers. Configuration for all timers are same, but TIM2 doesn't work
#include "stm32f10x.h"
#include "stm32f10x_rcc.h"
#include "can.h"
#include "main.h"
void TIM4_Config(void);
void TIM3_Config(void);
void TIM2_Config(void);
uint32_t DataCapture_TIM4=0,
DataCapture_TIM3=0,
DataCapture_TIM2=0;
Timer Configuration for capturing mode for TIM4_CH1
void TIM4_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_Time_user;
GPIO_InitTypeDef port;
TIM_ICInitTypeDef TIM_ICInit_user;
NVIC_InitTypeDef NVIC_InitStructure2;
// TIM4 clock enable
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
// GPIOB clock enable
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// TIM4 channel_1 configuration : PB6
port.GPIO_Pin = GPIO_Pin_6;
port.GPIO_Mode = GPIO_Mode_IN_FLOATING;
port.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOB, &port);
// Connect TIM pin to AF2
// Enable the TIM4 global Ymtirrupt
NVIC_InitStructure2.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStructure2.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure2.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure2.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure2);
// Time base configuration
TIM_Time_user.TIM_Period = PERIOD;
TIM_Time_user.TIM_Prescaler = PRESCALER;
TIM_Time_user.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_Time_user.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4, &TIM_Time_user);
//Capture configuration
TIM_ICInit_user.TIM_Channel = TIM_Channel_1;
TIM_ICInit_user.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInit_user.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInit_user.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInit_user.TIM_ICFilter = 0x0;
//Inicialize our structure for capture signal
TIM_PWMIConfig(TIM4, &TIM_ICInit_user);
//input trigger
TIM_SelectInputTrigger(TIM4, TIM_TS_TI1FP1);
TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Reset);
// Select the slave Mode: Reset Mode
TIM_SelectMasterSlaveMode(TIM4,TIM_MasterSlaveMode_Enable);
// Enable the TIM4 Counter
TIM4->CR1 |= TIM_CR1_CEN;
// Enable the CC1 Interrupt request
TIM4->DIER |= TIM_IT_CC1;
}
Interrupt handler for TIM4_CH1
void TIM4_IRQHandler(void)
{
if (TIM4->SR & TIM_SR_CC1IF )
{
DataCapture_TIM4 = TIM4->CCR2;
}
}
Capture mode for TIM3_Ch1
void TIM3_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_Time_user;
GPIO_InitTypeDef port;
TIM_ICInitTypeDef TIM_ICInit_user1;
NVIC_InitTypeDef NVIC_InitStructure1;
// TIM4 clock enable
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
// GPIOB clock enable
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// TIM channel1 confikurotion : PA6
port.GPIO_Pin = GPIO_Pin_6;
port.GPIO_Mode = GPIO_Mode_IN_FLOATING;///////////////////////////////
port.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &port);
// Enable the TIM3 global interrupt
NVIC_InitStructure1.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure1.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure1.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure1.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure1);
// Time base configuration
TIM_Time_user.TIM_Period = PERIOD;
TIM_Time_user.TIM_Prescaler = PRESCALER;
TIM_Time_user.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_Time_user.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_Time_user);
//
TIM_ICInit_user1.TIM_Channel = TIM_Channel_1;
TIM_ICInit_user1.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInit_user1.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInit_user1.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInit_user1.TIM_ICFilter = 0x0;
//
TIM_PWMIConfig(TIM3, &TIM_ICInit_user1);
//input trigger
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);
// Select the slave Mode: Reset Mode
TIM_SelectMasterSlaveMode(TIM3,TIM_MasterSlaveMode_Enable);
// Enable the TIM3 Counter
TIM3->CR1 |= TIM_CR1_CEN;
// Enable the CC1 Intirrupt Request
TIM3->DIER |= TIM_IT_CC1;
}
Interrupt handler for TIM3_CH1
void TIM3_IRQHandler(void)
{
if (TIM3->SR & TIM_SR_CC1IF )
{
DataCapture_TIM3 = TIM3->CCR2;
}
}
A problem is HERE for Capturing mode for TIM2_Ch1
void TIM2_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_Time_user;
GPIO_InitTypeDef port;
TIM_ICInitTypeDef TIM_ICInit_user;
NVIC_InitTypeDef NVIC_InitStructure2;
// TIM4 clock enable
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
// GPIOB clock enable
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// TIM2 channel_1 configuration : PA0
port.GPIO_Pin = GPIO_Pin_0;
port.GPIO_Mode = GPIO_Mode_IN_FLOATING;
port.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &port);
// Enable the TIM2 global Interrupt
NVIC_InitStructure2.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure2.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure2.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure2.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure2);
// Time base configuration
TIM_Time_user.TIM_Period = PERIOD;
TIM_Time_user.TIM_Prescaler = PRESCALER;
TIM_Time_user.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_Time_user.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_Time_user);
//
TIM_ICInit_user.TIM_Channel = TIM_Channel_1;
TIM_ICInit_user.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInit_user.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInit_user.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInit_user.TIM_ICFilter = 0x0;
//
TIM_PWMIConfig(TIM2, &TIM_ICInit_user);
//input trigger
TIM_SelectInputTrigger(TIM2, TIM_TS_TI1FP1);
TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);
// Select the slave Mode: Reset Mode
TIM_SelectMasterSlaveMode(TIM2,TIM_MasterSlaveMode_Enable);
// Enable the TIM2 Counter
TIM2->CR1 |= TIM_CR1_CEN;
// Enable the CC1 Interrupt Request
TIM2->DIER |= TIM_IT_CC1;
}
Interrupt handler for TIM2_CH1
void TIM2_IRQHandler(void)
{
if (TIM2->SR & TIM_SR_CC1IF )
{
DataCapture_TIM2 = TIM2->CCR2;
}
}
Sorry I cann't add comment.You can check your setting register values in debug mode and check different between your work and CubeMx setting to find problem
As an alternative answer, I recommend you to try CubeMX.
It's a code generator for STM32 that can help you quickly write STM32 low level driver.

My Timer in STM32F4 doesn't perform the delay correctly

I'm using Timer to make the LED on/off for every 1 second (instead of using delay function). However, when I execute this code on IAR IDE, the LED on/off at about 2.5-3 second, not 1 second like I want. I wonder if there is any mistake in the code, or do I have to modify somewhere for the correct clock speed?
I'm using standard peripheral library, and the IDE I'm using is IAR.
#include "stm32f4xx.h"
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_BaseStruct;
void GPIO_Configuration(void);
void TIM_Configuration(void);
void Delay(__IO uint32_t nCount);
int main(void)
{
GPIO_Configuration();
TIM_Configuration();
while(1)
{
if(TIM_GetFlagStatus(TIM2,TIM_FLAG_Update) != RESET)
{
TIM_ClearFlag(TIM2,TIM_IT_Update);
GPIO_ToggleBits(GPIOD,GPIO_Pin_14);
}
}
}
void GPIO_Configuration(void)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
/* Configure PB0 PB1 in output pushpull mode */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD, &GPIO_InitStructure);
}
void TIM_Configuration(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
TIM_BaseStruct.TIM_Prescaler = 42000-1;
TIM_BaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_BaseStruct.TIM_Period = 2000-1;
TIM_BaseStruct.TIM_ClockDivision = 0;
TIM_BaseStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_BaseStruct);
TIM_Cmd(TIM2,ENABLE);
}
void Delay(__IO uint32_t nCount)
{
while(nCount--)
{
}
}
#ifdef USE_FULL_ASSERT
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) */
/* Infinite loop */
while (1)
{
}
}
#endif
I'm going to assume your clock frequency is 168Mhz. With your current prescaler and period values, this would put your timer at 2s.
(CLK_FREQ / (PRESCALER * PERIOD)) = (168MHZ / (42000 * 2000)) = 2 seconds
Try changing the period to 1000 to make it run at 1s. This, again, is assuming your clock frequency is 168Mhz. If your clock frequency is different, you can easily calculate the PRESCALER and PERIOD values (shown above) to get to 1s.

smt32 interrupts. why my diode is not blinking?

I have smt32l1xx board and this code below is not working. Debugger shows pinA5 is set, but diode connected to this pin is still not lightening. I dont know why. Even i add delay after setting bit it is not working. diode is connected to PA5 and GND on board.
#include <stm32l1xx.h>
#define ENABLE 1
#define DISABLE 0
void TIM2_IRQHandler() //interrupt
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
if (GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_5))
GPIO_ResetBits(GPIOA, GPIO_Pin_5); //LED OFF
else
GPIO_SetBits(GPIOA, GPIO_Pin_5); //LED ON <- im here and still nothing
}
}
int main(void)
{
/* gpio init struct */
GPIO_InitTypeDef gpio;
TIM_TimeBaseInitTypeDef tim;
NVIC_InitTypeDef nvic;
/* reset rcc */
RCC_DeInit();
RCC_APB2PeriphClockCmd(RCC_AHBENR_GPIOAEN, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
GPIO_StructInit(&gpio);
/* use pin 0 */
gpio.GPIO_Pin = GPIO_Pin_5;
/* mode: output */
gpio.GPIO_Mode = GPIO_Mode_OUT;
/* apply configuration */
GPIO_Init(GPIOA, &gpio);
TIM_TimeBaseStructInit(&tim); //timer
tim.TIM_CounterMode = TIM_CounterMode_Up;
tim.TIM_Prescaler = 64000 - 1;
tim.TIM_Period = 1000 - 1;
TIM_TimeBaseInit(TIM2, &tim);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM2, ENABLE);
nvic.NVIC_IRQChannel = TIM2_IRQn; //interrupt
nvic.NVIC_IRQChannelPreemptionPriority = 0;
nvic.NVIC_IRQChannelSubPriority = 0;
nvic.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic);
while (1)
{
}
/* never reached */
return 0;
}
To make sure that your hardware is properly initialized, you should use STM32CubeMX.
It seems that the GPIOA clock is on the AHB bus, however you call RCC_APB2PeriphClockCmd which is for APB2. So try to use the equivalent for AHB something like RCC_AHBPeriphClockCmd

ADC through PWM signal problems

I am using an STM32F100RB at the moment and I am trying to read a value from a potentiometer and to display it through the PWM signal. The problem I have is where I am connecting them I think. The PWM signal is generated through this code:
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
uint32_t Prescaler, Period;
/* Enable GPIO clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* Enable TIM clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
/* Configure TIM1_CH1 as alternate function push-pull */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; // No point in overdriving
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Both these must ultimately fit in 16-bit, ie 1..65536 */
Prescaler = (SystemCoreClock / 20000); // System -> 20 KHz
Period = 2000; // 20 KHz -> 1 Hz
/* Extra caution required with TIM1/TIM8 full function timers, to initialize ALL fields */
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Prescaler = (uint16_t)(Prescaler - 1);
TIM_TimeBaseStructure.TIM_Period = (uint16_t)(Period - 1);
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // Where do those stairs go? They go up!
TIM_TimeBaseStructure.TIM_ClockDivision = 0; // Not used
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; // Not used
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
/* PWM1 Mode configuration: Channel1 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_Pulse = (uint16_t)(Period / ADC1ConvertedValue[0]); // 50%
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
/* TIM1 enable counter */
TIM_Cmd(TIM1, ENABLE);
/* TIM1 Main Output Enable */
TIM_CtrlPWMOutputs(TIM1, ENABLE);
while (1)
{
}
The PWM output works fine, and it displays what it should display. The problem comes with the ADC, where something seems not to work as it should (the code is from the manufacturer website), and this is the full code.
#include "stm32f10x.h"
//#include "stm32f10x_conf.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_tim.h"
#include "stm32f10x_adc.h"
#include "stm32f10x_dma.h"
#include "stm32f10x_flash.h"
#define ADC1_DR_Address ((uint32_t)0x4001244C)
#define BufferLenght 4
ADC_InitTypeDef ADC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
uint16_t ADC1ConvertedValue[BufferLenght];
ErrorStatus HSEStartUpStatus;
void RCC_Configuration(void);
void GPIO_Configuration(void);
RCC_Configuration();
GPIO_Configuration();
/* DMA1 channel1 configuration ---------------------------------------------*/
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC1ConvertedValue;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = BufferLenght;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
/* Enable DMA1 channel1 */
DMA_Cmd(DMA1_Channel1, ENABLE);
/* ADC1 configuration ------------------------------------------------------*/
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = BufferLenght;
ADC_Init(ADC1, &ADC_InitStructure);
/* ADC1 regular channel11, channel14, channel16 and channel17 configurations */
ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_41Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_17, 2, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 3, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 4, ADC_SampleTime_1Cycles5);
/* Enable ADC1 DMA */
ADC_DMACmd(ADC1, ENABLE);
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);
/* Enable TempSensor and Vrefint channels: channel16 and Channel17 */
ADC_TempSensorVrefintCmd(ENABLE);
/* Enable ADC1 reset calibaration register */
ADC_ResetCalibration(ADC1);
/* Check the end of ADC1 reset calibration register */
while(ADC_GetResetCalibrationStatus(ADC1));
/* Start ADC1 calibaration */
ADC_StartCalibration(ADC1);
/* Check the end of ADC1 calibration */
while(ADC_GetCalibrationStatus(ADC1));
/* Start ADC1 Software Conversion */
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
/* Test on Channel 1 DMA1_FLAG_TC flag */
while(!DMA_GetFlagStatus(DMA1_FLAG_TC1));
/* Clear Channel 1 DMA1_FLAG_TC flag */
DMA_ClearFlag(DMA1_FLAG_TC1);
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
uint32_t Prescaler, Period;
/* Enable GPIO clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* Enable TIM clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
/* Configure TIM1_CH1 as alternate function push-pull */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; // No point in overdriving
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Both these must ultimately fit in 16-bit, ie 1..65536 */
Prescaler = (SystemCoreClock / 20000); // System -> 20 KHz
Period = 2000; // 20 KHz -> 1 Hz
/* Extra caution required with TIM1/TIM8 full function timers, to initialize ALL fields */
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Prescaler = (uint16_t)(Prescaler - 1);
TIM_TimeBaseStructure.TIM_Period = (uint16_t)(Period - 1);
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // Where do those stairs go? They go up!
TIM_TimeBaseStructure.TIM_ClockDivision = 0; // Not used
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; // Not used
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
/* PWM1 Mode configuration: Channel1 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_Pulse = (uint16_t)(Period / ADC1ConvertedValue[0]); // 50%
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
/* TIM1 enable counter */
TIM_Cmd(TIM1, ENABLE);
/* TIM1 Main Output Enable */
TIM_CtrlPWMOutputs(TIM1, ENABLE);
while (1)
{
}
}
/**
* #brief Configures the different system clocks.
* #param None
* #retval None
*/
void RCC_Configuration(void)
{
/* RCC system reset(for debug purpose) */
RCC_DeInit();
/* Enable HSE */
RCC_HSEConfig(RCC_HSE_ON);
/* Wait till HSE is ready */
HSEStartUpStatus = RCC_WaitForHSEStartUp();
if(HSEStartUpStatus == SUCCESS)
{
/* Enable Prefetch Buffer */
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
/* Flash 2 wait state */
FLASH_SetLatency(FLASH_Latency_2);
/* HCLK = SYSCLK */
RCC_HCLKConfig(RCC_SYSCLK_Div1);
/* PCLK2 = HCLK */
RCC_PCLK2Config(RCC_HCLK_Div1);
/* PCLK1 = HCLK/2 */
RCC_PCLK1Config(RCC_HCLK_Div2);
/* ADCCLK = PCLK2/4 */
RCC_ADCCLKConfig(RCC_PCLK2_Div4);
#ifndef STM32F10X_CL
/* PLLCLK = 8MHz * 7 = 56 MHz */
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_7);
#else
/* Configure PLLs *********************************************************/
/* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */
RCC_PREDIV2Config(RCC_PREDIV2_Div5);
RCC_PLL2Config(RCC_PLL2Mul_8);
/* Enable PLL2 */
RCC_PLL2Cmd(ENABLE);
/* Wait till PLL2 is ready */
while (RCC_GetFlagStatus(RCC_FLAG_PLL2RDY) == RESET)
{}
/* PLL configuration: PLLCLK = (PLL2 / 5) * 7 = 56 MHz */
RCC_PREDIV1Config(RCC_PREDIV1_Source_PLL2, RCC_PREDIV1_Div5);
RCC_PLLConfig(RCC_PLLSource_PREDIV1, RCC_PLLMul_7);
#endif
/* Enable PLL */
RCC_PLLCmd(ENABLE);
/* Wait till PLL is ready */
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}
/* Select PLL as system clock source */
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
/* Wait till PLL is used as system clock source */
while(RCC_GetSYSCLKSource() != 0x08)
{
}
}
/* Enable DMA1 clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/* Enable peripheral clocks --------------------------------------------------*/
/* Enable ADC1 and GPIOC clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
}
/**
* #brief Configures the different GPIO ports.
* #param None
* #retval None
*/
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Configure PC.01 and PC.04 (Channel11 and Channel14) as analog input -----*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
I am combining those two parts of code at the point where I should divide the value that TIM_Pulse is assigned:
TIM_OCInitStructure.TIM_Pulse = (uint16_t)(Period / ADC1ConvertedValue[0]);
I am a newcome in the embedded programming, and I just started playing with this board and the goal I want to achieve is to set the Pulse length according to the potentiometer value.
Thank you in advance,
Alex.
The modified code looks like this:
#include "stm32f10x_conf.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_adc.h"
#include "stm32f10x_tim.h"
double x = 0;
GPIO_InitTypeDef myGPIO;
ADC_InitTypeDef myADC;
void adc_config()
{
//ADC
myGPIO.GPIO_Pin = GPIO_Pin_6; //setat pe pin6
myGPIO.GPIO_Mode = GPIO_Mode_AIN; //setare ca analog
GPIO_Init(GPIOA, &myGPIO); //set to A6
RCC_ADCCLKConfig (RCC_PCLK2_Div6); //ceas pentru ADC (max 14MHz, 72/6=12MHz)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //ceas ADC
//configurare parametrii ADC
myADC.ADC_Mode = ADC_Mode_Independent;
myADC.ADC_ScanConvMode = DISABLE;
myADC.ADC_ContinuousConvMode = ENABLE;
myADC.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
myADC.ADC_DataAlign = ADC_DataAlign_Right;
myADC.ADC_NbrOfChannel = 1;
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_55Cycles5); //PA6 as Input
ADC_Init(ADC1, &myADC);
//enable
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
ADC_Cmd(ADC1, ENABLE);
}
int getPot(void)
{
return ADC_GetConversionValue(ADC1);
}
//configurare pini I/O
void GPIO_config(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
//LED-pinC9
GPIO_StructInit(&myGPIO);
myGPIO.GPIO_Pin = GPIO_Pin_9;
myGPIO.GPIO_Mode = GPIO_Mode_Out_PP;
myGPIO.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOC, &myGPIO);
}
int main(void)
{
GPIO_config(); //configurare pini
adc_config(); //configurare ADC
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
uint32_t Prescaler, Period;
/*!< At this stage the microcontroller clock setting is already configured,
this is done through SystemInit() function which is called from startup
file (startup_stm32f10x_xx.s) before to branch to application main.
To reconfigure the default setting of SystemInit() function, refer to
system_stm32f10x.c file
*/
/* Enable GPIO clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* Enable TIM clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
/* Configure TIM1_CH1 as alternate function push-pull */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; // No point in overdriving
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Both these must ultimately fit in 16-bit, ie 1..65536 */
Prescaler = (SystemCoreClock / 200000); // System -> 20 KHz
Period = 2000; // 20 KHz -> 1 Hz
/* Extra caution required with TIM1/TIM8 full function timers, to initialize ALL fields */
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Prescaler = (uint16_t)(Prescaler - 1);
TIM_TimeBaseStructure.TIM_Period = (uint16_t)(Period - 1);
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // Where do those stairs go? They go up!
TIM_TimeBaseStructure.TIM_ClockDivision = 0; // Not used
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; // Not used
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
while(1)
{
x = getPot()*3.3/4096; //obtinere valoare analog si convertirea in volti, 12bit ADC
/* PWM1 Mode configuration: Channel1 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_Pulse = (uint16_t)(Period / x); // 50%
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
/* TIM1 enable counter */
TIM_Cmd(TIM1, ENABLE);
/* TIM1 Main Output Enable */
TIM_CtrlPWMOutputs(TIM1, ENABLE);
if(x > 2)
{
GPIO_WriteBit(GPIOC, GPIO_Pin_9, Bit_SET);//pornire Led
}
else {
GPIO_WriteBit(GPIOC, GPIO_Pin_9, Bit_RESET);//oprire Led
}
}
}
Thanks to #Olaf.

STM32 Can't clear PWM interrupt flag

I'm trying to generate a 2MHz PWM with a duty-cycle of 50%. My problem is that I can't clear the interrupt flag. Here is my code:
#include "includes.h"
TIM_TimeBaseInitTypeDef TIM1_InitStruncture;
TIM_TimeBaseInitTypeDef TIM3_InitStruncture;
TIM_OCInitTypeDef TIM3_OCInitStructure;
SPI_InitTypeDef SPI_InitStructure;
void Timer3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_CC3) != RESET)
{
TIM_ClearFlag(TIM3, TIM_IT_CC3);
//dummy code
++StatusReg;
}
}
void CLK_init()
{
//activez HSI
RCC_HSICmd(ENABLE);
//astepst sa se activeze HSI
while( RCC_GetFlagStatus( RCC_FLAG_HSIRDY) == RESET );
//setez HSI ca sursa de clock
RCC_SYSCLKConfig( RCC_SYSCLKSource_HSI );
//activez HSE
RCC_HSEConfig( RCC_HSE_ON );
//astept sa se termine secventa de activare
while( RCC_GetFlagStatus( RCC_FLAG_HSERDY) == RESET );
//setez HSE (8MHz) ca input py PLL
//setez factotul de multiplicare 9
RCC_PLLConfig( RCC_PLLSource_HSE_Div1, RCC_PLLMul_9 );
//activez PLL-ul
RCC_PLLCmd(ENABLE);
//astept sa se termine secventa de activare
while( RCC_GetFlagStatus( RCC_FLAG_PLLRDY) == RESET );
#ifdef EMB_FLASH
// 5. Init Embedded Flash
// Zero wait state, if 0 < HCLK 24 MHz
// One wait state, if 24 MHz < HCLK 56 MHz
// Two wait states, if 56 MHz < HCLK 72 MHz
// Flash wait state
FLASH_SetLatency(FLASH_Latency_2);
// Half cycle access
FLASH_HalfCycleAccessCmd(FLASH_HalfCycleAccess_Disable);
// Prefetch buffer
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
#endif // EMB_FLASH
//setez iesirea de la PLL ca sursa de CLK
RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK );
}
void Port_C_Enable()
{
//GPIO_InitTypeDef GPIOC_InitStructure;
//resetez portul C (just in case)
RCC->APB2RSTR |= RCC_APB2RSTR_IOPCRST;
RCC->APB2RSTR &= ~RCC_APB2RSTR_IOPCRST;
//activez CLK-ul pentru portul C
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
/*
GPIOC_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIOC_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIOC_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOC, &GPIOC_InitStructure);
*/
}
void Timer3_Init()
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//reset Timer3 (just in case)
//RCC->APB1RSTR |= RCC_APB1RSTR_TIM3RST;
//RCC->APB1RSTR &= ~RCC_APB1RSTR_TIM3RST;
//give clock to Timer-ul 3
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
//frequency 2Mhz
TIM3_InitStruncture.TIM_Period = 36;
TIM3_InitStruncture.TIM_Prescaler = 0;
TIM3_InitStruncture.TIM_ClockDivision = 0;//TIM_CKD_DIV1;
TIM3_InitStruncture.TIM_CounterMode = TIM_CounterMode_CenterAligned3;
TIM_TimeBaseInit(TIM3, &TIM3_InitStruncture);
TIM_ITConfig(TIM3, TIM_IT_CC3, ENABLE);
TIM_Cmd(TIM3, ENABLE);
//dutycicle 50%
TIM3_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM3_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM3_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM3_OCInitStructure.TIM_Pulse = 18;
TIM_OC3Init(TIM3, &TIM3_OCInitStructure);
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM3, ENABLE);
TIM_Cmd(TIM3, ENABLE);
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*GPIOB Configuration: TIM3 channel1, 2, 3 and 4
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;*/
//GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE);
/* GPIOA Configuration:TIM3 Channel1, 2, 3 and 4 as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
void NVIC_init(void)
{
// NVIC init
#ifndef EMB_FLASH
/* Set the Vector Table base location at 0x20000000 */
NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
#else /* VECT_TAB_FLASH */
/* Set the Vector Table base location at 0x08000000 */
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
#endif
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
}
void SPI_init()
{
SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 0;
SPI_Init(SPI2, &SPI_InitStructure);
}
void main(void)
{
#ifdef DEBUG
debug();
#endif
// NVIC_SETPRIMASK();
CLK_init();
NVIC_init();
Port_C_Enable();
GPIO_Configuration();
//Timer1_Init();
Timer3_Init();
TIM_Cmd(TIM1, ENABLE);
unsigned int j=0;
while(1)
{
//dummy code
++j;
if(j == 0xff)
{
j=0;
}
}
}
Can anyone tell me why the CCR3 (Capture/Compare Register 3 Flag) stays high?
Thanks.
Pay attention to not mixing the following:
TIM_ClearFlag which shall be used together with TIM_FLAG_CC1
TIM_ClearITPendingBit which shall be used together with TIM_IT_CC1
as far as I understand. Otherwise you could have some masks which avoid to flag to be set.
Thanks aamxip for your answer.
I found the problem. It seems that the configuration is correct but i don't have enough time to execute the ISR(interrupt service routine). Once every 36 CLK's a new interrupt is generated and it takes me somewhere around 30 instructions or more only to enter in the ISR.
After more research i found out that i don't really need that frequency and i adopted a different approach, bit-banging.

Resources