Ну вот, после очень большого перерыва я наконец что-то смог сделать. Эта третья по счету статья о среде программирования 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/
Прикрепленные файлы:
- iCPU_ mikroPascal les_ 3.rar (213 Кб)
Комментарии (2) | Я собрал (0) | Подписаться
Для добавления Вашей сборки необходима регистрация
uses AADL;
Он присутствует у тебя в этом и других уроках. У меня mikropascal v6 не находит этот модуль. Пишет