mikroPascal for AVR. Урок 3. Еще раз UART а также немного о прерываниях

Ну вот, после очень большого перерыва я наконец что-то смог сделать. Эта третья по счету статья о среде программирования mikroPascal, речь в которой пойдет снова о UART. 
Первая статья, вторая статья

В этот раз все будем делать наглядно. В качестве «экспоната» я решил использовать исходник программы «iCPU». Вообще то в связке должны работать две программы: на ПК и на микроконтроллере. Так как выбор пал на микроконтроллер Atmega8, то связь с ПК будет через COM порт (виртуальный). Под словом «связь» имеется ввиду прием данных. Но на данный момент, в программа под Windows не готова, и будем использовать моделирование в Proteus + терминал.

Итак, еще раз о UART:

 UARTx_Init(baud_rate : dword); //Инициализация 1 UART модуля (для тех МК, где их несколько) UARTx_Init_Advanced(baud_rate : dword; parity : byte; stop_bits : byte); //Расширенная инициализация UART. Можно задать параметры: бауд рейт, четность, стоповые биты. UARTx_Data_Ready(): byte; //Возвращает "1" если в буфере приема есть данные. UARTx_Tx_Idle(): byte; //Возвращает "1" если данные были переданы. UARTx_Read(): byte; //Процедура считывания данных; UARTx_Read_Text(var Output : string[255]; var Delimiter : sting[10]; Attempts : byte); //То же самое, но запись ведется в строковую переменную. Параметры: переменная, разделитель, какой длинны строки нужно обрабатывать. UARTx_Write(data_ : byte); //Процедура записи в порт. UARTx_Write_Text(var uart_text : string[255]); //То же самое, только отсылает строковую переменную. UART_Set_Active (read_ptr : ^Tread_ptr; write_ptr : ^Twrite_ptr; ready_ptr : ^Tready_ptr; tx_idle_ptr : ^Ttx_idle_ptr); //Выбор модуля UART, который должен быть активным на данный момент (подробнее есть в справке).

А вот после того, как вы вспомнили что представляет из себя UART в mikroPascal, можно и с прерываниями познакомиться. Я и сам не так давно с ними разобрался, так что если где увидите косяк, то сразу пишите в коментах 🙂

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

В данном случае нас интересуют прерывания по таймеру/счетчику 1. А точнее, из всех прерываний только прерывание по совпадению. Для этого нам нужны такие регистры:

