четверг, 21 сентября 2017 г.

Использование глобальных переменных в Atmel Studio (модификатор volatile)

   Иногда, при написании программ на Си приходится использовать глобальные переменные, при изменении которых контроллер должен выполнить ряд определенных действий. Но если просто объявить переменную в глобальной области видимости (за пределами main), то увы, данного функционала мы не получим.
   Для примера напишем небольшую программу, которая зажигает светодиод при изменении состояния вывода  (возьмем INT0). Создадим глобальную переменную "CheckIt", которая будет своеобразным триггером. В цикле будем проверять эту переменную, и если она не равна нулю, то зажжем светодиод. В свою очередь данную переменную будет изменять подпрограмма прерывания, запускающаяся при изменении состояния вывода (INT0).

example project


   Соберем проект и запустим его в контроллере или в симуляторе. И как бы мы не старались дергать вывод INT0, светодиод никогда не загорится. 
   Теперь посмотрим на ассемблерный код, который сгенерировал компилятор.

example project assembler code

   Код достаточно простой, но если посмотреть на то, как реализована проверка переменной в цикле (адреса от 0000004F до 00000054), все станет ясно. Обратите внимание, что перед проверкой переменной она загружается из памяти в регистр (адрес 00000004F), затем проверяется на равенство нулю. И в следующей команде (по адресу 00000052) скрывается самое интересное - если переменная по прежнему равна нулю, то мы переходим на предыдущую команду (PC-0x01). Т.е. мы один раз загрузили переменную и постоянно ее проверяем, не загружая снова.
   Все это связано с тем, что компилятор, создавая ассемблерный код не предполагает, что переменную "CheckIt", которую мы проверяем в цикле, кто-то может изменить в другом месте. Выход из этой ситуации достаточно простой - необходимо сообщить компилятору, что переменная может измениться в другой части программы. Для этого в Си предусмотрен модификатор "volatile" (изменяемый).
   Добавим модификатор к переменной в коде программы.

example project using volatile modificator

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

example project using volatile modificator (assembler code)

   Обратите внимание на адрес 000000052 - в предыдущей версии программы мы переходили на предыдущую команду и начинали проверку снова. А теперь мы переходим (PC-0x03) сначала к загрузке переменной (адрес 0000004F), и лишь после этого снова проверяем ее.
   

*Проект был написан для ATmega328p, и проверен в симуляторе. Оптимизация проекта была установлена по умолчанию и не изменялась.