test
Dependencies: ad5422_arduino mbed LT1446 ADS1248-1 LM35-1 Flash FT813 PGA280_ADS1259
main.cpp
- Committer:
- nikmaos
- Date:
- 2020-08-11
- Revision:
- 4:d53895f65eb7
- Parent:
- 3:d4b106bf3a32
- Child:
- 5:21240fa1ee4c
File content as of revision 4:d53895f65eb7:
#include "PerifConfig.h" #include "PGA280.h" #include "ADS1259.h" #include "PGA280ADS1259.h" #include "main.h" #include "ad5422_arduino.h" #include "lm35.h" #include "LT1446.h" #include "ads1248.h" #include "flashwrapper.h" #include <cmath> #include "FT_Platform.h" #include "display.h" //коэффициенты по умолчанию температура колонки #define E_PCOEFF 0.5F //Пропорциональный коэффициент ПИД регулятора (Из EEPROM) #define E_ICOEFF 1500.0F //Постоянная времени интегрирования ПИД регулятора в секундах (Из EEPROM) (был 3000) #define E_DCOEFF 0.1F #define PID_SAMPLE_TIME 0.25F //Период ПИД // --//-- расхода #define R_PCOEFF 0.11F //Пропорциональный коэффициент ПИД регулятора (Из EEPROM) #define R_ICOEFF 3000.0F //Постоянная времени интегрирования ПИД регулятора в секундах (Из EEPROM) (был 3000) #define R_DCOEFF 0.1F #define R_PID_SAMPLE_TIME 0.25F #define CHKD (0x43484B44) //константа для проверки записанных данных (необходима контрольная сумма) #define TEMP 35 /*Таймер для вызова функции каждые 40 мс*/ Ticker lm_temp; Ticker adctask; Ticker PID1; //ПИД для нагрева колонки Ticker PID2; //ПИД для расхода /*Конец*/ //InterruptIn button(USER_BUTTON); InterruptIn ADSRdy(PG_3); //настройка SPI для дисплея FT813 TFT (D11, D12, D13, D10, D9, D8); //mosi, miso, sck, ss, int, pd Display disp(&TFT); //конец ADS1248_t ads1;//экземпляр ЦАП 1446 в памяти LM35_t RAS; PID_prom_t TempCol; PID_prom_t Rashod; PID_defs_t PID_defs={E_PCOEFF,E_ICOEFF,E_DCOEFF,PID_SAMPLE_TIME}; PID_defs_t PID_R_defs={R_PCOEFF,R_ICOEFF,R_DCOEFF,R_PID_SAMPLE_TIME}; volatile uint8_t temp=0;// уставка температуры колонки volatile float rashod_u=0; //уставка расхода char mbflag; unsigned char pga280gain; char UComandFlag; volatile unsigned char UComand; volatile long tempdata=0; char ch=0; //адрес на плате ТЭД-2 = 6 float x=0; float k; unsigned char str[]; //указатель для подстановки нужной функции void (*printer_p)(void); //функция чтения АДС для выполнения в задаче void readADC() { tempdata=ads1259_readData(5); //функция может зависать (надо избавиться от бесконечного цикла) x=NormADC(tempdata); UART.printf("%08f\r\n",x); } void readtemp() { float t; /*Обработка ошибки*/ //LM35_start(0.25); if(ads1.MUX0.MUX_SP!=0) { DS1248_START=1; ADS1248WakeupCommand(); ads1.MUX0.MUX_SN=1; //AIN1 ads1.MUX0.MUX_SP=0; //AIN0 ads1.VBIAS.all=0; ADS1248SettingReg(&ads1); } if (LM35_0.ready) //если данные готовы то считать и сбросить флаг (способ многозадачности) { LM35_0.ready=0; t=LM35_0.temp; TempCol.Error=temp-t; TempCol.Integral += TempCol.Error; TempCol.dError = TempCol.Error - TempCol.last_Error; TempCol.last_Error = TempCol.Error; //временно записать в калиброванное значение (нельзя так делать) LT1446_0.dacB.Code=(uint16_t)(PID(&Mem.PID,&TempCol) * 4095); LTCwrite(<1446_0); } } void readRashod() { float r; //Если не настроен, то настраиваем нужный канал чтения if(ads1.MUX0.MUX_SP!=2) { DS1248_START=1; ADS1248WakeupCommand(); ads1.MUX0.MUX_SN=3; //AIN3 ads1.MUX0.MUX_SP=2; //AIN2 ads1.VBIAS.VBIAS3=1; ADS1248SettingReg(&ads1); } //используем тот же тип из-за схожести if(RAS.ready)//Если данные готовы для считывания.. {RAS.ready=0; r=RAS.temp; Rashod.Error=rashod_u-r; Rashod.Integral += Rashod.Error;// Rashod.dError = Rashod.Error - Rashod.last_Error; Rashod.last_Error = Rashod.Error; LT1446_0.dacA.Code=(uint16_t)(PID(&Mem.PID_R,&Rashod) * 4095); LTCwrite(<1446_0); //Pressure.write(PID(&Mem.PID_R,&Rashod)); } } void printtemp() //отобразить температуру 1 раз { if (LM35_0.ready||RAS.ready) //если данные готовы то считать { UART.printf("TempCol %0.2f\t|\tRashod %0.4f ml (%0.4fV)\r\n",LM35_0.temp,RAS.temp,RAS.volts); } } void printPID() { /*функция отобображает в терминале значения не в момент расчёта ПИДа, но этого по идее достаточно для оценки результата работы, так как отображаются последние записанные в переменную данные*/ if ((Mem.PID.enabled==1)&&(Mem.PID_R.enabled==0)) //Если включен только первый ПИД { UART.printf("PID_t: Power %04d temp %0.2f\r\n",LT1446_0.dacB.Code,LM35_0.temp); } else if ((Mem.PID_R.enabled==1)&&(Mem.PID.enabled==0)) //Если включен только второй ПИД { UART.printf("PID_R: Power %04d rashod %0.4f ml (%0.4fV)\r\n",LT1446_0.dacA.Code,RAS.temp,RAS.volts); } else if ((Mem.PID_R.enabled==1)&&(Mem.PID.enabled==1)) //Если включены оба ПИД { UART.printf("PID_t: Power %04d temp %0.2f | ",LT1446_0.dacB.Code,LM35_0.temp); UART.printf("PID_R: Power %04d rashod %0.4f ml (%0.4fV)\r\n",LT1446_0.dacA.Code,RAS.temp,RAS.volts); } } float inline NormADC(long data) { x=data*2.5/0x800000; //x=x*8/(1<<pga280gain); return x; } /*Собственно сам расчёт ПИД. В аргумент подставляется конкретный ПИД (температура или расход) и его промежуточные значения (вклады)*/ float PID (PID_t *pidx, PID_prom_t *prom) { float P = pidx->kP * prom->Error; float I = pidx->kP / pidx->kI * pidx->sampleTime * prom->Integral; float D = pidx->kP * pidx->kD / pidx->sampleTime * prom->dError; float control = P + I + D; if (control > 1.0) return 1.0; if (control < 0.0) return 0.0; return control; } //включить пид1 void pressed() { /*Включить АЦП*/ DS1248_START=1; ADS1248WakeupCommand(); /*Выключить постоянное оторажение температуры*/ //запустить ПИД PID1.attach(&readtemp,0.25); Mem.PID.enabled=1; UART.printf("PID is ON\r\n"); } //включить пид2 void pid2_start() { DS1248_START=1; ADS1248WakeupCommand(); PID2.attach(&readRashod,0.25); Mem.PID_R.enabled=1; UART.printf("PID_R is ON\r\n"); } void readads() /*чтение АЦП в режиме -только первый канал -только второй канал -оба канала или как вариант: позиция бита означает какие каналы считываются если их больше чем 2*/ { double v=0; v=2.048*ADS1248ReadData(&ads1)/0x800000; if(ads1.MUX0.MUX_SP==0) { LM35_0.temp=v*100; LM35_0.ready=1; } if (ads1.MUX0.MUX_SP==2) { RAS.volts=v; RAS.temp=v*26.316-0.263;// мл/мин (сделать уставку) RAS.ready=1; } } void Mem_write() { flashWrite(0,Mem.w,(sizeof(Mem.w)/sizeof(uint32_t))); } float BufToFloat(unsigned char *buf) /*Преобразование строки из 16 символов в float*/ /*Нельзя первым символом вводить точку, перед неей обязательно нужна цифра Числа вводятся в формате 1.0 или 1,0 (точка обяательна даже если дробной части нет)*/ { int8_t n=-1; //начало (при старте его нет) uint8_t i=0; int8_t j=-1; //счётчик символов до точки uint8_t p=0; //позиция точки float result=1; //временно используем чтобы определить знак числа //while(str[i]!=) //команда выглядит как "/v0=-123.456" поэтому считаем с символа "-" позиция которого 3 (4 знак в массиве) //проверка на отрицательность и первый знак for (i=0;i<16;i++) { if(buf[i]=='-'){ result=-result; n=i; i++; break; } if((buf[i]>=0x30)&&(buf[i]<=0x39)) { n=i; break; } } if(n<0) return 0; //до тех пор, пока поступают нужные символы и нет превышения длины строки while((i<16)&&(buf[i]>=0x30)&&(buf[i]<=0x39)||((buf[i]=='.')||(buf[i]==',')||(buf[i]=='-'))) { i++; //проверять на наличие точки или запятой if((buf[i]=='.')||(buf[i]==',')) { if(p==0){ //если точка ещё не попадалась p=i; //запоминаем позицию плавающей запятой if (result>=0) j=i-n; //цифр до точки (3 начало) else j=i-n-1;} //на 1 символ больше из-за "-" else break; //если попалась ещё одна точка } } /*Добавить проверку на целое число!!*/ //ниже преобразуем информацию в число if (result>=0){ result=0; i=i-j-n-1; //знаки после запятой while(i--){ result+=(buf[p+i+1]-0x30)*powf(10,-i-1); } while(j--){ result+=(buf[p-j-1]-0x30)*powf(10,j); } } else {//строка на 1 знак больше result=0; i=i-j-n-2; //знаки после запятой while(i--){ result+=(buf[p+i+1]-0x30)*powf(10,-i-1); } while(j--){ result+=(buf[p-j-1]-0x30)*powf(10,j); } result=-result; } if (result>=-10000&&result<=10000)//заменить на проверку адекватности return result; else return 0; } void setPIDdefault(PID_t *pidx,PID_defs_t *defs) /*Установка для нужного пида его настроек по умолчанию*/ { pidx->kP=defs->kp; pidx->kI=defs->ki; pidx->kD=defs->kd; pidx->sampleTime=defs->st; pidx->chkd=CHKD; } int main() { //считывание данных настроек из памяти for (int i=0;i<sizeof(Mem.w)/sizeof(uint32_t);i++) { Mem.w[i]=flashRead(i*4);//sizeof(uint32_t)=4; } if ((Mem.PID.chkd!=CHKD)||(Mem.PID_R.chkd!=CHKD)) { //если метки данных нет то записать настройки ПИД по умолчанию UART.printf("PID is default\r\n"); setPIDdefault(&Mem.PID,&PID_defs); setPIDdefault(&Mem.PID_R,&PID_R_defs); Mem_write(); } UART.printf("PID:\r\nKp=%0.3f\r\nkI=%0.3f\r\nkD=%0.3f\r\n",Mem.PID.kP,Mem.PID.kI,Mem.PID.kD); UART.printf("PID_R:\r\nKp=%0.3f\r\nkI=%0.3f\r\nkD=%0.3f\r\n",Mem.PID_R.kP,Mem.PID_R.kI,Mem.PID_R.kD); UART.printf("%08X\r\n",Mem.PID.chkd); //Инициализация периферийных устройств SPI1_MasterInitMode(1);//работают режимы 1 и 2 для платы ТЭД2 //SPI3_MasterInitMode(1); //запустить задачу проверки команды по прерыванию УАРТ UART.attach(&ComandCheck,Serial::RxIrq); /*EN1=1; ch=5; pga280_setAdress(ch); pga280_ads1259_init(ch); //инициализировать АЦП по адресу на плате ТЭД-2 = 5 (4) printf("\r\n"); tempdata=pga280_readOneRegisterDevice(PGA280_BUF_TIMEOUT_ADR,ch); printf("Timeout %X\n",tempdata); wait_ms(20); pga280_setGAIN(GAIN_1_PGA280,ch); pga280_setMUX(2,ch); pga280gain=3; // gain=1 (0x03) tempdata=pga280_readOneRegisterDevice(PGA280_ERROR_ADR,ch);//чтение ошибок printf("Errors %X\n",tempdata); /*Конец*/ /*Инициализация внешнего АПЦ для LM35*/ //создадим экземпляр АЦП если их несколько UART.printf("Configuring ADC\r\n"); DS1248_START=0; wait_ms(20); DS1248_START=1; wait_ms(20); /*Настройка регистров в ОЗУ (описание в заголовочном файле)*/ ads1.MUX0.MUX_SN=1; //AIN1 ads1.MUX0.MUX_SP=0; //AIN0 ads1.MUX0.BCS=0; ads1.VBIAS.all=0; ads1.VBIAS.VBIAS0=0; ads1.SYS0.DR=DR20; ads1.SYS0.PGA=PGA_1; //=log2(PGA) пример: PGA=16 значит значение будет log2(16)=4; ads1.MUX1.MUXCAL=0; ads1.MUX1.REFSELT=2; ads1.MUX1.VREFCON=1; ads1.IDAC0.all=0; ads1.IDAC1.all=0xFF; ads1.FSC.all=1*(0x400000); //масштаб АЦП (наверное не коэффициент усиления) /*Конец*/ //Записать в чип ADS1248SettingReg(&ads1); if(ads1.SYS0.all==ADS1248ReadRegister(DS1248_REGISTER_SYS0,1)) //30.07.2020/8:40 UART.printf("ADS is configured\r\n"); ADSRdy.fall(&readads);//виснет без платы (решение - закомментить) wait_ms(20); //ADS1248SleepCommand();//временно отключаем чтобы при старте не началось считывание DS1248_START=0; /*Инициализация внещнего ЦАП*/ LT1446_0.dacA.Code=0; LT1446_0.dacA.Code=0; LTCwrite(<1446_0); UART.printf("DAC is configured\r\n"); //обнуление режима работы ПИД Mem.PID.enabled=0; Mem.PID_R.enabled=0; /*Тестовая кнопка для включения PID*/ //button.fall(&pressed); //Калибрануть если есть дисплей if(TFT.IsOn()){ UART.printf("Process of calibration\r\n"); disp.Calibration(Mem.calibration,0); Mem_write(); UART.printf("Calibrated\r\n");} disp.activeScreen = TEST_CHROM_SCREEN; disp.pressedButton = NONE_PRESS; ADS1248CalibrateSoft(&ads1,BufToFloat); //эксперимент // change active screen depending on pressed area while(1) { if(TFT.IsOn()) //проверка включения дисплея { disp.pressedButton = disp.GetTouch(); //-------------------------------ChromTestScreen------------------------ if (disp.activeScreen == TEST_CHROM_SCREEN) { disp.ChromTest(LM35_0.temp,RAS.temp,temp,rashod_u); if(disp.pressedButton){ wait_ms(100); if(disp.pressedButton==CHROM_TEMP_PRESS){ disp.pressedButton = NONE_PRESS; disp.activeScreen=MENU_SCREEN; } } } // --------------------------------------------------------------------- // Main menu screen if (disp.activeScreen == MENU_SCREEN) { disp.MainMenu(LM35_0.temp, LM35_0.temp);//сюда писать значения (наверное) (расход/температура) if (disp.pressedButton) { wait_ms(150); if (disp.pressedButton == CURR_TEMP_PRESS) { disp.activeScreen = CURR_TEMP_SCREEN; } else if (disp.pressedButton == CURR_HUM_PRESS) { disp.activeScreen = CURR_HUM_SCREEN; } disp.pressedButton = NONE_PRESS; } // --------------------------------------------------------------------- // Any other screen } else { // ---------------------------------------------------------------------------------------------- // You can back to main menu from any screen if (disp.pressedButton == MENU_PRESS) { disp.pressedButton = NONE_PRESS; disp.activeScreen = MENU_SCREEN; } else { // ---------------------------------------------------------------------------------------------- // Screen with current temperature / humidity if (disp.activeScreen == CURR_TEMP_SCREEN) { disp.CurrentTemperature(LM35_0.temp);//и сюда тоже } else if (disp.activeScreen == CURR_HUM_SCREEN) { disp.CurrentHumidity(LM35_0.temp);//и сюда } } } }//Проверка включения дисплея } } /*команды*/ void ComandCheck() { UART_gets(16); //моя функция на время if(str[0]=='/') { UART.printf("%c\r\n",str[1]); switch (str[1]){ /*Проверка платы SB-1 SWEN*/ case 'i':{ if (str[2]=='1') {SB1_SWEN=1; UART.printf("SW is ON\r\n");} else if (str[2]=='0') {SB1_SWEN=0; UART.printf("SW is OFF\r\n");} else if (str[2]=='a') { PosAw=1; PosBw=0; wait_ms(50); PosAw=0; PosBw=0; } else if (str[2]=='b') { PosAw=0; PosBw=1; wait_ms(50); PosAw=0; PosBw=0; } else if (str[2]=='r') { char a='0'; if (PosAr) a='a'; else if (PosBr) a='b'; UART.printf("POS is %c",a); } break;} case 'k':{//настройки ПИДа температуры колонки PID1.detach(); LT1446_0.dacB.Code=0; LT1446_0.dacA.Code=0; LTCwrite(<1446_0); switch (str[2]){ case 'P':{ Mem.PID.kP=BufToFloat(str); Mem_write(); UART.printf("P=%f\r\n",Mem.PID.kP); break; } case 'I':{ Mem.PID.kI=BufToFloat(str); Mem_write(); UART.printf("I=%f\r\n",Mem.PID.kI); break; } case 'D':{ Mem.PID.kD=BufToFloat(str); Mem_write(); UART.printf("D=%f\r\n",Mem.PID.kD); break; } case 'f':{ setPIDdefault(&Mem.PID,&PID_defs); Mem_write(); UART.printf("PID is default\r\n"); break;} default:break; } //задание уставки температуры if ((str[2]>='0')&&(str[2]<='9')){ temp=(str[2]-'0')*10+(str[3]-'0'); UART.printf("temp= %d\r\n",temp); } break;} case 't':{ //начать чтение температуры //отключаем отображение для перенастройки, настраиваем, записываем, включаем обратно отображение с новым параметром lm_temp.detach(); DS1248_START=1; ADS1248WakeupCommand(); ads1.MUX0.MUX_SN=1; //AIN1 ads1.MUX0.MUX_SP=0; //AIN0 ads1.VBIAS.all=0; ADS1248SettingReg(&ads1); UART.printf("Temperature reading started.\r\n"); //снять показания термодатчика //LM35_start(0.25); printer_p=&printtemp; lm_temp.attach(printer_p,0.1); break;} case 'p':{ pga280gain=str[2]; //присваиваем числовое значение команды взятое из символа цифры pga280gain-=0x30; ch=5; pga280_setGAIN(pga280gain,ch); ch=0; break;} case 'g': { //включить нужный ПИД if(str[2]=='p') { switch(str[3]) {//gp1 case '1'://первый - температура { lm_temp.detach(); pressed(); printer_p=&printPID; lm_temp.attach(printer_p,0.25); break;} case '2'://второй - расход {//gp2 lm_temp.detach(); pid2_start(); printer_p=&printPID; lm_temp.attach(printer_p,0.25); break;} //остановить работу ПИД регуляторов. case 's':// /gps { lm_temp.detach(); PID1.detach(); PID2.detach(); Mem.PID.enabled=0; Mem.PID_R.enabled=0; LT1446_0.dacB.Code=0; LT1446_0.dacA.Code=0; LTCwrite(<1446_0); UART.printf("PIDs is stopped\r\n"); break;} } } //запустить задачу чтения АЦП каждые 40 мс (25 герц) //adctask.attach(&readADC,0.04); break;} case's': {//остановить постоянное отображение //adctask.detach(); lm_temp.detach(); Mem.PID.enabled=0; Mem.PID_R.enabled=0; //LM35_stop(); ADS1248SleepCommand(); DS1248_START=0; break;} case 'd':{//LTC1446 задание значений на ЦАП оба канала //MBFlagRegister.DAC=1; if (str[2]=='a')//команда = "da=xxxx" где хххх - код ЦАП { LT1446_0.dacA.Code=(str[3]-0x30)*1000+(str[4]-0x30)*100+(str[5]-0x30)*10+(str[6]-0x30); UART.printf("%d\r\n",LT1446_0.dacA.Code); } if (str[1]=='b') { LT1446_0.dacB.Code=(str[3]-0x30)*1000+(str[4]-0x30)*100+(str[5]-0x30)*10+(str[6]-0x30); UART.printf("%d\r\n",LT1446_0.dacB.Code); } LTCwrite(<1446_0); break;} case 'e':{ tempdata=pga280_readOneRegisterDevice(PGA280_ERROR_ADR,5);//чтение ошибок printf("ERRORS %X\n",tempdata); break;} case 'r':{//выключить ПИД 2 перед манипуляциями с ним PID2.detach(); //задающее воздействие =0; LT1446_0.dacA.Code=0; LT1446_0.dacB.Code=0; LTCwrite(<1446_0); //обработка команд switch (str[2]){ case 'P':{ Mem.PID_R.kP=BufToFloat(str); Mem_write(); UART.printf("P=%f\r\n",Mem.PID_R.kP); break; } case 'I':{ Mem.PID_R.kI=BufToFloat(str); Mem_write(); UART.printf("I=%f\r\n",Mem.PID_R.kI); break; } case 'D':{ Mem.PID_R.kD=BufToFloat(str); Mem_write(); UART.printf("D=%f\r\n",Mem.PID_R.kD); break; } case 'f':{// /rf setPIDdefault(&Mem.PID_R,&PID_defs); Mem_write(); UART.printf("PID is default\r\n"); break;} case 'g':{//запуск чтения расхода /rg lm_temp.detach(); DS1248_START=1; ADS1248WakeupCommand(); ads1.MUX0.MUX_SN=3; //AIN3 ads1.MUX0.MUX_SP=2; //AIN2 ads1.VBIAS.VBIAS3=1; ADS1248SettingReg(&ads1); UART.printf("Rashod reading started.\r\n"); printer_p=&printtemp; lm_temp.attach(printer_p,0.1); break;} case 'u':{ // ru=xx.xx; точка обязательна //задание уставки расхода rashod_u=BufToFloat(str); UART.printf("Rashod = %0.3f\r\n",rashod_u); break;} default:break; } if((str[2]=='e')&&(str[3]=='s')&&(str[4]=='e')&&(str[5]=='t')){ //перезагрука printf("System Reset\r\n"); NVIC_SystemReset();} if ((str[2]=='e')&&(str[3]=='c')&&(str[4]=='a')&&(str[5]=='l')){ disp.Calibration(Mem.calibration,1); UART.printf("Recalibrated\r\n"); }; break;} default: //MBFlagRegister.TMLS=0; //починка зависания (не работает) break; } } }