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);
TIM_TimeBaseStructInit(&TIMER_InitStructure);
TIMER_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIMER_InitStructure.TIM_Prescaler = 8000;
TIMER_InitStructure.TIM_Period = 500;
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);
TIM_TimeBaseStructInit(&TIMER_InitStructure);
TIMER_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIMER_InitStructure.TIM_Prescaler = 7200;
TIMER_InitStructure.TIM_Period = 10000;
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. Тестовая плата. Прошивка через последовательный порт и через 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
Додати коментар
Недавні записи
- 🇺🇦 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
Архіви