TCCR1B (настраиваем предделитель (1, 8, 64, 256, 1024) , OCR1A (настраиваем компаратор) , TIMSK (выбор прерываний), SREG (регистр статуса).

Теперь, когда знаем зачем нам эти регистры, можно и сконфигурировать их.

 TCCR1B:=0x04; //устанавливаем предделитель на 256 OCR1AH:=0x05; //Записываем старший бит регистра сравнения OCR1AL:=0xdc; //Записываем младший бит регистра сравнения TIMSK:=0x10; //Выбираем прерывание по совпадению  SREG_I_bit:=1; //Разрешаем прерывания

Вы скорее всего обратили внимание, что регистр OCR1A странный. Просто он состоит из двух восьмибитных регистров. В итоге его разрядность составляет 16 бит.

Теперь можно создать процедуру для обработки прерываний. Назовем ее Light().

 procedure Light(); iv IVT_ADDR_TIMER1_COMPA; //тут мы выбрали, по какое прерывание будет вызывать выполнение процедуры. begin      TCNT1H:=0x00; //Обнуляем счетный регистр (он тоже "двойной").      TCNT1L:=0x00; end;

Ну вот, основное по части прерываний сделали, а теперь….

 program iCPU;  uses AADL;  var c, inp:integer;     iarr:array [0..3] of integer;     str:string [10];  procedure Light(); iv IVT_ADDR_TIMER1_COMPA; begin      TCNT1H:=0x00;      TCNT1L:=0x00;            PORTD7_bit:=1;      delay_ms(10);      PORTD7_bit:=0;      if inp<=3 then inc(inp) else inp:=0;      case inp of      0:begin             case iarr[inp] of                     1:        begin                                    PORTB:=0x01;                                    PORTC:=0x1e;                               end;                     2:        begin                                    PORTB:=0x01;                                    PORTC:=0x1d;                               end;                     3:        begin                                    PORTB:=0x01;                                    PORTC:=0x1b;                               end;                     4:        begin                                    PORTB:=0x01;                                    PORTC:=0x17;                               end;                     5:        begin                                    PORTB:=0x02;                                    PORTC:=0x0f;                               end;                     6:        begin                                    PORTB:=0x02;                                    PORTC:=0x1e;                               end;                     7:        begin                                    PORTB:=0x02;                                    PORTC:=0x1d;                               end;                     8:        begin                                    PORTB:=0x02;                                    PORTC:=0x1b;                               end;                     9:        begin                                    PORTB:=0x02;                                    PORTC:=0x17;                               end;                     10:        begin                                    PORTB:=0x02;                                    PORTC:=0x0f;                               end;             end;             delay_ms(30);      end;      1:begin             case iarr[inp] of                     1:        begin                                    PORTB:=0x04;                                    PORTC:=0x1e;                               end;                     2:        begin                                    PORTB:=0x04;                                    PORTC:=0x1d;                               end;                     3:        begin                                    PORTB:=0x04;                                    PORTC:=0x1b;                               end;                     4:        begin                                    PORTB:=0x04;                                    PORTC:=0x17;                               end;                     5:        begin                                    PORTB:=0x04;                                    PORTC:=0x0f;                               end;                     6:        begin                                    PORTB:=0x08;                                    PORTC:=0x1e;                               end;                     7:        begin                                    PORTB:=0x08;                                    PORTC:=0x1d;                               end;                     8:        begin                                    PORTB:=0x08;                                    PORTC:=0x1b;                               end;                     9:        begin                                    PORTB:=0x08;                                    PORTC:=0x17;                               end;                     10:        begin                                    PORTB:=0x08;                                    PORTC:=0x0f;                               end;             end;             delay_ms(30);      end;      2:begin             case iarr[inp] of                     1:        begin                                    PORTB:=0x10;                                    PORTC:=0x1e;                               end;                     2:        begin                                    PORTB:=0x10;                                    PORTC:=0x1d;                               end;                     3:        begin                                    PORTB:=0x10;                                    PORTC:=0x1b;                               end;                     4:        begin                                    PORTB:=0x10;                                    PORTC:=0x17;                               end;                     5:        begin                                    PORTB:=0x10;                                    PORTC:=0x0f;                               end;                     6:        begin                                    PORTB:=0x20;                                    PORTC:=0x1e;                               end;                     7:        begin                                    PORTB:=0x20;                                    PORTC:=0x1d;                               end;                     8:        begin                                    PORTB:=0x20;                                    PORTC:=0x1b;                               end;                     9:        begin                                    PORTB:=0x20;                                    PORTC:=0x17;                               end;                     10:        begin                                    PORTB:=0x20;                                    PORTC:=0x0f;                               end;             end;             delay_ms(30);      end;      3:begin             case iarr[inp] of                     1:        begin                                    PORTB:=0x40;                                    PORTC:=0x1e;                               end;                     2:        begin                                    PORTB:=0x40;                                    PORTC:=0x1d;                               end;                     3:        begin                                    PORTB:=0x40;                                    PORTC:=0x1b;                               end;                     4:        begin                                    PORTB:=0x40;                                    PORTC:=0x17;                               end;                     5:        begin                                    PORTB:=0x40;                                    PORTC:=0x0f;                               end;                     6:        begin                                    PORTB:=0x80;                                    PORTC:=0x1e;                               end;                     7:        begin                                    PORTB:=0x80;                                    PORTC:=0x1d;                               end;                     8:        begin                                    PORTB:=0x80;                                    PORTC:=0x1b;                               end;                     9:        begin                                    PORTB:=0x80;                                    PORTC:=0x17;                               end;                     10:        begin                                    PORTB:=0x80;                                    PORTC:=0x0f;                               end;             end;             delay_ms(30);      end;      end; end;    begin TCCR1B:=0x04; OCR1AH:=0x05; OCR1AL:=0xdc; TIMSK:=0x10; SREG_I_bit:=1;  DDRC:=0xFF; DDRB:=0xFF; DDD7_bit:=1;   Uart1_init(9600);       While TRUE do begin            if (UART_Data_Ready() = 1) then begin                UART_Read_Text(str, '/', 10);                c:=StrToInt(str);                Str_Cut_Left(str,1);                if (c>100) and (c<200) then iarr[0]:=StrToInt(str)                else if (c>200) and (c<300) then iarr[1]:=StrToInt(str)                else if (c>300) and (c<400) then iarr[2]:=StrToInt(str)                else if (c>400) and (c<500) then iarr[3]:=StrToInt(str);            end;      end; end.

Вот это все — тело программы. Нет, она не сложная, но много рутинных действий.

Разберем по порядку.

1. Это конфигурация регистров и портов микроконтроллера.

 TCCR1B:=0x04;  OCR1AH:=0x05; OCR1AL:=0xdc; TIMSK:=0x10; SREG_I_bit:=1;  DDRC:=0xFF; //Конфигурируем порт на выход DDRB:=0xFF; //         ------------//--------------- DDD7_bit:=1; //То же самое, только для конкретного пина.   Uart1_init(9600); //Инициализация UART

2. Далее основной цикл программы.

 While TRUE do begin            if (UART_Data_Ready() = 1) then begin //Ждем данные, если они есть то                UART_Read_Text(str, '/', 10); //Считываем то что есть, при наличии слеша                c:=StrToInt(str); //Преобразовываем из строки в числовую переменную "с"                Str_Cut_Left(str,1); //Обрезаем строку с лева на 1 символ                if (c>100) and (c<200) then iarr[0]:=StrToInt(str) ,,Перебираем варианты                else if (c>200) and (c<300) then iarr[1]:=StrToInt(str)                else if (c>300) and (c<400) then iarr[2]:=StrToInt(str)                else if (c>400) and (c<500) then iarr[3]:=StrToInt(str);            end; end;

В этой процедуре используются такие большие числа (200, 300 и т.д.), так  как формат приходящих данных следующий:

3. И самая большая процедура в программе (и единственная).

 procedure Light(); iv IVT_ADDR_TIMER1_COMPA; begin      TCNT1H:=0x00;      TCNT1L:=0x00;            PORTD7_bit:=1; //Здесь мы зажигаем....      delay_ms(10);      PORTD7_bit:=0; //И гасим светодиодик.      if inp<=3 then inc(inp) else inp:=0;       case inp of //Методом перебора выбираем нужное действие      0:begin             case iarr[inp] of //И снова перебор....                     1:        begin                                    PORTB:=0x01; //Тут отсылаем значение в порт В                                    PORTC:=0x1e; //То же тольков порт С                               end;                     2:        begin                                    PORTB:=0x01;                                    PORTC:=0x1d;                               end;                     3:        begin                                    PORTB:=0x01;                                    PORTC:=0x1b;                               end;                     4:        begin                                    PORTB:=0x01;                                    PORTC:=0x17;                               end;                     5:        begin                                    PORTB:=0x02;                                    PORTC:=0x0f;                               end;                     6:        begin                                    PORTB:=0x02;                                    PORTC:=0x1e;                               end;                     7:        begin                                    PORTB:=0x02;                                    PORTC:=0x1d;                               end;                     8:        begin                                    PORTB:=0x02;                                    PORTC:=0x1b;                               end;                     9:        begin                                    PORTB:=0x02;                                    PORTC:=0x17;                               end;                     10:        begin                                    PORTB:=0x02;                                    PORTC:=0x0f;                               end;             end;             delay_ms(30); //И задержка в 30 с , что бы было видно, что диод светится.      end;      1:begin             case iarr[inp] of                     1:        begin                                    PORTB:=0x04;                                    PORTC:=0x1e;                               end;                     2:        begin                                    PORTB:=0x04;                                    PORTC:=0x1d;                               end;                     3:        begin                                    PORTB:=0x04;                                    PORTC:=0x1b;                               end;                     4:        begin                                    PORTB:=0x04;                                    PORTC:=0x17;                               end;                     5:        begin                                    PORTB:=0x04;                                    PORTC:=0x0f;                               end;                     6:        begin                                    PORTB:=0x08;                                    PORTC:=0x1e;                               end;                     7:        begin                                    PORTB:=0x08;                                    PORTC:=0x1d;                               end;                     8:        begin                                    PORTB:=0x08;                                    PORTC:=0x1b;                               end;                     9:        begin                                    PORTB:=0x08;                                    PORTC:=0x17;                               end;                     10:        begin                                    PORTB:=0x08;                                    PORTC:=0x0f;                               end;             end;             delay_ms(30);      end;      2:begin             case iarr[inp] of                     1:        begin                                    PORTB:=0x10;                                    PORTC:=0x1e;                               end;                     2:        begin                                    PORTB:=0x10;                                    PORTC:=0x1d;                               end;                     3:        begin                                    PORTB:=0x10;                                    PORTC:=0x1b;                               end;                     4:        begin                                    PORTB:=0x10;                                    PORTC:=0x17;                               end;                     5:        begin                                    PORTB:=0x10;                                    PORTC:=0x0f;                               end;                     6:        begin                                    PORTB:=0x20;                                    PORTC:=0x1e;                               end;                     7:        begin                                    PORTB:=0x20;                                    PORTC:=0x1d;                               end;                     8:        begin                                    PORTB:=0x20;                                    PORTC:=0x1b;                               end;                     9:        begin                                    PORTB:=0x20;                                    PORTC:=0x17;                               end;                     10:        begin                                    PORTB:=0x20;                                    PORTC:=0x0f;                               end;             end;             delay_ms(30);      end;      3:begin             case iarr[inp] of                     1:        begin                                    PORTB:=0x40;                                    PORTC:=0x1e;                               end;                     2:        begin                                    PORTB:=0x40;                                    PORTC:=0x1d;                               end;                     3:        begin                                    PORTB:=0x40;                                    PORTC:=0x1b;                               end;                     4:        begin                                    PORTB:=0x40;                                    PORTC:=0x17;                               end;                     5:        begin                                    PORTB:=0x40;                                    PORTC:=0x0f;                               end;                     6:        begin                                    PORTB:=0x80;                                    PORTC:=0x1e;                               end;                     7:        begin                                    PORTB:=0x80;                                    PORTC:=0x1d;                               end;                     8:        begin                                    PORTB:=0x80;                                    PORTC:=0x1b;                               end;                     9:        begin                                    PORTB:=0x80;                                    PORTC:=0x17;                               end;                     10:        begin                                    PORTB:=0x80;                                    PORTC:=0x0f;                               end;             end;             delay_ms(30);      end;      end; end;

Я использовал столько операторов case, так как тут они (на мой взгляд) лучше подходят. Эта процедура отвечает за динамическое обновление картинки (именно по этому и назвал ее Light(); ).

Таймер срабатывает 20 раз в секунду. Это можно посчитать по формуле c(циклов):=31250*t(c). Где с(циклов) — то самое число, которое мы записываем в регистр сравнения. Кстати, в mikroPascal есть полезная утилитка Quick Converter, с помощью которой можно перевести это число из десятичной системы в шестнадцатиричную.Внимание! Формула справедлива только для частоты 8 МГц и предделителя установленного на 256.

Так как у нас четыре столбца светодиодов, то картинка в секунду успевает обновиться 5 раз. Я считаю что этого вполне достаточно, но при желании можно увеличить частоту регенерации «изображения».

Кстати, а вот и схема:

Это устройство в Proteus’e работает, но почему-то частота переключения светодиодов слишком низкая. Скорее всего программа не хочет симулировать работу схемы в реальном времени. После небольших доработок эту схему вполне можно «оживить», что я и планирую сделать в будущем (когда время появится). Но еще раз хочу заметить, это — не готовое устройство, а только «наглядное пособие». До реального прототипа его еще доделывать нужно.

Спасибо всем кто читал мою писанину! Буду рад всем замечаниям и пожеланиям!

Полезные ресурсы:
http://www.gaw.ru/html.cgi/txt/doc/micros/avr/index.htm
http://www.linker.ru/node/1387
http://radiokot.ru/start/mcu_fpga/avr/11/