9. STM32. Програмування STM32F103. TIMER
9. STM32. Программирование STM32F103. TIMER
(на русском языке)
Таймери загального призначення
Таймери у мікроконтролерах STM32 поділяються за функціоналом на:- basic timers (базові таймери)
- general-purpose timers (загального призначення: TIM2, TIM3, TIM4)
- advanced-control timers (продвинуті таймери: TIM1)
Сьогодні ми розглянемо таймери загального призначення. Базових таймерів у нас немає, та це не страшно, таймери загального призначення мають такий самий функціонал, як і базові, плюс ще дещо.
Взагалі, таймери можуть не тільки генерувати переривання через певний час, та вимірювати час між подіями. Таймери можуть генерувати PWM сигнал, та поодинокі імпульси, працювати з периферією, наприклад запускати перетворення АЦП, виконувати захват сигналу, працювати з енкодерами, тощо. Таймерам буде присвячено декілька статей. Почнемо з самого простого.
Генерування переривання через рівні проміжки часу
Для прикладу налаштуємо таймер TIM4 і заставимо блимати світлодіод через певний проміжок часу. Пам`ятаєте самий перший приклад? Там затримка робилась через цикл. Це не правильно. Намагайтесь так не робити. Давайте зробимо блимання світлодіодом "по феншую".Отже, ми налаштуємо таймер таким чином, щоб він рахував до певного числа і визивав переривання по переповненню. Таймери рахують імпульси, якими вони тактуються (Дивись статтю про Тактування STM32). У STM мікроконтролерах таймери мають поділювач частоти, який можна налаштувати для кожного таймера. Значення поділювача можуть бути від 1 до 65535. Крім того, ми можемо задати число, дорахувавши до якого таймер буде викликати переривання по переповненню, обнулятись і рахувати з початку. Комбінуючи ці два параметри, можна добитися потрібної частоти, з якою буде викликатися переривання від таймера.
У нашому прикладі контролер тактуватиметься від зовнішнього кварцу частотою 8МГц. Таймер також буде тактуватися цією частотою, бо поділювач APB1 = 1. Зверніть увагу на жовті блоки схеми тактування.
Тобто, якщо APB1 = 1, тоді таймер TIM4 буде тактуватися частотою 8МГц. Якщо APB1 = 2, тоді таймер TIM4 буде тактуватися теж частотою 8МГц. Бо, згідно малюнку, частота помножується на 2. Якщо APB1 = 4, тоді таймер TIM4 буде тактуватися частотою 4МГц. Бо, згідно малюнку, якщо APB1 > 1, то частота, поділена на APB1 потім домножиться на 2. Не забувайте про цю особливість.
Ми встановимо поділювач таймера 8000. Таймер буде рахувати з частотою 8000000/8000 = 1000 разів за секунду. Встановимо період = 500. Тобто, дорахувавши до 500 (це 2 рази за секунду) буде визиватися переривання. У обробнику переривання ми пропишемо команду для зміни стану світлодіода.
Код програми:
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_tim.h"
#include "misc.h"
void SetSysClockToHSE(void)
{
ErrorStatus HSEStartUpStatus;
/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration -----------------------------*/
/* 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)
{
/* HCLK = SYSCLK */
RCC_HCLKConfig( RCC_SYSCLK_Div1);
/* PCLK2 = HCLK */
RCC_PCLK2Config( RCC_HCLK_Div1);
/* PCLK1 = HCLK */
RCC_PCLK1Config(RCC_HCLK_Div1);
/* Select HSE as system clock source */
RCC_SYSCLKConfig( RCC_SYSCLKSource_HSE);
/* Wait till PLL is used as system clock source */
while (RCC_GetSYSCLKSource() != 0x04)
{
}
}
else
{ /* If HSE fails to start-up, the application will have wrong clock configuration.
User can add here some code to deal with this error */
/* Go to infinite loop */
while (1)
{
}
}
}
void TIM4_IRQHandler(void)
{
if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
{
// Обов`язково скидаємо прапор. Якщо цього не зробити, після обробки переривання знову попадемо сюди
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
GPIOC->ODR ^= GPIO_Pin_13;
}
}
int main(void)
{
SetSysClockToHSE();
/* Initialize LED which connected to PC13 */
GPIO_InitTypeDef GPIO_InitStructure;
// Enable PORTC Clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
/* Configure the GPIO_LED pin */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_ResetBits(GPIOC, GPIO_Pin_13); // Set C13 to Low level ("0")
// TIMER4
TIM_TimeBaseInitTypeDef TIMER_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); // Вмикаємо тактування таймера TIM4
TIM_TimeBaseStructInit(&TIMER_InitStructure);
TIMER_InitStructure.TIM_CounterMode = TIM_CounterMode_Up; // Режим рахунку
TIMER_InitStructure.TIM_Prescaler = 8000; // Поділювач частоти для таймера
// Треба ще враховувати як налаштовані поділювачі RCC_HCLKConfig( RCC_SYSCLK_Div1); RCC_PCLK1Config(RCC_HCLK_Div1);
// У нашому випадку обидва = RCC_SYSCLK_Div1, тобто до поділювача таймера доходить частота зовнішнього кварцу (8МГц)
TIMER_InitStructure.TIM_Period = 500; // Період, через який виконується переривання по переповненню // F=8000000/8000/500 = 2 рази/сек.
TIM_TimeBaseInit(TIM4, &TIMER_InitStructure);
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); // Вмикаємо переривання по переповненню таймера
TIM_Cmd(TIM4, ENABLE);// Вмикаємо таймер
/* NVIC Configuration */
/* Enable the TIM4_IRQn Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
while(1)
{
// У головному циклі робимо що нам заманеться.
}
}
Тепер у головному циклі можемо робити що нам треба, а світлодіод буде перемикатися по таймеру.
Вимірювання часу між двома подіями
Коли нам потрібно вимірювати довжину імпульсу чи час між двома подіями, теж застосовується таймер. Ми розглянемо приклад, у якому будемо запускати таймер однією подією (натисканням кнопки, підключеної до PB0), зупиняти - іншою (аналогічна кнопка, але підключена до PB1). При зупинці будемо вираховувати час між цими двома подіями і відправляти результат у послідовний порт USART.
Код програми:
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_tim.h"
#include "misc.h"
volatile int TimeResult;
volatile int TimeSec;
volatile uint8_t TimeState = 0;
void SetSysClockTo72(void)
{
ErrorStatus HSEStartUpStatus;
/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration -----------------------------*/
/* 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);
/* PLLCLK = 8MHz * 9 = 72 MHz */
RCC_PLLConfig(0x00010000, RCC_PLLMul_9);
/* 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)
{
}
}
else
{ /* If HSE fails to start-up, the application will have wrong clock configuration.
User can add here some code to deal with this error */
/* Go to infinite loop */
while (1)
{
}
}
}
void TIM4_IRQHandler(void)
{
if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
{
// Обов`язково скидаємо прапор. Якщо цього не зробити, п_сля обробки переривання знову попадемо сюди
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
TimeSec++;
}
}
void usart_init(void)
{
/* Enable USART1 and GPIOA clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
/* Configure the GPIOs */
GPIO_InitTypeDef GPIO_InitStructure;
/* Configure USART1 Tx (PA.09) as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure USART1 Rx (PA.10) as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure the USART1 */
USART_InitTypeDef USART_InitStructure;
/* USART1 configuration ------------------------------------------------------*/
/* USART1 configured as follow:
- BaudRate = 115200 baud
- Word Length = 8 Bits
- One Stop Bit
- No parity
- Hardware flow control disabled (RTS and CTS signals)
- Receive and transmit enabled
- USART Clock disabled
- USART CPOL: Clock is active low
- USART CPHA: Data is captured on the middle
- USART LastBit: The clock pulse of the last data bit is not output to
the SCLK pin
*/
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
/* Enable USART1 */
USART_Cmd(USART1, ENABLE);
}
void USARTSend(const unsigned char *pucBuffer)
{
while (*pucBuffer)
{
USART_SendData(USART1, *pucBuffer++);
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
{
}
}
}
int main(void)
{
char buffer[80] = {`\0`};
SetSysClockTo72();
/* Initialize LED which connected to PC13 */
GPIO_InitTypeDef GPIO_InitStructure;
// Enable PORTC Clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
/* Configure the GPIO_LED pin */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_SetBits(GPIOC, GPIO_Pin_13); // Set C13 to Low level ("0")
/* Initialize Button input PB0 PB1 */
// Enable PORTB Clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
/* Configure the GPIO_BUTTON pin */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// TIMER4
TIM_TimeBaseInitTypeDef TIMER_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); // Вмикаємо тактування таймера TIM4
TIM_TimeBaseStructInit(&TIMER_InitStructure);
TIMER_InitStructure.TIM_CounterMode = TIM_CounterMode_Up; // Режим рахунку
TIMER_InitStructure.TIM_Prescaler = 7200; // Поділювач таймера
TIMER_InitStructure.TIM_Period = 10000; // Період, через який виконується переривання по переповненню // F=72000000/7200/10000 = 1 раз/сек.
TIM_TimeBaseInit(TIM4, &TIMER_InitStructure);
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); // Вмикаємо переривання по переповнення таймера
TIM_Cmd(TIM4, ENABLE);// Вмикаємо таймер
/* NVIC Configuration */
/* Enable the TIM4_IRQn Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
usart_init();
while(1)
{
if (TimeState == 0) {
// Кнопка запускає відлік таймера
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) {
TIM_Cmd(TIM4, ENABLE);
TIM_SetCounter(TIM4, 0);
TimeSec = 0;
// Set Status "ON"
TimeState = 1;
// OFF LED
GPIO_ResetBits(GPIOC, GPIO_Pin_13);
USARTSend("Started...");
}
}
if (TimeState == 1) {
// Кнопка зупиняє відлік таймера
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) {
TimeResult = TIM_GetCounter(TIM4)/10 + TimeSec * 1000; // Time im msec
TIM_Cmd(TIM4, DISABLE);
TimeState = 0;
// ON LED
GPIO_SetBits(GPIOC, GPIO_Pin_13);
sprintf(buffer, "Time: %d ms\r\n", TimeResult);
USARTSend(buffer);
}
}
}
}
Бажаю успіхів!
Дивись також:
- 1. STM32. Програмування STM32F103. Тестова плата. Прошивка через UART та через ST-Link
- 2. STM32. Програмування. IDE для STM32
- 3. STM32. Програмування STM32F103. GPIO
- 4. STM32. Програмування STM32F103. Тактування
- 5. STM32. Програмування STM32F103. USART
- 6. STM32. Програмування STM32F103. NVIC
- 7. STM32. Програмування STM32F103. ADC
- 8. STM32. Програмування STM32F103. DMA
- 9. STM32. Програмування STM32F103. TIMER
- 10. STM32. Програмування STM32F103. TIMER. Захоплення сигналу
- 11. STM32. Програмування STM32F103. TIMER. Encoder
- 12. STM32. Програмування STM32F103. TIMER. PWM
- 13. STM32. Програмування STM32F103. EXTI
- 14. STM32. Програмування STM32F103. RTC
- 15. STM32. Програмування STM32F103. BKP
- 16. STM32. Програмування STM32F103. Flash
- 17. STM32. Програмування STM32F103. Watchdog
- 18. STM32. Програмування STM32F103. Remap
- 19. STM32. Програмування STM32F103. I2C Master
- 20. STM32. Програмування STM32F103. I2C Slave
- 21. STM32. Програмування STM32F103. USB
- 22. STM32. Програмування STM32F103. PWR
- 23. STM32. Програмування STM32F103. Option bytes
- 24. STM32. Програмування STM32F103. Bootloader
- STM32. Скачати приклади
- System Workbench for STM32 Інсталяція на Ubuntu
- Keil uVision5 – IDE для STM32
- IAR Workbench – IDE для STM32
- Керування безколекторним двигуном постійного струму (BLDC) за допомогою STM32
- Керування PMSM за допомогою STM32
Отличный цикл статей. Не останавливайтесь и продолжайте дальше.
Делитель таймера TIM_Prescaler = 7200
т.е. Таймер щелкает 72000000/7200 = 10000 раз в секунду.
Если таймер насчитал 10000, тогда это 1000 миллисекунд. Результат нужно оделить на 10.
Можно было бы установить делитель TIM_Prescaler = 72000, и тогда делить на 10 не пришлось бы, но... делитель может быть не более 65535.
Додати коментар
Недавні записи
- 🇺🇦 FOC Board STM32F103RB 🧩
- STM32 Motor control SDK - керування оборотами мотора за допомогою потенціометра 📑
- Flask✙Gunicorn✙Nginx➭😎
- STM32 Motor control SDK - програмне керування обертам мотора
- STM32 Motor control SDK - як створити перший проект
- Vue SVG. Приклад побудови живого параметричного креслення
- Вимірювання моменту мотора
- Vue SVG - компонент. Приклад 📑
- Flask + Vue 🏁 Финальный пример 🏁
- Flask, CORS, JSON-файл. Пример#6
Tags
bldc brushless stm32 motor web html css flask atmega foc git java-script pmsm raspberry-pi python websocket mongodb esp8266 nodemcu st-link tim timer docker ngnix programmator ssd1331 ssd1306 wifi uart meteo bme280 bmp280 i2c gps mpu-6050 mpu-9250 sensors 3d-printer options usb barometer remap watchdog flash eeprom rtc bkp encoder pwm servo capture examples dma adc nvic usart gpio books battery dc-dc sms max1674 avr lcd dht11 piezo rs-232 rfid solar exti bluetooth eb-500 displays ethernet led smd soldering mpx4115a hih-4000
Архіви