Ну вот, после очень большого перерыва я наконец что-то смог сделать. Эта третья по счету статья о среде программирования 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/
Прикрепленные файлы:
- iCPU_ mikroPascal les_ 3.rar (213 Кб)