691_Mikushin_A.V._Programmirovanie_mikroprotsessorov_
.pdfТеперь можно заняться реализацией подпрограммы опроса кнопок. Для хранения состояния кнопок в этой программе пришлось ввести новую переменную. Её задача состоит в копировании логических уровней с выводов порта во внутреннюю переменную микроконтроллера SostKn. Так как это будет проис-
ходить быстро и с постоянной задержкой по времени, то время взятия отсчетов (а также время вывода на индикаторы) будет строго постоянным.
Исходный текст подпрограммы опроса кнопок приведён в листинге 15.
Листинг 15. Исходный текст подпрограммы опроса кнопок.
sbit P1_7 = 0x97; sbit P2_7 = 0xa7; sbit P3_7 = 0xb7;
unsigned char SostKn=0;
/********************************************************************
Подпрограмма опроса кнопок
********************************************************************/
void OprosKnop(void) {if(P1_7)
SostKn|=1; else
SostKn&=~1; if(P2_7)
SostKn|=2; else
SostKn&=~2; if(P3_7)
SostKn|=4; else
SostKn&=~4;
}
В этой подпрограмме кнопки, разнесённые по трем различным портам, сводятся в одну переменную SostKn. Для опроса состояния кнопок установки времени используется условный оператор if. При помощи этого оператора опрашивается состояние логического уровня на входах седьмого вывода портов P1, P2 и P3. Объединение сигналов производится при помощи команд логического ‘И’ и ‘ИЛИ’.
При обнаружении на выводе порта логической единицы, в соответствующий бит переменной SostKn записывается логическая единица. Запись производится при помощи оператора логического сложения с константой, соответствующей логической единице в заданном бите.
При обнаружении на выводе порта логического нуля, в соответствующий бит переменной SostKn записывается логический ноль. Запись производится при помощи оператора логического умножения с константой, содержащей логический ноль в заданном бите. Константа вычисляется на этапе трансляции программы при помощи одноместной операции побитовой инверсии ‘~’.
Исполнение команд, вводимых при помощи кнопок, можно осуществить в любом месте основного цикла программы. Это обеспечит время исполнения команды в пределах 50мс. Точно так же, как и в предыдущих случаях, выделим
121
это действие в отдельную подпрограмму. Пусть эта подпрограмма называется
CorrClock.
Первое действие, которое мы выполним в подпрограмме CorrClock – это обработаем команду, вводимую кнопкой установка секунд. По этой команде нужно просто обнулить счётчик секунд. Команду будем получать через глобальную переменную состояния кнопок SostKn. Исходный текст подпрограммы, выполняющей данное действие, приведен в листинге 16.
Листинг 16. Исходный текст первого варианта подпрограммы коррекции внутреннего состояния часов.
/********************************************************************
Подпрограмма блока коррекции часов
********************************************************************/
void CorrClock(void)
{if((SostKn&1)==0) //Если нажата кнопка "уст сек",
SEC=60; |
//то обнулить счетчик секунд |
}
Обработка нажатия на кнопки “уст мин” и “уст часов” производится несколько иначе. Дело в том, что по общепринятому алгоритму коррекции показаний часов при нажатии на эти кнопки необходимо непрерывно увеличивать показания счётчика часов или минут. Предварительный делитель начинает счет от числа 20, которое в двоичном представлении выглядит как 101002. То, что в младших разрядах записаны нулевые значения означает, что эти разряды будут меняться по двоичному закону, и единица в этих разрядах будет появляться через одинаковые промежутки времени.
Например, в младшем разряде единица будет появляться один раз за 100 мс, то есть десять раз в секунду. В следующем разряде единица будет появляться один раз за 200 мс, то есть пять раз в секунду. В третьем же разряде появление единицы будет неравномерным. Два раза единица в этом разряде появится через 400 мс, а один раз через 200 мс. Если использовать сигнал с этого бита для коррекции времени, то эта непериодичность коррекции показаний часов будет раздражать пользователя, поэтому такой вариант неприемлем.
Пусть изменение показаний часов происходит пять раз в секунду. Эта скорость изменения показаний часов ещё может восприниматься человеком. Подпрограмма коррекции показания часов будет выглядеть так, как это приведено в листинге 17.
Листинг 17. Исходный текст окончательного варианта подпрограммы коррекции внутреннего состояния часов.
/********************************************************************
Подпрограмма блока коррекции часов
********************************************************************/
void CorrClock(void)
{if((SostKn&1)==0) //Если нажата кнопка "уст сек",
122
SEC=60; |
//то |
обнулить счетчик секунд |
if((Delit&3)==2) |
//Если |
в предделителе число не кратно 2, |
return; |
//то |
выйти из подпрограммы коррекции времени |
if((SostKn&2)==0) //Если |
нажата кнопка "уст мин", |
|
IncMin(); |
//то |
увеличить содержимое счётчика минут |
if((SostKn&4)==0) //Если нажата кнопка "уст час", |
||
IncChas(); |
//то увеличить содержимое счётчика минут |
|
} |
|
|
В приведенном варианте программы проверяется два младших бита делителя. Для выделения двух младших бит используется логическое умножение содержимого переменной Delit с маской 00011b. Это число соответствует десятичному эквиваленту 310. Так как эти биты принимают значение 102 пять раз в секунду, то соответствующее количество раз в секунду будет вызываться подпрограмма увеличения значения содержимого счётчика минут (или часов).
Если скорость коррекции показаний часов пять раз в секунду покажется слишком высокой, то можно изменить соотношение коэффициентов деления таймера и делителя, например: 32 31250. Тогда можно будет выбрать скорость коррекции показания часов из набора вариантов два, четыре или восемь раз в секунду. Это обеспечивается двоичным характером коэффициента деления предварительного делителя, равного числу 32. В этом случае, так как изменился коэффициент деления таймера, один проход по основному циклу программы будет осуществляться за 31,25 мс. То есть время реакции часов увеличится. Правда, уменьшится и потребление тока от источника питания.
На этом можно завершить разработку часов. В заключение я хотел бы отметить, что ток потребления микроконтроллера на частоте 12 МГц в режиме пониженного энергопотребления составляет 6,5 мА. Это означает, что даже если отключить светодиодную индикацию, то батарейки с емкостью 300 мА/ч хватит на 46 часов работы. Отличные часы! Не правда ли?
Ток потребления можно снизить, применив вместо стандартного кварцевого резонатора часовой резонатор с частотой 32,768 кГц. При этом ток потребления микроконтроллера снизится в 366 раз и составит 18 мкА. При таком токе потребления той же самой батарейки хватит на 16666 часов или на 694 суток (почти два года работы). Есть разница? Однако при этом программа предварительного делителя будет немного сложнее.
Придется строить делитель с дробным коэффициентом деления. Такие делители обычно строят, переключая два коэффициента деления с определенным периодом, но я думаю, в данном случае это имеет смысл. Основная цель приведенного примера была не в разработке рабочего устройства, а в обучении создания и развития программного проекта на языке программирования C-51, а также иллюстрация его возможностей.
123
Заключение
Ну, вот мы и закончили краткий обзор возможностей языка программирования C-51. Конечно, в рамках одной книги невозможно рассмотреть все особенности применения языка программирования. Тем не менее, я сделал попытку рассмотреть основные особенности работы с микроконтроллерами на языке программирования высокого уровня.
При разработке обучающего примера с использованием языка программирования C-51 получилась программа объемом всего 369 байт. Это неплохой результат и для языка программирования ассемблер. Надеюсь, что это может поколебать позиции даже убежденных противников использования языков программирования высокого уровня при создании программ для микроконтроллеров.
Надеюсь, что после прочтения данной книги вы сможете свободно создавать и сопровождать программные проекты и для своих устройств.
Успехов вам!
124
Приложения
П.1. Система команд микроконтроллеров семейства MCS-51
Таблица П.1. Машинные команды, влияющие на значения флагов регистра PSW
|
|
Флаги |
|
|
Флаги |
||||
Мнемоника |
|
|
|
|
|
Мнемоника |
|
|
|
C |
|
OV |
|
AC |
C |
OV |
AC |
||
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
|
ADD |
+ |
|
+ |
|
+ |
CLR C |
0 |
|
|
|
|
|
|
|
|
|
|
|
|
ADDC |
+ |
|
+ |
|
+ |
CPL C |
+ |
|
|
|
|
|
|
|
|
|
|
|
|
SUBB |
+ |
|
+ |
|
+ |
ANL C, bit |
+ |
|
|
|
|
|
|
|
|
|
|
|
|
MUL |
0 |
|
+ |
|
|
ANL C, /bit |
|
|
|
|
|
|
|
|
|
|
|
|
|
DIV |
0 |
|
+ |
|
|
ORL C, bit |
+ |
|
|
|
|
|
|
|
|
|
|
|
|
DA |
+ |
|
|
|
|
ORL C, /bit |
+ |
|
|
|
|
|
|
|
|
|
|
|
|
RRC |
+ |
|
|
|
|
MOV C, bit |
+ |
|
|
|
|
|
|
|
|
|
|
|
|
RLC |
+ |
|
|
|
|
CJNE |
+ |
|
|
|
|
|
|
|
|
|
|
|
|
SETB C |
1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
125
Таблица П.2. Обозначения и символы, используемые при описании команд
Обозначение |
Назначение |
|
|
|
|
А |
Аккумулятор |
|
|
|
|
Rn |
Регистры текущего выбранного банка регистров |
|
|
|
|
R |
Номер загружаемого регистра, указанного в команде |
|
|
|
|
direct |
Прямоадресуемая 8-битовая ячейка памяти данных |
|
|
|
|
@Ri |
Косвенноадресуемая 8-битовая ячейка внутреннего |
|
ОЗУ данных |
||
|
||
|
|
|
data 8 |
8-битная константа, входящая в код операции (КОП) |
|
|
|
|
data 16 |
16-битная константа, входящая в код операции (КОП) |
|
|
|
|
data H |
Старшие биты (15-8) 16-битной константы |
|
|
|
|
data L |
Младшие биты (7-0) 16-битной константы |
|
|
|
|
adr 11 |
Младшие 11-бит адреса |
|
|
|
|
adr 16 |
Полный 16-битный адрес |
|
|
|
|
adr L |
Младшие биты адреса |
|
|
|
|
rel |
Байт смещения с учетом знака |
|
|
|
|
bit |
Адрес бита |
|
|
|
126
Таблица П.3. Перечень команд микроконтроллеров семейства MCS-51
Hex |
Чис- |
Число |
Мнем. |
|
|
|
|
ло |
цик- |
Операнды |
Описание команды |
||||
код |
код |
||||||
байт |
лов |
|
|
|
|||
|
|
|
|
|
|||
|
|
|
|
|
|
||
00 |
1 |
1 |
NOP |
|
Нет операции |
||
|
|
|
|
|
|
||
01 |
2 |
2 |
AJMP |
code addr (0 |
Безусловный переход |
||
|
|
|
|
стр) |
в пределах 2 кБ |
||
|
|
|
|
|
|
||
02 |
3 |
2 |
LJMP |
code addr |
Безусловный переход |
||
|
|
|
|
|
в пределах 64 кБ |
||
|
|
|
|
|
|
||
03 |
1 |
1 |
RR |
A |
Циклический сдвиг вправо |
||
|
|
|
|
|
|
||
04 |
1 |
1 |
INC |
A |
ACC ACC + 1 |
||
|
|
|
|
|
|
||
05 |
2 |
1 |
INC |
data addr |
data data + 1 |
||
|
|
|
|
|
|
|
|
06 |
1 |
1 |
INC |
@R0 |
(R0) |
(R0) + 1 |
|
|
|
|
|
|
|
|
|
07 |
1 |
1 |
INC |
@R1 |
(R1) |
(R1) + 1 |
|
|
|
|
|
|
|
|
|
08 |
1 |
1 |
INC |
R0 |
R0 |
R0 + 1 |
|
|
|
|
|
|
|
|
|
09 |
1 |
1 |
INC |
R1 |
R1 |
R1 + 1 |
|
|
|
|
|
|
|
|
|
0A |
1 |
1 |
INC |
R2 |
R2 |
R2 + 1 |
|
|
|
|
|
|
|
|
|
0B |
1 |
1 |
INC |
R3 |
R3 |
R3 + 1 |
|
|
|
|
|
|
|
|
|
0C |
1 |
1 |
INC |
R4 |
R4 |
R4 + 1 |
|
|
|
|
|
|
|
|
|
0D |
1 |
1 |
INC |
R5 |
R5 |
R5 + 1 |
|
|
|
|
|
|
|
|
|
0E |
1 |
1 |
INC |
R6 |
R6 |
R6 + 1 |
|
|
|
|
|
|
|
|
|
0F |
1 |
1 |
INC |
R7 |
R7 |
R7 + 1 |
|
|
|
|
|
|
|
||
10 |
3 |
2 |
JBC |
bitaddr, |
Усл. переход если бит = 1 и |
||
|
|
|
|
codeaddr |
сброс бита |
||
|
|
|
|
|
|
||
11 |
2 |
2 |
ACALL |
codeaddr |
Вызов подпрограммы |
||
|
|
|
|
(0 стр) |
в пределах 2 кБ |
||
|
|
|
|
|
|
||
12 |
3 |
2 |
LCALL |
Dataaddr |
Вызов подпрограммы |
||
|
|
|
|
|
в пределах 64 кБ |
||
|
|
|
|
|
|
||
13 |
1 |
1 |
RRC |
A |
Циклический сдвиг вправо |
||
|
|
|
|
|
через флаг C |
||
|
|
|
|
|
|
||
14 |
1 |
1 |
DEC |
A |
ACC ACC - 1 |
||
|
|
|
|
|
|
||
15 |
2 |
1 |
DEC |
dataaddr |
data data - 1 |
||
|
|
|
|
|
|
||
16 |
1 |
1 |
DEC |
@R0 |
(R0) (R0) - 1 |
||
|
|
|
|
|
|
|
127
17 |
1 |
1 |
DEC |
@R1 |
(R1) (R1) - 1 |
|
|
|
|
|
|
18 |
1 |
1 |
DEC |
R0 |
R0 R0-1 |
|
|
|
|
|
|
19 |
1 |
1 |
DEC |
R1 |
R1 R1-1 |
|
|
|
|
|
|
1A |
1 |
1 |
DEC |
R2 |
R2 R2-1 |
|
|
|
|
|
|
1B |
1 |
1 |
DEC |
R3 |
R3 R3-1 |
|
|
|
|
|
|
1C |
1 |
1 |
DEC |
R4 |
R4 R4-1 |
|
|
|
|
|
|
1D |
1 |
1 |
DEC |
R5 |
R5 R5-1 |
|
|
|
|
|
|
1E |
1 |
1 |
DEC |
R6 |
R6 R6-1 |
|
|
|
|
|
|
1F |
1 |
1 |
DEC |
R7 |
R7 R7-1 |
|
|
|
|
|
|
20 |
3 |
2 |
JB |
bit addr, data |
Условный переход если бит = |
|
|
|
|
addr |
1 |
|
|
|
|
|
|
21 |
2 |
2 |
AJMP |
code addr |
Безусловный переход в преде- |
|
|
|
|
(1 стр) |
лах 2 кБ |
|
|
|
|
|
|
22 |
1 |
2 |
RET |
|
Возвращение из подпрограм- |
|
|
|
|
|
мы |
|
|
|
|
|
|
23 |
1 |
1 |
RL |
A |
Циклический сдвиг влево |
|
|
|
|
|
|
24 |
2 |
1 |
ADD |
A, #data |
ACC ACC + число |
|
|
|
|
|
|
25 |
2 |
1 |
ADD |
A, data addr |
ACC ACC + data |
|
|
|
|
|
|
26 |
1 |
1 |
ADD |
A, @R0 |
ACC ACC + (R0) |
|
|
|
|
|
|
27 |
1 |
1 |
ADD |
A, @R1 |
ACC ACC + (R1) |
|
|
|
|
|
|
28 |
1 |
1 |
ADD |
A, R0 |
ACC ACC + R0 |
|
|
|
|
|
|
29 |
1 |
1 |
ADD |
A, R1 |
ACC ACC + R1 |
|
|
|
|
|
|
2A |
1 |
1 |
ADD |
A, R2 |
ACC ACC + R2 |
|
|
|
|
|
|
2B |
1 |
1 |
ADD |
A, R3 |
ACC ACC + R3 |
|
|
|
|
|
|
2C |
1 |
1 |
ADD |
A, R4 |
ACC ACC + R4 |
|
|
|
|
|
|
2D |
1 |
1 |
ADD |
A, R5 |
ACC ACC + R5 |
|
|
|
|
|
|
2E |
1 |
1 |
ADD |
A, R6 |
ACC ACC + R6 |
|
|
|
|
|
|
2F |
1 |
1 |
ADD |
A, R7 |
ACC ACC + R7 |
|
|
|
|
|
|
30 |
3 |
2 |
JNB |
bitaddr, |
Условный переход |
|
|
|
|
codeaddr |
если бит = 0 |
|
|
|
|
|
|
31 |
2 |
2 |
ACAL |
codeaddr |
Вызов подпрограммы |
|
|
|
L |
(1 стр) |
в пределах 2 кБ |
|
|
|
|
|
|
128
32 |
1 |
2 |
RETI |
|
Возвр. из подпр. обслужив. |
|
|
|
|
|
прерывания |
|
|
|
|
|
|
33 |
1 |
1 |
RLC |
A |
Циклический сдвиг влево |
|
|
|
|
|
через флаг C |
|
|
|
|
|
|
34 |
2 |
1 |
ADDC |
A, #data |
ACC ACC + число + C |
|
|
|
|
|
|
35 |
2 |
1 |
ADDC |
A, dataaddr |
ACC ACC + data + C |
|
|
|
|
|
|
36 |
1 |
1 |
ADDC |
A, @R0 |
ACC ACC + (R0) + C |
|
|
|
|
|
|
37 |
1 |
1 |
ADDC |
A, @R1 |
ACC ACC + (R1) + C |
|
|
|
|
|
|
38 |
1 |
1 |
ADDC |
A, R0 |
ACC ACC + R0+ C |
|
|
|
|
|
|
39 |
1 |
1 |
ADDC |
A, R1 |
ACC ACC + R1+ C |
|
|
|
|
|
|
3A |
1 |
1 |
ADDC |
A, R2 |
ACC ACC + R2+ C |
|
|
|
|
|
|
3B |
1 |
1 |
ADDC |
A, R3 |
ACC ACC + R3+ C |
|
|
|
|
|
|
3C |
1 |
1 |
ADDC |
A, R4 |
ACC ACC + R4+ C |
|
|
|
|
|
|
3D |
1 |
1 |
ADDC |
A, R5 |
ACC ACC + R5+ C |
|
|
|
|
|
|
3E |
1 |
1 |
ADDC |
A, R6 |
ACC ACC + R6+ C |
|
|
|
|
|
|
3F |
1 |
1 |
ADDC |
A, R7 |
ACC ACC + R7+ C |
|
|
|
|
|
|
40 |
2 |
2 |
JC |
codeaddr |
Условный переход если флаг |
|
|
|
|
|
C = 1 |
|
|
|
|
|
|
41 |
2 |
2 |
AJMP |
codeaddr (2 |
Безусловный переход в пре- |
|
|
|
|
стр) |
делах 2 кБ |
|
|
|
|
|
|
42 |
2 |
1 |
ORL |
dataaddr, A |
data data | ACC |
|
|
|
|
|
|
43 |
3 |
2 |
ORL |
dataaddr, #data |
data data | число |
|
|
|
|
|
|
44 |
2 |
1 |
ORL |
A, #data |
ACC ACC | число |
|
|
|
|
|
|
45 |
2 |
1 |
ORL |
A, data addr |
ACC ACC | data |
|
|
|
|
|
|
46 |
1 |
1 |
ORL |
A, @R0 |
ACC ACC | (R0) |
|
|
|
|
|
|
47 |
1 |
1 |
ORL |
A, @R1 |
ACC ACC | (R1) |
|
|
|
|
|
|
48 |
1 |
1 |
ORL |
A, R0 |
ACC ACC | R0 |
|
|
|
|
|
|
49 |
1 |
1 |
ORL |
A, R1 |
ACC ACC | R1 |
|
|
|
|
|
|
4A |
1 |
1 |
ORL |
A, R2 |
ACC ACC | R2 |
|
|
|
|
|
|
4B |
1 |
1 |
ORL |
A, R3 |
ACC ACC | R3 |
|
|
|
|
|
|
4C |
1 |
1 |
ORL |
A, R4 |
ACC ACC | R4 |
|
|
|
|
|
|
129
Hex |
Число |
Число |
Мнем. |
|
|
цик- |
Операнды |
Описание команды |
|||
код |
байт |
лов |
код |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4D |
1 |
1 |
ORL |
A, R5 |
ACC ACC | R5 |
|
|
|
|
|
|
4E |
1 |
1 |
ORL |
A, R6 |
ACC ACC | R6 |
|
|
|
|
|
|
4F |
1 |
1 |
ORL |
A, R7 |
ACC ACC | R7 |
|
|
|
|
|
|
50 |
2 |
2 |
JNC |
code addr |
Условный переход |
|
|
|
|
|
если флаг C = 0 |
|
|
|
|
|
|
51 |
2 |
2 |
ACAL |
code addr |
Вызов подпрограммы |
|
|
|
L |
(2 стр) |
в пределах 2 кБ |
|
|
|
|
|
|
52 |
2 |
1 |
ANL |
data addr, A |
data data & ACC |
|
|
|
|
|
|
53 |
3 |
2 |
ANL |
data addr, |
data data & число |
|
|
|
|
#data |
|
|
|
|
|
|
|
54 |
2 |
1 |
ANL |
A, #data |
ACC ACC & число |
|
|
|
|
|
|
55 |
2 |
1 |
ANL |
A, data addr |
ACC ACC & data |
|
|
|
|
|
|
56 |
1 |
1 |
ANL |
A, @R0 |
ACC ACC & (R0) |
|
|
|
|
|
|
57 |
1 |
1 |
ANL |
A, @R1 |
ACC ACC & (R1) |
|
|
|
|
|
|
58 |
1 |
1 |
ANL |
A, R0 |
ACC ACC & R0 |
|
|
|
|
|
|
59 |
1 |
1 |
ANL |
A, R1 |
ACC ACC & R1 |
|
|
|
|
|
|
5A |
1 |
1 |
ANL |
A, R2 |
ACC ACC & R2 |
|
|
|
|
|
|
5B |
1 |
1 |
ANL |
A, R3 |
ACC ACC & R3 |
|
|
|
|
|
|
5C |
1 |
1 |
ANL |
A, R4 |
ACC ACC & R4 |
|
|
|
|
|
|
5D |
1 |
1 |
ANL |
A, R5 |
ACC ACC R5 |
|
|
|
|
|
|
5E |
1 |
1 |
ANL |
A, R6 |
ACC ACC R6 |
|
|
|
|
|
|
5F |
1 |
1 |
ANL |
A, R7 |
ACC ACC R7 |
|
|
|
|
|
|
60 |
2 |
2 |
JZ |
codeaddr |
если ACC=0 то PC= codeaddr |
|
|
|
|
|
иначе PC+2 |
|
|
|
|
|
|
61 |
2 |
2 |
AJMP |
codeaddr |
Безусловный переход |
|
|
|
|
(3 стр) |
в пределах 2 кБ |
|
|
|
|
|
|
62 |
2 |
1 |
XRL |
dataaddr, A |
data data ACC |
|
|
|
|
|
|
63 |
3 |
2 |
XRL |
dataaddr, #data |
data data число |
|
|
|
|
|
|
64 |
2 |
1 |
XRL |
A, #data |
ACC ACC число |
|
|
|
|
|
|
65 |
2 |
1 |
XRL |
A, data addr |
ACC ACC data |
|
|
|
|
|
|
130