Разберем назначение отдельных элементов программы. В первой строке записана директива препроцессора компилятора #include, которая предписывает присоединить к программе заголовочный файл с именем 68НС12ВЗ2.h. Содержимое этого файла мы рассмотрим ниже. Строка 3 содержит объявление функции TOIISR как подпрограммы прерывания по переполнению таймера. Строки с пятой по девятую содержат директивы, которые назначают ячейку памяти с адресом 0x0B1E для размещения в ней адреса начала подпрограммы прерывания TOIISR. В строке 10 осуществляется определение и инициализация глобальной переменной с именем second, которая будет использоваться в качестве программного счетчика. Строки с одиннадцатой по двадцатую содержат текст основной программы, в которой происходит инициализация подсистемы таймера. Таймер запускается на счет, разрешаются прерывания по его переполнению. Обратите внимание, в строке 17 вызывается макрос разрешения прерывания, который был определен в заголовочном файле. Строка 18 содержит конструкцию бесконечного цикла, который обеспечивает выполнение пустых команд микроконтроллером, пока не поступит запрос на прерывание от таймера. В строке 19 записан макрос программного прерывания EXIT, который также определен в заголовочной файле. В строке 22 начинается подпрограмма прерывания по таймеру. В ней сбрасывается флаг переполнения таймера (строка 24), а затем инкрементируется программный счетчик second (строка 25). Заметим, что период счета 16 разрядного счетчика таймера микроконтроллера 68HC12 при частоте шины 8 МГц составляет 8,19 мс. Поэтому для отсчета 1 с требуется 122 периода переполнения этого таймера. В строке 26 записана конструкция условия if. Выражения строк 28 и 29 будут исполняться, только если счетчик second достиг значения 122. Тогда код на линиях PORTA будет инвертирован, а содержимое программного счетчика обнулено.
Обратимся теперь к разъяснению содержимого заголовочного файла. Мы не будем приводить его текст целиком, а приведем лишь те строки, которые необходимы для рассматриваемой в основном примере программы:
1 #define _IO_BASE 0
2 #define _P(off) *(unsigned char volatile*) (_IO_BASE + off)
3 #define TSCR _Р(0х86)
4 #define TMSK2 _Р(0х8D)
5 #define TFLG2 _P(0x8F)
6 #define DDRA _Р(0х02)
7 #define PORTA _Р(0х00)
8 #define CLI asm("cli\n")
9 #define EXIT asm("swi\n")
Две первые строки приведенного фрагмента заголовочного файла используются для определения макроса _P с аргументом off. Обратите внимание на символ указателя в макросе. Все следующие выражения в строках с 3 по 7 определяют численные значения для символьных обозначений регистров специальных функций МК. Эти численные значения — адреса регистров в соответствии с картой памяти МК. Любое упоминание имен регистров в тексте программы связано с выполнением операций чтения или записи в эти регистры по их физическим адресам. Этим объясняется необходимость применения указателя в определении макроса _P(off). Две последние строки 8 и 9 являются примерами определения макросов.
Вернемся к примеру управления светодиодами. После обработки программой компилятора исходного текста программы Sample.c будет получен следующий текст программы на языке ассемблера.
1 .module interrupt.c
2 .area memory(abs)
3 .org 0xb1e
4 _Timer_Overflow_interrupt_vector::
5 .word _TOIISR
6 .area data
7 _second::
8 .blkb 1
9 .area idata
10 .byte 0
11 .area data
12 .area text _main::
14 ; void TOIISR(void);
15 ; #pragma interrupt_handler TOIISR ;
16 ; #pragma abs_address:0x0B1E
17 ; void (*Timer_Overflow_interrupt_vector[]) ={TOIISR};
18 ; #pragma end_abs_address ;
19 ; unsigned char second=0x00;
20 ;
21 ;void main(void)
22 ;{
23 ; TSCR=0x80;
24 ldab #128
25 stab 0х86
26 ; ТМSК2=0х80;
27 ldab #128
28 stab 0x8d
29 ; TFLG2=0x80;
30 ldab #128
31 stab 0x8f
32 ; DDRA=0xFF;
33 ldab #255
34 stab 0х2
35 ; CLI;
36 сli
37 L3: L4:
38 bra L3
39 X0:
40 ; while(1) {};
41 ; EXIT;
42 swi
43 ; }L2
44 .dbline 0
45 ; func end
46 rts
47 _TOIISR: :
48 void TOIISR(void)
49 ; {
50 ; TFLG2=0x80;