13. STM32. Программирование STM32F103. EXTI
Внешние прерывания
Внешние прерывания могут вызываються при изменении состояния логического сигнала на входе микроконтроллера. Внешние прерывания используют, когда нужно следить за изменением входного сигнала и оперативно реагировать. Когда нам нужен такой функционал, мы настраиваем нужный пин, настраиваем внешнее прерывание (по спаду, по фронту или по обоим фронтам) и, как только произойдет смена состояния сигнала, будет вызван обработчик прерывания.Любые линии портов ввода-вывода STM32 могут работать с внешними прерываниями. То есть, любой пин можно настроить на работу с прерыванием. В STM32F103 всего 19 линий внешних прерываний:
- EXTI0 ... EXTI15 - для работы с пинами портов
- EXTI16 - подключенный к PVD
- EXTI17 - RTC Alert event (будильник)
- EXTI18 - USB Wakeup event
- EXTI19 - Ethernet Wakeup event
В разных сериях STM32 количество EXTI может быть разной. Не поленитесь заглянуть в документацию. В некоторых микроконтроллерах через EXTI заведены прерывания от периферии. И не только типа wakeup. Например, на линии EXTI могут быть заведены выходы аналоговых компараторов. Давайте рассмотрим следующую структурную схему:
Из схемы видно, что прерывание EXTI0 можно задействовать для работы с нулевыми линиями всех портов. Прерывание EXTI1 можно задействовать для работы с первыми линиями всех портов. И так далее.
Если мы настроим прерывания на линиях PA0, PB0, при изменениях на любом из этих двух пинов будет вызываться один обработчик - EXTI0. Поэтому нам, возможно, понадобится дополнительная проверка состояния входов.
Инициализация внешнего прерывания
Инициализация внешнего прерывания выполняется следующим образом:- Включается тактирование порта GPIO и AFIO
- Настраивается пин порта на вход
- Настраиваем EXTI в NVIC
- Указываем порт и пин, который будет использоваться как источник для EXTI
- Настраиваем параметры EXTI

Пример использования EXTI
Использование EXTI на примере работы с сонаром HC-SR04. В этом примере микроконтроллер посылает сонару импульс Trigger, который запускает измерения. Через некоторое время сонар должен "поднять" сигнал Echo - именно в этот момент вызывается перывание и начинается отсчет времени. А потом сонар сигнал Echo "опускает". В этот момент вызывается перывание и измерение заканчивается. С таймера считывается показание счетчика и, в зависимости от измеренной длительности обратного импульса Echo рассчитывается расстояние от сонара до препятствия.Диаграмма сигналов HC-SR04:
Схема подключения сонара к тестовой плате STM32F103C8:
Текст програми:
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_exti.h"
#include "stm32f10x_tim.h"
#include "stdio.h"
#include "misc.h"
volatile uint8_t FLAG_ECHO = 0;
volatile uint16_t SonarValue;
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 sonar_init() {
GPIO_InitTypeDef gpio_cfg;
GPIO_StructInit(&gpio_cfg);
/* Timer TIM3 enable clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
/* Timer TIM3 settings */
TIM_TimeBaseInitTypeDef timer_base;
TIM_TimeBaseStructInit(&timer_base);
timer_base.TIM_CounterMode = TIM_CounterMode_Up;
timer_base.TIM_Prescaler = 72;
TIM_TimeBaseInit(TIM3, &timer_base);
TIM_Cmd(TIM3, ENABLE);
//Trigger Pin
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
gpio_cfg.GPIO_Mode = GPIO_Mode_Out_PP;
gpio_cfg.GPIO_Pin = GPIO_Pin_15;
GPIO_Init(GPIOB, &gpio_cfg);
/* Set variables used */
GPIO_InitTypeDef GPIO_InitStruct;
EXTI_InitTypeDef EXTI_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
/* Enable clock for AFIO */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
/* Set pin as input */
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
/* Add IRQ vector to NVIC */
/* PB0 is connected to EXTI_Line0, which has EXTI0_IRQn vector */
NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
/* Set priority */
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x00;
/* Set sub priority */
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x00;
/* Enable interrupt */
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
/* Add to NVIC */
NVIC_Init(&NVIC_InitStruct);
/* Tell system that you will use PB0 for EXTI_Line0 */
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
/* PD0 is connected to EXTI_Line0 */
EXTI_InitStruct.EXTI_Line = EXTI_Line0;
/* Enable interrupt */
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
/* Interrupt mode */
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
/* Triggers on rising and falling edge */
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
/* Add to EXTI */
EXTI_Init(&EXTI_InitStruct);
}
void EXTI0_IRQHandler(void) {
/* Make sure that interrupt flag is set */
if (EXTI_GetITStatus(EXTI_Line0) != RESET) {
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) != 0) {
// Rising
TIM_SetCounter(TIM3, 0);
}
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) {
// Falling
SonarValue = TIM_GetCounter(TIM3);
}
/* Clear interrupt flag */
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
void sonar_start() {
int i;
GPIO_SetBits(GPIOB, GPIO_Pin_15);
//Delay 0x72000
for(i=0;i<0x7200;i++);
GPIO_ResetBits(GPIOB, GPIO_Pin_15);
}
unsigned int sonar_get() {
unsigned long Sonar;
// 354000 - Sound speed (mm/sec)
// 72000000 - F_CPU
// 16 - Timer Prescaler
// Result = mm
Sonar = (354/2) * (unsigned long)SonarValue / (72000 / 72);
if (Sonar > 4000) Sonar = 4000;
if (Sonar < 20) Sonar = 20;
return (unsigned int)Sonar;
}
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(char *pucBuffer)
{
while (*pucBuffer)
{
USART_SendData(USART1, *pucBuffer++);
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
{
}
}
}
void TIM4_IRQHandler(void)
{
if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
{
sonar_start();
FLAG_ECHO = 1;
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
}
}
int main(void)
{
char buffer[80] = {`\0`};
SetSysClockTo72();
// TIMER4 два раза в секунду вызывает sonar_start(); и устанавливает флаг FLAG_ECHO = 1;
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 = 5000;
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();
sonar_init();
while(1)
{
if (FLAG_ECHO == 1) {
sprintf(buffer, "%d\r\n", sonar_get());
USARTSend(buffer);
FLAG_ECHO = 0;
}
}
}
Interrupt/Event
У STM32, кроме привычного нам Interrupt (прерывание) есть такое понятие как Events (события). Для того, чтобы понять, что такое Events (события) и зачем появился еще один термин, представьте, что все прерывания (Interrupts) вызваны событиями (Events). То есть, таймер переполнился - это событие, прилетел байт в USART - это событие. А события могут вызвать прерывания. А могут и не вызывать. Тогда зачем они? Дело в том, что события могут взаимодействовать с периферией напрямую без выполнения программного кода. Например, событие может запустить преобразование ADC. При этом совсем не нужно чтобы вызывался обработчик прерывания. Событие само может "толкнуть" периферию и не отвлекать микроконтроллер от выполнения основного кода. Использование событий мы рассмотрим позже.Желаю успехов!
Смотри также:
- 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
Архіви