Прерывания на stm32 f4 discovery - зажигаем диоды по кругу - arm

Продолжаем знакомиться с stm32f4 discovery.
На этот раз речь пойдёт о прерываниях.
Создадим программу которая будет зажигать диоды "по кругу" т.е. при нажатии на кнопку срабатывает прерывание и зажигается следующий диод..

Как и раньше я использую eclipse + arm plugin + st-util

Первым делом инклуды:

#include <stm32f4xx.h>
#include <stm32f4xx_rcc.h>   // для настройки тактирования
#include <stm32f4xx_gpio.h>  // для работы с периферией
#include <misc.h>   // всё необходимое для обработки прерываний

#include <stm32f4xx_syscfg.h>

#include <stm32f4xx_exti.h>

Первым делом необходимо инициализировать и настроить необходимую периферию.
Функция настройки кнопки:

void ButtonInit() {
      RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);   // разрешаем тактирование порта A
      GPIO_InitTypeDef gpio_b;   /// структура для инициализации кнопки
      GPIO_StructInit(&gpio_b);  // заполняем структуру стандартными параметрами
      gpio_b.GPIO_Mode = GPIO_Mode_IN;   // это кнопка - порт работает как вход

      GPIO_Init(GPIOA, &gpio_b);   // инициализируем порт А
}

Как вы наверное догадались из комментариев к коду, сначала мы разрешаем тактирование порта А, после чего заполняем настроечную структуру стандартными данными, а изменяем только параметр mode.

Глобальная константа для обозначения всех диодов:

const uint16_t LEDS = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;

Глобальный массив с диодами:

const uint16_t LED[] = {GPIO_Pin_12, GPIO_Pin_13, GPIO_Pin_14, GPIO_Pin_15};

Инициализируем порт D для работы с диодами:

void leds_init() {
      RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);  // разрешаем тактирование
      GPIO_InitTypeDef gpio;   // структура 
      GPIO_StructInit(&gpio);
      gpio.GPIO_OType = GPIO_OType_PP;   // подтяжка резисторами
      gpio.GPIO_Mode = GPIO_Mode_OUT;  // работаем как выход
      gpio.GPIO_Pin = LEDS;   // все пины диодов

      GPIO_Init(GPIOD, &gpio);
}

А теперь время пришло для самых вкусностей.
Настроим прерывание по нажатии кнопки.
Вот функция настройки:

void it_init() {
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0);
    EXTI_InitTypeDef exti;  // структура для настроки
    exti.EXTI_Line = EXTI_Line0;    /// кнопка на линии 0
    exti.EXTI_Mode = EXTI_Mode_Interrupt;    // прерывание (а не событие)
    exti.EXTI_Trigger = EXTI_Trigger_Rising;  // срабатываем по переднему фронту импульса
    exti.EXTI_LineCmd = ENABLE;    /// вкл

    EXTI_Init(&exti);

    NVIC_InitTypeDef nvic;
    nvic.NVIC_IRQChannel = EXTI0_IRQn;  // указываем канал IRQ
    nvic.NVIC_IRQChannelPreemptionPriority = 2;  // приоритет канала ( 0 (самый приоритетный) - 15)
    nvic.NVIC_IRQChannelSubPriority = 2;  // приоритет подгруппы
    nvic.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&nvic);
}

Поясню.
Сначала мы выбираем A порт как линию прерывания EXTI. Затем при помощи известной структуры настраиваем его. После чего переходим к настройке NVIC.

Глобальная переменная - флаг горящего диода:

u16 flag = 0;

Обработчик прерывания:

void EXTI0_IRQHandler(void){
     /* нужно проверять состояние линии прерывания  */
    if (EXTI_GetITStatus(EXTI_Line0) != RESET){
        flag += 1;  // увеличиваем флаг - следующий диод
        if (flag >= 4) flag = 0;  // у нас 4 диода. Обнуляем 
        EXTI_ClearITPendingBit(EXTI_Line0);  // сбрасываем флаг прерывания
    }
}

!!! Обращаем внимание на выделенные жирным участки кода.

Функция main:

int main(){
    ButtonInit();
    it_init();
    leds_init();
  do {
      GPIO_Write(GPIOD, LED[flag]);  // зажигаем диод по флагу. по сути флаг - номер диода (от 0 до 3)
  } while(1);
}

Теперь при нажатии кнопки будет включатся следующий диод. Изначально горит зелёный.
Против дребезга контактов я ничего не применял, так что кнопка иногда может срабатывать не совсем правильно.

