
- •Проектирование микропроцессорных систем на базе учебного стенда sdk 1.1
- •Введение
- •1. Описание компонентов мп системы.
- •Микропроцессорный стенд sdk - 1.1.
- •Микроконтроллер aDuC812
- •Плис max3064
- •Дискретные входы-выходы
- •Аналоговые входы-выходы
- •Инструкция по работе с программой
- •Обзор основных свойств, методов, событий компоненты ComPort
- •4. Датчики
- •5. Пример 1
- •Расчет параметров технологического процесса
- •5.2. Описание функций для работы с sdk 1.1
- •5.3. Алгоритм программы для sdk
- •5.4. Программа для sdk
- •5.5. Интерфейс программы для пк
- •5.6. Алгоритм программы для пк
- •5.7. Программа для пк
- •6. Пример 2
- •6.1. Алгоритм работы системы
- •6.2. Имитация датчиков
- •6.3. Расчет параметров технологического процесса
- •6.4. Программа для работы микропроцессорного контроллера на языке Ассемблер
- •6.5. Программа для работы цап и ацп на языке c
- •6.6. Программа для персонального компьютера на языке с в среде c-Builder
- •6.7. Интерфейс программы для пк
- •7. Графическая часть
- •Sdk 1.1
- •5.2. Описание функций для работы с sdk 1.1 …………………... 21
- •5.4. Программа для sdk ………………………………………….. 35
- •5.5. Интерфейс программы для пк ………………………………. 41
- •5.6. Алгоритм программы для пк ………………………………... 43
5.2. Описание функций для работы с sdk 1.1
В программе для СДК мы будем использовать готовые функции, написанные на языке С, для работы с различными компонентами СДК. Также нам понадобятся два файла с набором констант.
Файл ADUC812.h содержит адреса всех регистров микроконтроллера. Этот файл находится в папке Keil/C51/INC/ADI.
Файл ser_inc.h содержит набор констант и команд. Вот его содержимое:
#define TRUE 1 //логическая 1
#define FALSE 0//логический 0
#define MAXBASE 0x08//8-ая страница памяти
#define RAM_TABLE 0x2000//базовый адрес пользовательской таблицы векторов прерываний
#define LJMP 0x2//код команды LJMP
// Регистры PLIS MAX
#define KB 0x0000//регистр клавиатуры
#define EXT_LO 0x0002//
#define EXT_HI 0x0003//
#define ENA 0x0004//регистр звука
#define SV 0x0007//р.светодиодов
#define DATA_IND 0x0001//р.данных ЖКИ
#define C_IND 0x0006//р.команд ЖКИ
//---
// Константы для LCD
//--Команды для контроллера LCD
#define CLEAR 0x01 //requires delay cylce of min 57 NOPs
#define HOME 0x02
#define ENTRY_MODE 0x04
#define DISPLAY_CTRL 0x08
#define SHIFT 0x10
#define FUNCTION_SET 0x20
#define RAM_CG 0x40
#define RAM_DD 0x80
#define BF_AC 0x80 //Read Busy flag (DB7) and AC (DB6-0)
//--Опции для этих команд
#define INCR 0x02
#define DISPLAY_SHIFT 0x01
#define DISPLAY_ON 0x04
#define CURSOR_ON 0x02
#define BLINK 0x01
#define DISPLAY 0x08
#define RIGHT 0x04
#define EIGHT_BITS 0x10
#define TWO_LINE 0x08
//--
// Константы COM-порта
#define BUFFERLEN 16//размер буфера
#define S9600 0xFD//скорость передачи данных
#define S4800 0xFA
#define S2400 0xF4
#define S1200 0xE8
//--
// Константы для ЦАП и АЦП
#define VDD 5.0
#define BUSY 0x80 //АЦП производит преобразование
#define DAC0 0 // Каналы ЦАП
#define DAC1 1 //
#define ON 1
#define OFF 0
Функция записи в регистры ПЛИС
void WriteMax(unsigned char xdata *regnum, unsigned char val)
{
unsigned char oldDPP=DPP;//сохраняем текущую страницу памяти
DPP=MAXBASE;//переключаемся на 8-ую страницу памяти
*regnum=val;//заносим в регистр байт информации
DPP=oldDPP;//восстанавливаем значение страничного регистра
}
regnum – один из семи регистров ПЛИС.
val – число, которое нужно записать в regnum.
DPP – страничный регистр.
Функция чтения из регистров ПЛИС
unsigned char ReadMax(unsigned char xdata *regnum)
{
unsigned char oldDPP=DPP; //сохраняем текущую страницу памяти
unsigned char val=0;//обнуляем локальную переменную val
DPP=MAXBASE; //переключаемся на 8-ую страницу памяти
val=*regnum;// считываем байт из регистра ПЛИС
DPP=oldDPP; //восстанавливаем значение страничного регистра
return val;//возвращаем считанный байт в точку вызова данной функции
}
Функция инициализации СДК
void Initilization(void)
{
DACCON = 0x7F;//настройка ЦАП
ADCCON1 = 0xAC;// настройка АЦП
InitLCD();//инициализация ЖКИ
MyInitSIO();//инициализация СОМ-порта
SetVect(0x04,(void code*)COM_ISR);// установка вектора прерывания от СОМ-порта
IE=0x10;//разрешаем прерывания от СОМ-порта
EA=TRUE;//разрешаем окончательно
}
Функция воспроизведения звукового сигнала СДК
void Buzz(void)
{
int dur,i;//локальные переменные dur – длительность звучания
for(dur = 0; dur < 8000; dur++)//основной цикл
{
WriteMax(ENA,0x3C);//подаем ток на пьезоэлектрический элемент
for(i=0; i < 25; i++)continue;//задержка
WriteMax(ENA,0x20);//снимаем ток
for(i=0; i < 25; i++)continue;//задержка
}
}
Функция управления светодиодами СДК
void Svetik(bit tetrad)
{
int j,u;//переменные для циклов
if(tetrad)//если tetrad=1 то светодиоды будут загораться потетрадно
{
for(j=0;j<50;j++)//основной цикл
{
WriteMax(SV,0x0F);//зажигаем первые 4 светодиода
for(u=0;u<31000;u++) continue;//задержка
WriteMax(SV,0xF0);//зажигаем другие 4 светодиода
for(u=0;u<31000;u++) continue;//задержка
}
}else
{
WriteMax(SV,i);// зажигаем светодиоды
i++; i – инкремент глобальной переменной(определяет какие светодиоды горят)
}
}
Функция работы с клавиатурой СДК
При работе с клавиатурой СДК используются:
Массив KBTable, который содержит символы клавишСДК
char KBTable[]="147*2580369#ABCD";
Глобальная переменная symbol_from_kb, в ней находится символ нажатой клавишы после опроса клавиатуры.
char* symbol_from_kb;
bit ScanKBOnce(char *ch)//Опрос клавиатуры
{
unsigned char row,col,rownum,colnum;
unsigned int i;
for(colnum = 0; colnum < 4; colnum++)//цикл по столбцам
{
col = 0x1 << colnum; //Формируем 0001,0010,0100,1000,0001,...
WriteMax(KB,~col);//Заносим в регистр клавиатуры 11111110,11111101,11111011,11110111,11111110,...
for(rownum = 0; rownum < 4; rownum++)//цикл по строкам
{
row = ReadMax(KB) & (0x10 << rownum);//считываем из регистра клавиатуры
if( !row ) // если клавиша нажата
{
for(i = 0; i<10000; i++)continue;//задержка из-за дребезга контакта
row = ReadMax(KB) & (0x10 << rownum);// опять считываем из регистра клавиатуры
if( !row )// теперь клавиша точно нажата
{
*ch = (KBTable[(colnum<<2) + rownum]);//в ch символ нажатой клавиши.
return 1; // успешный выход из функции
}
}
}
}
return 0; // функция ScanKBOnce возвращает 0 если клавиша не нажата
}
Функции для работы с СОМ-портом СДК
void WSio(unsigned char Sym)//запись символа в порт
{
SBUF=Sym;//помещаем символ Sym в регистр SBUF
TI=0;// сбрасываем флаг TI
while(!TI);//ждем окончания передачи символа
TI=0;// сбрасываем флаг TI
}
unsigned char RSio(void)//чтение символа из порта
{
while(!RI);//ждем окончания приема символа
RI=0;//сбрасываем флаг RI
return SBUF;//возвращаем символ
}
void Type(char * Str)//запись строки в порт
{
while(*Str)//пока не дойдем до конца строки идет запись в порт
WSio(*Str++);//запись символа в порт и переход к следующему символу в строке
}
void MyInitSIO(void)//инициализация последовательного порта
{
T3CON= 0x83; //настройка таймера-счетчика2
T3FD= 0x2D;//
SCON= 0x50;//настройка СОМ-порта
}
Функции для работы с АЦП и ЦАП
void SetVoltage(float v, bit channel)//установка напряжения
{
unsigned short max_val;//максимальное значение
unsigned short val;//значение напряжения
if(DACCON & 0x80)// если 8-ми битый режим
{
max_val = 0xFF;//то максимальное значение=255
}
else // если 12-битный режим
{
max_val = 0xFFF;// то максимальное значение=4095
}
val = (unsigned short)(v * max_val / VDD); //преобразуем напряжение v к нужному виду
val &= max_val;
if(channel == DAC0)//если выбран канал 0 ЦАП
{
DAC0H = val >> 8; //заносим значение напряжения в регистры
DAC0L = val;
}
else//если выбран канал 1 ЦАП
{
DAC1H = val >> 8;// заносим значение напряжения в регистры
DAC1L = val;//
}
}
float GetVoltage(unsigned char channel)//получение напряжения
{
float v;//локальная переменная
while(ADCCON3 & BUSY);// ждем окончание преобразования, то есть когда бит BUSY=0
ADCCON2 = 0x10 | channel ;// выбираем канал channel
while(ADCCON3 & BUSY); // ждем окончание преобразования
v = ((unsigned short)(ADCDATAH&0xF) << 8) | ADCDATAL;//результат преобразования в переменную v
v = v * 5.0 / 0xFFF;//преобразуем v
return v;//возвращаем результат преобразования
}
Функции для работы с ЖКИ СДК
При работе с дисплеем СДК используются:
Глобальная переменная CurPosCtrl. С помощью нее можно контролировать позицию курсора.
static bit CurPosCtrl=1;
Глобальная переменная cur_x. Она определяет позицию курсора в строке дисплея.
static char cur_x=0;
Глобальная переменная cur_y. Она определяет одну из двух строк дисплея.
static char cur_y=0;
void SwitchCurPosControl(bit o)//активация или деактивация курсора
{
CurPosCtrl=o;
}
void Strobe(char c)//стробирование информации
{
unsigned int i;//локальная переменная
WriteMax(C_IND,c | 0x1);// установка строба
WriteMax(C_IND,c & 0xFE);//сброс строба
for (i=0;i<300;i++)continue;// задержка
}
void LCD_SwitchCursor(bit cursor, bit blink)//переключение курсора
{
unsigned char i=0;
WriteMax( DATA_IND, DISPLAY_CTRL |
DISPLAY_ON |
((cursor)?CURSOR_ON:0) |
((blink)?BLINK:0) );// код команды переключения в регистр данных ЖКИ
Strobe(0x8); //R/W = 0; RS = 0
}
void LCD_Clear(void)//очистка дисплея
{
int i;
WriteMax(DATA_IND, CLEAR);//код команды очистки в регистр данных ЖКИ
Strobe(0x8); //очистка
cur_x = 0;//курсор на первое знакоместо
cur_y = 0;//первой строки
// for(i=0; i<1600; i++)continue;
}
void InitLCD(void)//инициализация ЖКИ
{
unsigned short i;
for(i=0; i<4000; i++)continue;
// cmd = 0x30; //
WriteMax(DATA_IND, FUNCTION_SET|EIGHT_BITS);
Strobe(0x8);
for(i=0; i<1500; i++)continue;
// cmd = 0x30; //
WriteMax(DATA_IND, FUNCTION_SET|EIGHT_BITS);
Strobe(0x8);
for(i=0; i<50; i++)continue;
// cmd = 0x30; //
WriteMax(DATA_IND, FUNCTION_SET|EIGHT_BITS);
Strobe(0x8);
// for(i=0; i<100; i++)continue;
// cmd = 0x38; //
WriteMax(DATA_IND, FUNCTION_SET|EIGHT_BITS|TWO_LINE);
Strobe(0x8);
// cmd = 0x08; //
WriteMax(DATA_IND, DISPLAY_CTRL); //Display off
Strobe(0x8);
// cmd = 0x01; //
WriteMax(DATA_IND, CLEAR);
Strobe(0x8);
// for(i=0; i<1600; i++)continue;
WriteMax(DATA_IND, ENTRY_MODE|INCR);
Strobe(0x8);
// cmd = 0x0F; //Display ON
WriteMax(DATA_IND, DISPLAY_CTRL|DISPLAY_ON); //Cursor OFF, Blinking OFF
Strobe(0x8);
}
void LCD_Putch(char ch)//вывод символа на дисплей
{
if(CurPosCtrl)//если курсор разрешен
{
LCD_GotoXY(cur_x,cur_y);//перемещаем курсор
if(++cur_x>15)cur_x=0,cur_y=~cur_y;//если символ не влезает в строку, то переключаемся на первое знакоместо другой строки
}
WriteMax(DATA_IND,ch);//помещаем символ в регистр данных ЖКИ
Strobe(0xC); //R/W = 0, RS = 1 строб-сигнал
}
void LCD_GotoXY(unsigned char x,bit y)//перевод курсора на знакоместо y – строка дисплея, x – позиция в строке.
{
WriteMax(DATA_IND,RAM_DD|(x+((y)?0x40:0)));//переключаем курсор
Strobe(0x8); //стробируем
cur_x = x;//сохраняем текущее знакоместо
cur_y = y;//сохраняем текущую строку
}
void LCD_Type(char* s)//вывод строки на дисплей
{
bit t = CurPosCtrl;//сохраняем значение переменной CurPosCtrl
SwitchCurPosControl(1);//разрешаем курсор
while(*s)//пока не дойдем до конца строки
LCD_Putch(*s++);//выводим очередной символ и переходим к следующему
SwitchCurPosControl(t);// восстанавливаем значение переменной CurPosCtrl
}
Функции для работы с прерываниями СДК
void SetVect(unsigned char num, void code * handler) //установка вектора прерывания
{
unsigned char xdata * p = RAM_TABLE + (num << 3) + 3;//базовый адрес для прерывания от СОМ-порта
*p++ = LJMP;//заносим в память код команды LJMP
*(unsigned short xdata *)p = (unsigned short)handler;//в итоге по базовому адресу р находится команда ljmp COM_ISR
}
void COM_ISR(void) interrupt 4//обработчик прерывания от СОМ-порта
{
if(RI)//чтение из RS-232
{
switch(RSio())//читаем символ из порта
{
case 'Y'://если это символ Y то значит нажата кнопка ПУСК
{
start_process=TRUE;//разрешаем работу в функции main
LCD_Clear();//чистим дисплей
rrr=TRUE;//
break; //выход из switch
}
case 'N':// если это символ N то значит нажата кнопка СТОП
{
char stop_mes[]={0xa8,0x70,0x6f,0xe5,0x65,0x63,0x63,0x20,0x6f,0x63,0xbf,0x61,0xbd,0x6f,0xb3,0xbb,0x65,0xbd};//Строка «Процесс остановлен»
start_process=FALSE;//запрещаем работу в функции main
LCD_Clear();//чистим дисплей
LCD_Type(stop_mes);//вывод на дисплей«Процесс остановлен»
rrr=FALSE;//
i=1;//для светодиодов
break;// выход из switch
}
case 'M'://если это символ M то значит на катушке 500 кг проволоки
{
int h=0;//
char mes1[]={0x48,0x61,0x20,0xba,0x61,0xbf,0x79,0xc1,0xba,0x65,0x20,0x35,0x4f,0x4f,0xba,0xb4,0x20,0xbe,0x70,0x6f,0xb3,0x6f,0xbb,0x6f,0xba,0xb8};// Строка «На катушке 500 кг проволоки»
LCD_Clear();//чистим дисплей
LCD_Type(mes1);//вывод на дисплей «На катушке 500 кг проволоки»
Svetik(TRUE);//светодиоды потетрадно мигают
Buzz();//звук
LCD_Clear();//чистим дисплей
WriteMax(SV,0x00);//выключаем светодиоды
rrr=TRUE;//
start_process=FALSE;// запрещаем работу в функции main
i=1;//для светодиодов
for(;h<32000;h++) continue;//задержка
break;// выход из switch
}
case 'L'://если это символ L то значит на катушке 100 метров проволоки
{
char mes2[]={0x31,0x4f,0x4f,0x20,0xbc,0x65,0xbf,0x70,0x6f,0xb3,0x20,0xbe,0x70,0x6f,0xb3,0x6f,0xe3,0x61};//строка «100 метров провода»
int h=0;//
LCD_Type(mes2);// вывод на дисплей «100 метров провода»
Svetik(FALSE);//горят светодиоды
Buzz();//звук
for(;h<32000;h++) continue;// задержка
LCD_Clear();//чистим дисплей
WriteMax(SV,0x00);// выключаем светодиоды
break;// выход из switch
}
}
}
if(TI)// запись в RS-232
{
}
}
Функция MAIN
void main(void)//главная функция
{
Initilization();//инициализация системы
while(1)//бесконечный цикл
{
if(rrr) //если rrr=1
{
char start_mes[] = {0xA1,0x6f,0xbf,0x6f,0xb3,0xbd,0x6f,0x63,0xbf,0xc4,0x20,0xba,0x20,0xbe,0x79,0x63,0xba,0x79};// строка «Готовность к пуску»
LCD_Clear();//чистим дисплей
LCD_Type(start_mes);// вывод на дисплей «Готовность к пуску»
rrr=FALSE;//
}
if(start_process)//если нажата кнопка ПУСК то начинаем опрос датчиков(клавиатуры), иначе ждем нажатия кнопки ПУСК
{
if(ScanKBOnce(symbol_from_kb)==0x01)//если клавиша нажата
{
char sym=*symbol_from_kb;//то определяем, что это за клавиша
float voltage=0;//напряжение с толщиномера
switch(sym)//какая клавиша нажата
{
case '1': {// клавиша 1 (1 импульс)
Type("1");//в СОМ-порт отправляем символ
break;// выход из switch
}
case '2': {// клавиша 2 (10 импульсов)
Type("10");// в СОМ-порт отправляем символ
break;// выход из switch
}
case '3': { // клавиша 3 (100 импульсов)
Type("100");//
break;// выход из switch
}
case 'A': { // клавиша А (1000 импульс)
Type("1000");//
break;// выход из switch
}
case '4': {// клавиша 4 (10000 импульс)
Type("10000");//
break;// выход из switch
}
case '5': { // клавиша 5 (100000 импульс)
Type("100000");//
break;// выход из switch
}
case '6': { // клавиша 6 ( 0.0001 метра)
SetVoltage(0.2,DAC0);//АЦП
voltage=GetVoltage(0);//ЦАП
Type("0.0001");// в СОМ-порт отправляем диаметр.
break;// выход из switch
}
case '7': {// клавиша 7 ( -0.0001 метра)
SetVoltage(-0.2,DAC0);// АЦП
voltage=GetVoltage(0);// ЦАП
Type("-0.0001");//
break;// выход из switch
}
case '8': {// клавиша 8 ( 0.001 метра)
SetVoltage(1.2,DAC0);// АЦП
voltage=GetVoltage(0);// ЦАП
Type("0.001");//
break;// выход из switch
}
case '9': {// клавиша 9 ( -0.001 метра)
SetVoltage(-1.2,DAC0);// АЦП
voltage=GetVoltage(0);// ЦАП
Type("-0.001");//
break;// выход из switch
}
}//end switch
}
}
}//end while(1)
}//end main