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 странный. Просто он состоит из 2-х восьмибитных регистров. В итоге его разрядность составляет 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/


Прикрепленные файлы:

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

Ваш адрес email не будет опубликован.