!!! Обратите внимание. После прошивки такого проекта вам придётся держать reset для соединения с st-util

Скачать проект.

Просмотров:   27725

Комментарии

вс, 06/30/2013 - 06:44
Владимир

Не понятен комментарий на счёт резета в этом участке кода

void EXTI0_IRQHandler(void){
/* по тойже линии срабатывает RESET, поэтому нужно проверять */
if (EXTI_GetITStatus(EXTI_Line0) != RESET){
flag += 1; // увеличиваем флаг - следующий диод
if (flag >= 4) flag = 0; // у нас 4 диода. Обнуляем
EXTI_ClearITPendingBit(EXTI_Line0); // сбрасываем флаг прерывания
}
}

Мне кажется что эта запись if (EXTI_GetITStatus(EXTI_Line0) != RESET) проверяет наличие нуля (бит в состоянии резет) или единицы (бит - сет) в регистре статуса

ITStatus EXTI_GetITStatus ( uint32_t EXTI_Line )
Checks whether the specified EXTI line is asserted or not.
Parameters:
EXTI_Line,: specifies the EXTI line to check. This parameter can be EXTI_Linex where x can be(0..22)
Return values:
The new state of EXTI_Line (SET or RESET).

Вы так не считаете?

вс, 06/30/2013 - 18:54

глупость молодости ;)

Исправил! Спасибо за коментарий

чт, 09/05/2013 - 22:36
Валерий

Здравствуйте!
Подскажите плиз, как теперь залить что-то другое? :)
"держать reset для соединения с st-util" не помогает (либо держать надо как-то очень хитро?)
ST_Util отвечает либо " Core is held in reset", либо "No target connected"
Жаль будет, если такая навороченная борда до конца своих дней будет светодиодами по кругу моргать :)

сб, 09/14/2013 - 14:55

Я зажимал ресет, и затем запускал st-util. Дальше все как обычно.
Вы под windows или linux ?

ср, 10/23/2013 - 17:15
Віталь

Привіт! Скажіть будь ласка, чим відрізняється підключення переривання від іншої кнопки (зовнішньої)? Крім заміни GPIO, звичайно))

пн, 10/28/2013 - 19:33

Вам нужно понять, что такое вектора прерываний.

Соответственно нужно сменить EXTI_Line, EXTI_PinSource0 и название прерывания.
Все вектора (названия) прерываний можно найти в startup файле.

сб, 01/11/2014 - 18:51
инструктор по п...

Спасибо за статью с комментариями. Для начинающих очень полезна.

вс, 03/23/2014 - 20:29
Yakov

>lamazavr
>Вам нужно понять, что такое вектора прерываний.
Долго тупил, почему у меня прерывания он PA1 на EXTI0 не отрабатывает, спасибо за картину.

пт, 09/12/2014 - 18:23
Артем

"!!! Обратите внимание. После прошивки такого проекта вам придётся держать reset для соединения с st-util" - потому что забыли в инициализации кнопки добавить:
gpio_b.GPIO_Pin = GPIO_PinSource0;

вт, 12/02/2014 - 13:10
Ti-j

Если ошибаюсь, то сильно не пинайте, знакомлюcь с stm32 второй день.
Без этого работает лучше. Нет проблем с ресетом: GPIO_Init(GPIOA, &gpio_b); // инициализируем порт А

или вообще в void ButtonInit() { ... } всё заменить на:
// RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
// GPIOA->MODER &= ~GPIO_MODER_MODER0;

вс, 01/01/2017 - 10:48
Андрей

Попытался настроить прерывание на порт "С" pin2 переписав сточку
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC,EXTI_PinSource2); перенастроив соответственно на EXTI_Line2 везде. Вместо того чтобы получить прерывание на порту C2 получил прерывание на порту А2 и вообще что не настрой получаю прерывание только на порту "А". "Что не начни делать все равно получится паровоз". Сточку SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC,EXTI_PinSource2); можно вообще не писать все равно настраиваться на порт "А". Не подскажите: что я делаю не так?

вт, 03/28/2017 - 17:47

Присылайте весь код, посмотрю. Контроллер f407 ?

Добавить комментарий

Plain text

  • HTML-теги не обрабатываются и показываются как обычный текст
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Строки и параграфы переносятся автоматически.
CAPTCHA
Введи эти символы. Ато роботы одолели!