Методическое пособие 539
.pdfсчитываются в последовательный порт и создается иллюзия ввода данных с клавиатуры. То же самое касается и вывода данных в последовательный порт программой микроконтроллера, когда визуально данные появляются на экране, хотя фактически они выводятся в последовательный порт.
Для демонстрации возможностей отладчика среды Keil uVision разработаем несколько простых программных проектов, в каждом из которых продемонстрируем те или иные методы симуляции и отладки.
#include <stdio.h> #include <REG52.H> sbit Bit0 = P1^0; void main(void)
{
SCON = 0x50; TH1 = 0xFD; TMOD |= 0x20; TR1 = 1;
TI = 1; while (1)
{
printf(“Press Enter to reverse a bit P1.0\n”); getchar();
Bit0 = ~Bit0;
}
}
При вводе символа с последовательного порта инвертируется бит 0 порта P1. Выполним сборку программы и запустим ее на выполнение через меню Debug →Start/Stop Debug Session → Run. Мы хотим посмотреть, как работает ввод символов через последовательный порт, и проверить состояние бита 0 порта P1. Чтобы просмотреть прием символов с последовательного порта с помощью функции getchar, выберем
51
меню View → Serial Window →UART #0. Желательно посмотреть, будет ли инвертироваться бит 0. Для этого нужно в меню Peripherals →I/O–Ports выбрать опцию Port 1 (рис. 2.2).
Рис. 2.2. Выбор порта ввода/вывода
После этого появится небольшое диалоговое окно с обозначениями выводов порта P1, которые будут отображать состояние битов (рис. 2.3).
Рис. 2.3. Индикация состояния выводов порта Р1
52
Теперь, если перейти в окно UART #0 и нажать несколько раз клавишу Enter, то можно наблюдать изменения в нулевом бите (рис. 2.4).
Рис. 2.4. Индикация ввода/вывода программы и состояния порта
Рассмотрим, как симулируется работа прерывания INT0 в Keil uVision. Откомпилируем и скомпонуем следующую программу.
#include <stdio.h> #include <REG52.H> sbit Bit0 = P1^0;
void INT0Isr (void) interrupt 0 using 1 { EX0 = 0;
53
Bit0 = ~Bit0;
printf(“Interrupt 0 occured.\n”);
EX0 = 1;
}
void main(void)
{
SCON = 0x50; TH1 = 0xFD; TMOD |= 0x20; TR1 = 1;
TI = 1;
IT0 = 1;
EX0 = 1;
EA = 1; while (1);
}
В этой программе используется программа-обработчик внешнего прерывания 0 (INT0). Каждый раз при возникновении прерывания в последовательный порт выводится соответствующее сообщение и инвертируется бит 0 порта P1. Поскольку вывод данных в последовательный порт занимает относительно длительное время, в течение которого могут произойти новые прерывания, то на время передачи строки функцией printf внешнее прерывание 0 блокируется.
Для симуляции работы программы помимо двух окон отладки, используемых в предыдущем примере, нужно выбрать меню Peripherals →Interrupt (рис. 2.5).
54
Рис. 2.5. Симуляция работы прерывания
Обратите внимание на диалоговое окно Interrupt System. Если выбрать строку P3.2/Int0, то внизу появится строка устанавливаемых флагов для данного прерывания. Поскольку внешнее прерывание 0 может вызываться при установке флага IE0, то для моделирования возникновения/прекращения прерываний можно ставить или снимать отметку на флажке IE0 (рис. 2.6).
55
Рис. 2.6. Имитация вызова прерывания
При вызове прерывания будет изменяться и значение бита P1.0, что можно наблюдать в диалоговом окне Parallel Port
1.
При работе с портами ввода/вывода следует учитывать аппаратные особенности микроконтроллеров 8051, связанные с записью/чтением. Для записи данных в порт нужно просто выполнить оператор присваивания (в языке C), возможно, в комбинации с логическими побитовыми операциями (если требуется оперировать с отдельными битами). Можно оперировать с отдельными битами порта, для удобства присвоив им какое-либо имя. Например, для установки бита P1.7 в единичное состояние можно выполнить оператор
P1 |= 0x80;
При этом остальные биты порта P1 остаются нетронутыми. Для той же цели можно использовать и другой подход, назначив биту P1.7 переменную, после чего просто присваивать этой переменной нужное значение, например:
56
sbit bit0 = P1.7;
. . .
bit0 = 1;
Этот фрагмент программного кода выполняет те же действия, что и предыдущий оператор, устанавливая бит P1.7 в единичное состояние. Чтение данных с портов ввода/вывода отличается от записи тем, что предварительно в бит, значение которого нужно прочитать, записывается 1. В этом случае данные будут считаны не с внутреннего регистра-защелки данного бита, а с вывода порта микроконтроллера.
Для иллюстрации чтения/записи через порты ввода/вывода рассмотрим пример программы, исходный текст которой приведен ниже:
#include <stdio.h> #include <REG52.H> sbit Bit0 = P1^7;
void INT0Isr (void) interrupt 0 using 1 { EX0 = 0;
if (Bit0)
printf(“Bit P1.7 is set.\n”); else
printf(“Bit P1.7 is reset.\n”);
EX0 = 1;
}
void main(void)
{
SCON = 0x50; TH1 = 0xFD; TMOD |= 0x20; TR1 = 1;
TI = 1;
IT0 = 1;
EX0 = 1;
57
EA = 1; while (1);
}
Здесь при вызове прерывания INT0 считывается значение бита P1.7 с вывода микроконтроллера. Если скомпилировать эту программу и запустить ее в отладчике Keil, то при записанной в регистре-защелке P1.7 единице значение бита можно прочитать; если же в защелке записан 0, бит порта работает как выходной, поэтому данные, считанные с вывода P1.7 (не с защелки!) могут быть некорректными. Симулятор Keil отслеживает подобные ситуации, выдавая сообщение
(рис. 2.7).
Рис. 2.7. Моделирование ошибочной ситуации
58
В большинстве программ используются переменные различных типов, и требуется отслеживать корректность их значений при выполнении программного кода. В следующем примере мы посмотрим, как отслеживать значения переменных программы при отладке программы на симуляторе Keil.
Откомпилируем и запустим в симуляторе программу, исходный текст которой сохранен в файле debug_memo.c и имеет вид:
#include <stdio.h> #include <REG52.H> idata char COUNTER = 0;
void INT0Isr (void) interrupt 0 using 1 { COUNTER++;
if (COUNTER == 11) COUNTER = 0;
}
void main(void)
{
SCON = 0x50; TH1 = 0xFD; TMOD |= 0x20; TR1 = 1;
TI = 1;
IT0 = 1;
EX0 = 1;
EA = 1; while (1);
}
Предположим, что нужно наблюдать за изменениями переменной COUNTER. Откомпилируем исходный текст программы и запустим на отладку абсолютный объектный модуль. В меню View выберем опции Memory Window и Symbol Window (рис. 2.8).
59
Рис. 2.8. Симуляция работы с памятью
В области Symbols найдем адрес переменной COUNTER, значение которой нужно отслеживать, и введем его в область Memory в правом нижнем углу (рис. 2.9).
60