ARM Cortex-M4: порты, прерывания #2

Куда ж без них..?

Если вы работали с 8бит контроллерами, подобными AVR или PIC, а иначе и быть не может, то разобраться в портах не составит труда. Здесь есть привычные аналоги DDRx, PORTx, а с внешними прерываниями дела обстоят даже по-проще.

Порты

Все управляет PIO (Parallel In/Out) контроллер. У SAM4C таких контроллеров три: PIOA, PIOB, PIOC, каждый из которых содержит по 32 цифровых линий I/O. Расположение конкретных пинов вы можете увидеть в документации.

Ashampoo_Snap_2014.06.16_09h0765444m25s_0010000_

В первую очередь нам нужно «включить» контроллер порта через PMC.

1
PMC->PMC_PCER0 = PMC_PCER0_PID11; //PIOA Enable

Для полного доступа к регистрам разблокируем PIO:

1
PIOA->PIO_WPMR = 0x50494F; //Очень черная магия

Теперь, я полагаю, нужно начать с простого: конфигурование линии как входа/выхода, операции чтения/записи. Поэтому отбросим все лишнее.

Простые манипуляции
dia_1
Насколько вы знаете, цифровой пин может быть либо входом, либо выходом. Это настраивается через два регистра: PIO_ODR и PIO_OER соответственно, которые работают только в одну сторону, поэтому проводить операции, подобные этим |=, &=, чтобы не повлиять на другие линии — не нужно. Кстати, в библиотеке CMSIS каждый пин порта удобно пронумерован,  к примеру:

1
2
PIOA->PIO_OER = PIO_OER_P6; //PA6 пин - выход.
PIOB->PIO_ODR = PIO_ODR_P30 | PIO_ODR_P6; //PB30, PB6 пины — входы.

Уровень на выходе определенной линии порта устанавливается регистрами: PIO_SODR, PIO_CODR. Первый — записывает лог.единицу, второй — лог.ноль.

1
2
PIOA->PIO_SODR = PIO_SODR_P6; //Установить 1 на ножке PA6
PIOA->PIO_CODR = PIO_CODR_P6; //Установить 0 на ножке PA6

Однако мы не лишены возможности записывать в несколько линий.

1
PIOB->PIO_CODR = PIO_CODR_P30 | PIO_CODR_P31; //Установить 0 на ножках PB30, PB31

Вот вам, кстати, и простейшая моргалка, только без задержки. :-) Без таймеров ее можно делать пустым циклом for.

Для чтения состояния линии мы используем регистр PIO_PDSR, но… так мы прочитаем состояние всего порта, а для чтения конкретного пина необходима конструкция подобная этой:

1
= (PIOA->PIO_PDSR & PIO_PDSR_P6); //Читаем состояние на ножке PA6

Периферия

Кроме портов ввода/вывода в микроконтроллере также присутствует UART, SPI, EBI и прочая периферия, которая подключена к определенным линиям.  Но в каждом проекте имеются различные требования и приоритеты: кому-то нужно по-больше простых I/O, кому-то интерфейсов, поэтому дана возможность мультиплексирования.
dia_2
У каждой линии есть четыре периферийные группы:

    • Ab00
    • Bb01
    • Cb10
  • Db11

Они ничего не означают :-D , это просто варианты связей, их нужно смотреть в документации. Мы можем выбрать, какую периферию «разрешить» на данной линии, через регистры PIO_ABCDSR[0..1], которые как раз являются двумя битами периферийных групп. Однако так просто PIO свои линии не отдаст, его необходимо отключить через регистр PIO_PDR. Весь процесс:

1
2
3
4
5
6
7
8
9
PIOA->PIO_IDR = PIO_IDR_P6; //Запрещаем прерывания на PA6, во избежание всяких неожиданностей
//Сейчас установим переф. «C»
 
uint32_t last0 = PIOA->PIO_ABCDSR[0]; //Сохраняем предыдущие состояния, тк. этот регистр I/O. (Почему?)
PIOA->PIO_ABCDSR[0] &= (~PIO_ABCDSR_P6 & last0); //Установка 0 в бит ABCDSR[0]
uint32_t last1 = PIOA->PIO_ABCDSR[1]; //Сохраняем предыдущие состояния…
PIOA->PIO_ABCDSR[1] = (PIO_ABCDSR_P6 | last1); //Установка 1 в бит ABCDSR[1]
 
