У меня очень простая программа, которая вызывает у меня головную боль.
Немного предыстории:
Я программирую Arduino Uno «Atmega328P» в atmelstudio 6. Я использую JTAGICE mkII в режиме отладки для программирования и отладки.
Я также использовал Этот метод использовать библиотеки Arduino, чтобы сделать вещи проще.
Я написал простую программу для подсчета переходов на одном из внешних контактов прерывания. В моем случае я использую INT0_vect. Значение, на которое я указываю, просто увеличивается для каждого перехода.
Проблема заключается в использовании глобального указателя в ISR. Вы можете подумать, что я забыл добавить ключевое слово volatile, но вы ошибаетесь.
ISR обслуживается для каждого перехода, но не меняет значение. Я не мог понять, почему, поэтому я прошелся по программе и обнаружил, что мой указатель указывает на R00, один из рабочих регистров. Это должно быть хорошо, но перед обслуживанием ISR R00 помещается в стек, R00 увеличивается, и предыдущее значение восстанавливается.
Я понятия не имею, почему это так. Как я уже сказал, я использую «volatile uint8_t*
msgstr «который должен быть указателем на изменчивый uint8_t.
Вот как выглядит код:
#include "Arduino.h"void setup();
void loop();
void update();
volatile uint8_t* Count;
void setup()
{
*Count = 0;
attachInterrupt(0,update, CHANGE); // makes ISR call update
}
void loop()
{
// delay(1000);
}
void update()
{
(*Count)++;
}
Основной цикл вызывает установку один раз, а затем повторяет цикл.
Присоединить прерывание — это макрос, который устанавливает прерывание, а затем эффективно создает это:
`ISR(INT0_vect){update();}`
Некоторая дополнительная информация:
Так как Count
Указатель — это глобальная переменная, которая не инициализирована явно, она имеет значение 0x0
, который является адресом, который регистрирует карты Atmega328P R00
к.
Функция, помеченная ISR()
макрос указывает компилятору, что это обработчик C ISR. Чтобы компилятор мог организовать код C для выполнения ISR без разрушения состояния того, что могло выполняться при запуске ISR, он должен добавить некоторый код пролога, чтобы сохранить часть состояния регистра ЦП (и соответствующий эпилог для восстановления). это состояние, когда функция ISR возвращается), чтобы прерванный путь выполнения не был поврежден. Следовательно ISR()
обозначение заставляет компилятор сохранять и восстанавливать регистр R00
(и другие регистры) в стеке.
Сочетание этих двух ситуаций, почему R00
сохраняется в стеке, затем ваш ISR увеличивает его, затем R00
восстанавливается из стека.
Если вы не хотите, чтобы это произошло, вы можете использовать ISR_NAKED
атрибут, указывающий компилятору не генерировать код пролога или эпилога. Однако, если ваш дескриптор ISR не сохраняет и не восстанавливает состояние регистра должным образом, то, что работает на переднем плане, скорее всего, будет работать не очень хорошо.
Итак, установите Count
указать на какое-то место, которое вы выделили для счетчика. Или сделать Count
простую переменную вместо указателя и разобраться с ней прямо в ISR.
Хороший обзор поддержки обработки прерываний в avr-gcc можно найти здесь: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html
Во встроенных приложениях популярной идиомой для доступа к регистрам является использование указателя и присвоение ему адреса:
uint32_t volatile * const uart_rx = 0xFFFFD000;
volatile
Квалификатор сообщает компилятору, что указанное содержимое изменится без ведома программы.
Чтение из регистра приема UART будет выглядеть так:
uint32_t register_value = * uart_rx;
Если аппаратный элемент 8-битный, вы должны использовать uint8_t
тип.
Правда в языке ассемблера, сгенерированном компилятором.
Если это не отвечает вашей проблеме, пожалуйста, отредактируйте ваш пост и добавьте больше деталей.