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);
//EXTI
/* 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. Тестова плата. Прошивка через 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
Недавні записи
- Text to speech. Українська мова
- LCD Display ST7567S (IIC)
- Розпізнавання мови (Speech recognition)
- Selenium
- Комп'ютерний зір (Computer Vision)
- Деякі думки про точність вимірювань в електроприводі
- Датчики Холла 120/60 градусів
- Модуль драйверів напівмосту IGBT транзисторів
- Драйвер IGBT транзисторів на A316J
- AS5600. Варіант встановлення на BLDC мотор
Tags
docker sensors rtc led timer wifi remap sms css websocket mongodb bme280 rfid bluetooth esp8266 nodemcu bkp piezo bmp280 i2c mpu-6050 encoder examples avr brushless ngnix nvic displays bldc java-script pmsm barometer pwm lcd ethernet stm32 raspberry-pi capture usart gpio exti atmega meteo solar smd dma adc eeprom eb-500 flask python ssd1306 uart options flash mpx4115a html gps st-link 3d-printer servo dc-dc foc git books battery hih-4000 watchdog dht11 web tim programmator ssd1331 mpu-9250 usb rs-232 motor max1674 soldering
Архіви