PIOA->PIO_PDR = PIO_PDR_P6; //Отключаем PIO

Прерывания

Неотъемлемая часть системы, позволяющая выполнять команды точно и во-время. Все прерывания контролирует NVIC, позволяющий их включать/отключать, назначать преоритеты. В основу прерыванию должно идти определенное событие EVENT, в нашем случае это изменение состояния цифровой линии.
dia_3
Разрешить прерывание на конкретном пине не составит труда, это производится путем записи данных в регистр PIO_IER, но до этого нам необходимо настроить детектор событий. То есть, когда должно срабатывать прерывание. У детектора событий несколько своих регистров: PIO_ESRPIO_LSR — определяют тип: переход состояний/уровень; PIO_FELLSRPIO_REHLSR — определяют сам уровень: высокий или низкий; PIO_AIMDR, PIO_AIMER — событие по любому изменению.

  • Прерывание по высокому уровню: PIO_LSR = 1 PIO_REHLSR = 1 PIO_AIMER = 1
  • Прерывание по низкому уровню: PIO_LSR1 PIO_FELLSR = 1 PIO_AIMER = 1
  • Прерывание по перех. к высокому: PIO_ESR = 1 PIO_REHLSR = 1 PIO_AIMER = 1
  • Прерывание по перех. к низкому: PIO_ESR = 1 PIO_FELLSR = 1 PIO_AIMER = 1
  • Прерывание по любому переходу: PIO_AIMDR = 1

Не стоит забывать про NVIC контроллер, его также необходимо настроить. Здесь нам поможет CMSIS и пара функций:

1
2
NVIC_EnableIRQ(IRQn_t IRQn) - Разрешить прерывание
NVIC_SetPriority(IRQn_t IRQn, uint32_t priority) — Назначить приоритет. (не обязательно. можно по принципу: кто первый, тот в дамках!)

Кстати, я что-то не нашел, где посмотреть эти IRQn_t, приходится их подбирать с помощью подсказок CMSIS. Пример инициализации прерывания на PA6 по высокому уровню:

1
2
3
4
5
6
PIOA->PIO_IDR = PIO_IDR_P6; //Запрещаем
PIOA->PIO_AIMER = PIO_AIMER_P6; //Точная настройка событий
PIOA->PIO_LSR = PIO_LSR_P6; //Прерывание по уровню
PIOA->PIO_REHLSR = PIO_REHLSR_P6; //Прерывание по высокому уровню
PIOA->PIO_IER = PIO_IER_P6; //Разрешаем
NVIC_EnableIRQ(PIOA_IRQn); //Снова разрешаем

NVIC обрабатывает события с разных цифровых линий как одно общее с PIO контроллера и всегда вызывает одну и ту же процедуру:

1
void PIOA_Handler() { /***/ }

Чтобы сбросить прерывание, нужно просто прочитать значение из регистра PIO_ISR и положить его куда-нибудь. Так уж заведено у ARM.

1
2
3
4
5
6
7
void PIOA_Handler() {
//Код…
 
register uint32_t dummy;
dummy = PIOA->PIO_ISR; //Читаем
(void) dummy ; //Защита от оптимизаций компилятора
}

Но, как я писал, у нас нет функции прерывания на каждую ножку контроллера, у нас есть одна на 32-е. Это и вполне логично, зачем городить огород, если можно использовать регистры! PIO_ISR предназначен как раз для того, чтобы определить, на какой цифровой линии произошло прерывание.

1
2
3
4
5
6
7
8
9
void PIOA_Handler() {
 
register uint32_t dummy;
dummy = PIOA->PIO_ISR; //Читаем
if (dummy & PIO_ISR_P6) {
  //Код для PA6…
}
(void) dummy ; //Защита от оптимизаций компилятора
}

Все Handler-ы смотреть в файле startup_sam4c.c, который подключается к созданному проекту.


Все части

Вы можите оставить комментарий, или поставить трэкбек со своего сайта.

Написать комментарий

XHTML: Вы можете использовать эти теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Bug Report
Локализовано: шаблоны Wordpress