Курсовая работа. Техническое задание на разработку 14 2 Обоснование выбора инструмента программирования и отладки 15 3 Разработка программной части асу 15
Скачать 0.72 Mb.
|
2 Специальная часть2.1 Техническое задание на разработкуТехническое задание на разработку комплекта конструкторской документации для изготовления опытного образца Основание для разработки Основанием для разработки является учебная программа Технические характеристики разрабатываемого изделия Назначение и выполняемые функции Получение измерительной информации о температуре по пяти каналам от цифровых термометров DS18S20 фирмы Dallas Semiconductor и сигнализация при выходе значения температуры за заданные пределы. Тип и количество входных сигналов Входной сигнал типа сухой контакт 6 шт. Тип и количество выходных сигналов Выходной сигнал типа сухой контакт 1 шт. Метрологические характеристики Точность измерения температуры цифровым термометром DS18S20 Термометр уже откалиброван на заводе, гарантированная точность составляет ±0.5°C в диапазоне –10..+85°C и ±2°C во всем диапазоне рабочих температур. Типичная кривая ошибки измерения температуры: Особенности и функции интерфейса оператора Устройства отображения: - Модульный жидкокристаллический индикатор на базе контроллера HD44780 - Единичный светодиодный элемент Устройства ввода: - 6 кнопок управления интерфейсом Интерфейсы сопряжения микропроцессорного устройства с внешними устройствами Интерфейс 1-Wire. Питание устройства Напряжение постоянного тока 2,7-5,5 В, ток не более 71 мА. 2.2 Обоснование выбора инструмента программирования и отладкиДля программной эмуляции разрабатываемого изделия использована среда PROTEUS фирмы Labcenter Electronics, как один из самых удобных инструментов для программной эмуляции микропроцессорных контроллеров и подключаемых к ним периферических устройств. 2.3 Разработка программной части АСУПрограмма может быть условно разделена на две части: Основная программа Обработчики прерываний Основная программа представляет собой бесконечный цикл, в котором выполняются все основные функции программы. Обработчик прерываний 1 предназначен для точного задания с помощью таймера-счётчика промежутков времени для выполнения основных подпрограмм. По переполнению таймера-счётчика TC0 выполнение основной программы приостанавливается и выполняется подпрограмма обработчика прерываний. Обработчик прерываний 2 предназначен для точного задания с помощью таймера-счётчика ТС1 промежутков времени для выполнения команд обмена информации по интерфейсу 1-Wire. Таблица 2.1 – Перечень переменных с назначением и диапазон изменений
Таблица 2.2 – Состав и назначение подпрограмм и функций
Текст программы с подробными комментариями #include #include #define DB P2 // DB - синоним к порту P2 #define Buttons P1 // Buttons - синонимкпорту P1 ////////////Глобальные переменные////////////////////////////////// sbit E= P3^2; // E -синоним к контакту P3.2 sbit RW= P3^0; // RW -синоним к контакту P3.0 sbit RS= P3^1; // RS -синоним к контакту P3.1 sbit Error =P3^6; // Error - синонимк P3.6 sbit DQ =P3^7; // DQ - синоним к P3.7 int Ttek[5]; // текущее значение температур int Tlim[10]; // минимальные пределы температур нечётные, максимальные - чётные unsigned char Talarm[5]; //флаги срабатывания сигнализации термометра Ti unsigned char Mode; // выборрежимаменю unsigned char Pos1; unsigned char Pos2; unsigned char Pos3; // позициикурсоравменю unsigned char slog[3]={20,2,1}; // инкримет/декремент unsigned char Per_Key; //счётчикпрерыванийдляобработкиввода unsigned int Per_Contr; //счётчик прерываний для контроля unsigned char Per_Impulse; //счётчик прерываний для импульсов unsigned char keypress=0xFF; unsigned char i; // счётчикдляциклов bit Mode_Select; //флагсменырежима bit Err_Off;// флаг сброса сраьатывания сигнализации unsigned char Mig[5];// флаг мигания сигнализации термометра Ti unsigned long int SerL[5]; //массивы младших 4 байт семейства, серийного номера термомтров и контрольных сумм unsigned long int SerH[5]; //массивы старших 4 байт семейства, серийного номера термометров и контрольных сумм ///////////////////////////////////////////////////////////////////////// //////////////Ожидание сброса флага занятости ЖКИ////////////////////////////////// void wait_ready() {unsigned char BF;//переменная для проверки флага занятости DB=255; // на порт DB послать 255 do {E=0; // на всякий случай сбросить линию E в 0 RS=0;// линия RS на команды RW=1;// линия RW на чтение E=1;//начать чтения BF=DB;//считать с порта DB в BF E=0;//закончить чтение } while (BF&0x80);// пока старший бит BF равен 1 } /////////////////////////////////////////////////////////////////// //////////////Записать команду в память ЖКИ/////////////////////////////// void write_command(char B) {wait_ready();//дождаться готовности RS=0;// линия RS на команды RW=0;// линия RW на запись E=1;//начать запись DB=B;// записать на порт DB число B E=0;//закончить запись } //////////////////////////////////////////////////////////// //////////////Записать данныев память ЖКИ///////////////////////////// void write_data(char B) {wait_ready();//дождаться готовности RS=1;// линия RS на данные RW=0;// линия RW на запись E=1;//начать запись DB=B;// записать на порт DB число B E=0;//закончить запись } //////////////////////////////////////////////////////////// /////////////////Отправить бит на термометр//////////////////////////////////////// void send_bit(bit B) { DQ=0; //Сбросить линию данных DQ в ноль DQ=B; //Установить на линии DQ требуемое значение TH1=186; // нач состояние таймера для 70 мкс TL1=186; TF1=0; // исх состояние таймера и прерываний ET1=0; //запретить прерывания таймера Т1 TR1=1; //запустить таймер Т1 do {} while(!TF1); //делать пока сброшен флаг прерывания таймера Т1 TR1=0; //остановить таймер Т1 DQ=1; //Установить на линии DQ лог. единицу ET1=1; //разрешить прерывания таймера Т1 TF1=0; // сбросить флаг прерывания таймера Т1 } ///////////////////////////////////////////////////////////////////////////// /////////////////Отправить команду на термометр//////////////////////////////////////// void send_command(char command) {unsigned char B; // переменная для отправляемого бита unsigned char k; // переменная для цикла for (k=0;k<8;k++) {B= command&1; // записать k-ый бит с команды send_bit(B); // отправить бит B command>>=1; // сдвинуть полученную коаманду на бит впарво } } //////////////////////////////////////////////////////////////////////// /////////////////Прочитать бит с термометра//////////////////////////////////////// char read_bit() {char B; // переменная для прочтённого бита DQ=0; //Сбросить линию данных DQ в ноль DQ=1; //для состояния готовности к чтению TL1=253; // нач состояние таймера для 3 мкс TH1=253;TF1=0; // исх состояние таймера и прерываний ET1=0; //запретить прерывания таймера Т1 TR1=1; //запустить таймер Т1 do{}while (!TF1); //делать пока сброшен флаг прерывания таймера Т1 B=DQ; // прочитать с линии DQ бит TR1=0; //остановить таймер Т1 TL1=TH1=189; // нач состояние таймера для 67 мкс TF1=0; // сбросить флаг прерывания таймера Т1 TR1=1; //запустить таймер Т1 do{}while (!TF1); //делать пока сброшен флаг прерывания таймера Т1 TR1=0; //остановить таймер Т1 DQ=1; //Установить на линии DQ лог. единицу ET1=1; //разрешить прерывания таймера Т1 return B; // функция возвращает значение прочтённого бита } ///////////////////////////////////////////////////////////////////////////// /////////////////Прочитать байт с термометр//////////////////////////////////////// char read_byte() {unsigned char B; // переменная для прочтённого бит unsigned char k; // переменная для цикла unsigned char rez=0; // переменная для прочтённого байта for (k=0;k<8;k++) {B=read_bit(); // прочитатьбит B=(B&1)< rez+=B; //запить бит в байт } return rez; // функция возвращает значение прочтённого байта } //////////////////////////////////////////////////////////////////////// /////////////////Инициализация обмена с термометрами//////////////////////////////////////// void init_exchange() {Per_Impulse=0; //сбросить счётчик для обработки времени импульса сброса TH1=6; // нач состояние таймера для 250 мкс TL1=6; TF1=0; // исх состояние таймера и прерываний TR1=1; //запустить таймер Т1 DQ=0; //Сбросить линию данных DQ в ноль do {} while(Per_Impulse<2); // делать в течении 2X250 мксек TR1=0; //остановить таймер Т1 Per_Impulse=0; //сбросить счётчик для обработки времени импульса сброса TH1=6; // нач состояние таймера для 250 мкс TL1=6; TF1=0; // исх состояние таймера и прерываний TR1=1; //запустить таймер Т1 DQ=1; //Установить линию данных DQ в единицу do {} while(Per_Impulse<2); // делать в течении 2X250 мксек TR1=0; //остановить таймер Т1 Per_Impulse=0; //сбросить счётчик для обработки времени импульса сброса } //////////////////////////////////////////////////////////// /////////////////////Команда Match ROM интерфеса 1-Wire/////////////////////////////////////////////// void Match_ROM(long int A, long int B) {unsigned char j; // переменная для цикла send_command(0x55);//кодкоманды Match ROM for(j=0;j<4;j++) {send_command(A&0x000000FF);// отправитьбайтсерийникатермометра A>>=8; //сдвинуть серийник термометра на байт вправо } for(j=0;j<4;j++) {send_command(B&0x000000FF);// отправить байт серийника термометра B>>=8; //сдвинуть серийник термометра на байт вправо } } //////////////////////////////////////////////////////////////////// /////////////////////Команда Convert_T интерфеса 1-Wire/////////////////////////////////////////////// void Convert_T() {send_command(0x44);//кодкоманды Convert_T } //////////////////////////////////////////////////////////////////// /////////////////////Команда Read_Scratchpad интерфеса 1-Wire////////////////////////////////// void Read_Scratchpad() {send_command(0xBE);//кодкоманды Read_Scratchpad } //////////////////////////////////////////////////////////////////// /////////////Разделить число на разряды и вывести на ЖКИ////////////////////////////// void cut_number(int N, unsigned char adress) {unsigned char N_char; //переменнаядля перевода числа к типу char unsigned char ost; //переменная для остатка деления unsigned char j; // переменная для цикла write_command(0x04); // декремент адреса,экран не движется write_command(adress);// установить на требуемый адресс памяти ЖКИ if (N<0) //если число меньше нуля {N_char=((N&0x7FFF)^0x7FFF)+1; //получить модуль числа привести его к типу char write_data('-'); //вывести на ЖКИ символ '-' } else {write_data(' ');//вывести на ЖКИ символ ' ' N_char=N; //привести чило к типу char } write_command(adress+4);// установить на требуемый адресс, сдвинутый на 4, памяти ЖКИ if (N_char%2) {write_data('5');} //если последний бит числа равен 1, то вывести на ЖКИ символ '5' else {write_data('0');}//вывести на ЖКИ символ '0' write_data('.'); //вывести на ЖКИ символ '.' N_char>>=1; //сдвинуть число на 1 бит вправо write_command(adress+2);// установить на требуемый адресс, сдвинутый на 2, памяти ЖКИ j=0;//обнулить счётчик цикла do{ost=N_char%10; // делить до остатка число на 10 write_data(ost+0x30);//вывести на ЖКИ символ остаток деления N_char/=10;//раделить число на 10 j++; //инкремент счётчика }while (N_char); // пока не обнолуться переменная write_command(0x06); // инкремент адреса,экран не движется write_command(adress+1);// установить на требуемый адресс, сдвинутый на 1, памяти ЖКИ while (j<2){write_data(' '); j++;} //заполнить не задействованные разряды числа пробелами } ///////////////////////////////////////////////////////////// //////////////////////ВывестинаЖКИстроку///////////////////////////// void output_string(char adress, char *s, char l) {char j; write_command(adress); // установить на требуемый адресс памяти ЖКИ for ( i = 0; i < l; i++ ) write_data(s[i]);// записать i-ый символ выводимой на ЖКИ строки } ///////////////////////////////////////////////////////////// ///////////Инициализация устройств////////////////////////// void Init_Device() //инициализация МПК {TMOD=0x22; //настройка таймера TH0=6; // Т0 на 250 машинных циклов ET0=1; //разрешение прерываний таймера Т0 ET1=1; //разрешение прерываний таймера Т1 TF0=0; //сброс флага прерываний Т0 TF1=0; //сброс флага прерываний Т1 write_command(0x38); // 8-битная шина, 2 строки, шрифт 5x8 write_command(0x01); // очистить экран write_command(0x06); // инкремент адреса,экран не движется } //////////////////////////////////////////////////////////// ///////////////Контроль (сигнализация)//////////////////////////////// void Control() {Err_Off=1;// установить флаг сброса сигнализации for (i=0;i<5;i++) {if( Ttek[i]>Tlim[2*i+1] || Ttek[i] {Error=1; //зажечь светодиод Error красным Err_Off=0;//сбросить флаг сброса сигнализации Talarm[i]=1;// установить флаг срабатывания сигнализации термометра Ti } else {Talarm[i]=0;} //сбросить флаг срабатывания сигнализации термометра Ti } if (Err_Off==1)// если установлен флаг сброса сигнализации {Error=0;//зажечь светодиод Error зелёным } } /////////////////////////////////////////////////////////// ///////////////////Обработка ввода////////////////////////////////////// char Key_Code() {unsigned char cod; //пременная номера нажатой клавиши cod=0xFF; // значение cod при ненажатых кнопках switch (Buttons) { case 0xFE : { cod=1; break;} //нажатакнопка Up case 0xFD : { cod=2; break;} //нажатакнопка Down case 0xFB : { cod=3; break;} //нажатакнопка Left case 0xF7 : { cod=4; break;} //нажатакнопка Right case 0xEF : { cod=5; break;} //нажатакнопка Ok case 0xDF : { cod=6; break;} //нажатакнопка ESc } if (cod == keypress) cod = 0xFF; // нажатиеужеесть else keypress = cod; // новоенажатие return cod; //функциявзвращаетномернажатойклавиши } ///////////////////////////////////////////////////////// ///////////////////Переключение режимов////////////////////////////////////// void Select() {unsigned char index; //переменная для вычисления индекса элемента массива index=2*Pos1+Pos2; // вычислить индекс switch(Mode)//переключатель режимов меню {case 0://первый режим меню "Выбор термометра Ti" switch(Key_Code()) {case 3://нажатакнопка "Left" if (Pos1>0) {Pos1--;} //если позиции не крайняя слева, то менять позицию курсора влево else{Pos1=4;} //иначе установить позиции курсора на крайнюю справа break; case 4://нажата кнопка "Rigth" if (Pos1<4) {Pos1++;} //если позиции не крайняя справа, то менять позицию курсора вправо else{Pos1=0;} //иначе установить позиции курсора на крайнюю слева break; case 5://нажата кнопка "Ok" Mode=1; //втрой режим меню Mode_Select=1; //установить флаг переключения режима break; } break; case 1://второй режим меню "Выбор Tmin или Tmax" switch(Key_Code()) {case 1://нажата кнопка "Up" if (Pos1<4) {Pos1++; Mode_Select=1;}//если позиции не крайняя справа, то менять позицию курсора вправо else{Pos1=0;Mode_Select=1;} //иначе установить позиции курсора на крайнюю слева break; case 2://нажата кнопка "Down" if (Pos1>0) {Pos1--; Mode_Select=1;}//если позиции не крайняя слева, то менять позицию курсора влево else{Pos1=4;Mode_Select=1;} //иначе установить позиции курсора на крайнюю справа break; case 3: //нажата кнопка "Left" if (Pos2>0) {Pos2--;} //если позиции не крайняя слева, то менять позицию курсора влево else{Pos2=1;} //иначе установить позиции курсора на крайнюю справа break; case 4://нажата кнопка "Rigth" if (Pos2<1) {Pos2++;} //если позиции не крайняя справа, то менять позицию курсора вправо else{Pos2=0;} //иначе установить позиции курсора на крайнюю слева break; case 5://нажата кнопка "Ok" Mode=2; //втрой режим меню Mode_Select=1; //установить флаг переключения режима break; case 6://нажатакнопка "Esc" {Mode=0;Mode_Select=1;}//если нажата кнопка "Esc", то выйти в первый режим меню break; } break; case 2://третий режим меню "Выбор разряда числа" switch(Key_Code()) { case 1://нажатакнопка "Up" if (Tlim[index]<250-slog[Pos3])Tlim[index]+=slog[Pos3];//если Tlim больше 124,5, топрибавить else Tlim[index]=249;// иначе Tlim равно 124,5 if (Pos2==0 && Tlim[index]>=Tlim[index+1]-1 ){Tlim[index]=Tlim[index+1]-2;}// если Tmin=>Tmax, тоотнять Tmin=Tmax-1 break; case 2://нажатакнопка "Down" if(Tlim[index]>-110+slog[Pos3])Tlim[index]-=slog[Pos3];//если Tlim больше -54,5, тоотнять else Tlim[index]=-109;// иначе Tlim равно -54,5 if (Pos2==1 && Tlim[index]<=Tlim[index-1]+1 ){Tlim[index]=Tlim[index-1]+2;}// если Tmax<=Tmin, тоотнять Tmax=Tmin+1 break; case 3://нажатакнопка "Left" if (Pos3>0) {Pos3--;} //если позиции не крайняя слева, то менять позицию курсора влево else{Pos3=2;} //иначе установить позиции курсора на крайнюю справа break; case 4://нажата кнопка "Right" if (Pos3<2) {Pos3++;} //если позиции не крайняя справа, то менять позицию курсора вправо else{Pos3=0;} //иначе установить позиции курсора на крайнюю слева break; case 6://нажата кнопка "Esc" {Mode=1;Mode_Select=1;}//если нажата кнопка "Esc", то выйти в первый режим меню break; } break; } } ///////////////////////////////////////////////////////// //////////////Обработка вывода на дисплей/////////////////////////////////// void Display() {unsigned char index1; //переменная для вычисления индекса элемента массива unsigned char index2; //переменная для вычисления индекса элемента массива index1=2*Pos1; // вычислить индекс index2=8*Pos2; // вычислить индекс write_command(0x0C); //включить дисплей, курсор не отображается switch(Mode)//переключатель режимов меню { case 0://первый режим меню "Выбор термометра Ti" if (Mode_Select==1)//Если флаг смены режима установлен {write_command(0x01); // очиститьэкран for (i=0;i<5;i++) {output_string(130+8*i,"T",1); //вывести на ЖКИ символ 'T' write_data(49+i); // вывести на ЖКИ символ "i" } Mode_Select=0;// сбросить флаг смены режима } for (i=0;i<5;i++) { cut_number(Ttek[i],192+i*8);//разделить Ttek наразряды if (Talarm[i]==1 && Mig[i]==1)// если установлен флаг срабатывания сигнализаци Talarm[i] {output_string(129+8*i,"!",1); //вывести на ЖКИ символ '!' output_string(132+8*i,"!",1); //вывести на ЖКИ символ '!' Mig[i]=0; //сбросить флаг мигания } else {output_string(129+8*i," ",1);//вывести на ЖКИ символ ' ' output_string(132+8*i," ",1);//вывести на ЖКИ символ ' ' Mig[i]=1; // установать флаг мигания } } write_command(130+8*(Pos1)); //курсор на выбранную температуру break; case 1://второй режим меню "Выбор Tmin или Tmax" if (Mode_Select==1)//Если флаг смены режима установлен {write_command(0x01); // очистить экран output_string(138,"Ttek",4);//вывести на ЖКИ строку output_string(146,"Tmin",4);//вывести на ЖКИ строку output_string(154,"Tmax",4); //вывести на ЖКИ строку output_string(193,"T",1); //вывести на ЖКИ символ write_data(49+Pos1); // вывести символ с номером выбранного термометра cut_number(Tlim[index1],209); //вывести минимальный предел температуры на ЖКИ cut_number(Tlim[index1+1],217); //вывести максимальный предел температуры на ЖКИ Mode_Select=0;// сбросить флаг смены режима } if (Talarm[Pos1]==1 && Mig[Pos1]==1) // если установлен флаг срабатывания сигнализации выбранного термометра T {output_string(192,"!",1);//вывести на ЖКИ символ '!' output_string(195,"!",1);//вывести на ЖКИ символ '!' Mig[Pos1]=0;// сбросить флаг мигания } else //если флаг мигания сброшен {output_string(192," ",1);//вывести на ЖКИ символ ' ' output_string(195," ",1);//вывести на ЖКИ символ ' ' Mig[Pos1]=1; // установить флаг мигания } cut_number(Ttek[Pos1],201);//вывести текущую температуру на ЖКИ write_command(147+index2); // установить курсор на выбранную температуру break; case 2: //третий режим меню "Выбор разряда числа" if (Talarm[Pos1]==1 && Mig[Pos1]==1) // если установлен флаг срабатывания сигнализации выбранного термометра T {output_string(192,"!",1); //вывести на ЖКИ символ '!' output_string(195,"!",1); //вывести на ЖКИ символ '!' Mig[Pos1]=0;// сбросить флаг мигания } else //если флаг мигания сброшен {output_string(192," ",1); //вывести на ЖКИ символ ' ' output_string(195," ",1); //вывести на ЖКИ символ ' ' Mig[Pos1]=1; // установить флаг мигания } cut_number(Ttek[Pos1],201);//разделить Ttek на разряды cut_number(Tlim[index1],209);//разделить Tlim на разряды cut_number(Tlim[index1+1],217);//разделить Tlim наразряды if (Pos3!=2){write_command(210+8*Pos2+Pos3);} // установитькурсорна else {write_command(210+8*Pos2+Pos3+1);} // выбраннуыйразрядтемпературы break; } write_command(0x0E);// появляетсякурсор } //////////////////////////////////////////////////////////////////////// ///////////////////////////////Получения информации о измерениях с термометров///////////////////////////////// void Measure() {bit A; //переменная для знака температуры for(i=0;i<5;i++) {init_exchange();// установить обмен с термометрами Match_ROM(SerL[i],SerH[i]); // отправить команду Mach ROM и серийник требуемого термометра Read_Scratchpad(); // отправитькоманду Read_Scratchpad Ttek[i]=(read_byte())&0xFF;// прочитать значение температуры A=read_bit(); // прочитать знак if (A) Ttek[i]-=256; // если знак отризательный то отнять 256 init_exchange(); // установить обмен с термометрами init_exchange(); // установить обмен с термометрами Match_ROM(SerL[i],SerH[i]); // отправить команду Mach ROM и серийник требуемого термометра Convert_T(); // отправить команду начала ковертирования температуры } } ///////////////////////////////////////////////////////////////// ///////////Обработка прерываний таймера Т0////////////////////////// void T0_ISR(void) interrupt 1 {Per_Key++; //инкремент счётчика прерываний для обработки ввода Per_Contr++; //инкремент счётчика прерываний для обработки контроля } ///////////////////////////////////////////////////////////// ///////////Обработка прерываний таймера Т1///////////////////// void T1_ISR(void) interrupt 3 {Per_Impulse++; //инкремент счётчика прерываний для обработки импульса сброса } //////////////////////////////////////////////////////////// /////////////////Главная программа/////////////////////////////// void main() {Mode_Select=1;// установить флаг смены режима Init_Device();//инициализация устройств TR0=1; TR1=0;//запустить таймеры Т0 и остановить Т1 Mode=0;//первый режим меню EA=1; //разрешить индивидуальные разрешения прерываний Per_Key=0;//обнулить счётчик прерываний для обработки ввода Per_Contr=0;//обнулить счётчик прерываний для обработки контроля Pos1=0; Pos2=0;Pos3=0;//позиции курсора во всех режимах меню на первое положение for (i=0;i<5;i++) {Ttek[i]=0; // //задание массива текущих значений температур } SerL[0]=0xC8C53010;SerL[1]=0xC8C53110;SerL[2]=0xC8C53210;SerL[3]=0xC8C53310;SerL[4]=0xC8C53410; // задание массивов серийников термометров SerH[0]=0xC3000000;SerH[1]=0xF4000000;SerH[2]=0xAD000000;SerH[3]=0x9A000000;SerH[4]=0x1F000000; for (i=0;i<5;i++) {Tlim[2*i]=-109; // задание массива знчений Tlim[2*i+1]=249; // пределов температур } for (i=0;i<5;i++) {Talarm[i]=0; //сбросить все флаги срабатывания сигнализации термометра Ti Mig[i]=0; // сбросить флаг мигания сигнализации Ti } Error=0;//зажечь светодиод Error зелёным do {if (Per_Key>=250){ Select();Display(); Per_Key=0;}// делатьна 700 прерываниитаймера T0 if (Per_Contr>=3000){Measure(); Control(); Per_Contr=0;}// делатьна 3000 прерываниитаймера T0 } while (1); } //////////////////////////////////////////////////////////////////////////// |