//#DOC Main, program entry and main loop
#include "main.h"
#include <otSerial.h>
#include <otTimer.h>
#include <otRTC.h>
#include <otFPGA.h>
#include <otSpi.h>
#include <otI2cBus.h>
#include <otPrintf.h>
#include <otMalloc.h>
#include <otGraphics.h>
#include <otTTY.h>
#include <otFILE.h>
#include <otRotaryEncoder.h>
#include <otString.h>
#include <otPlot.h>
#include <otMath.h>
#include <otWYSIWYG.h>
#include <otMAX7219.h>
#include <otBME280.h>
#define MAX_VALUE 100
#define C_OBJECTS 32
#define II_YEAR _objIndex[0]
#define II_MONTH _objIndex[1]
#define II_DAY _objIndex[2]
#define II_HOUR _objIndex[3]
#define II_MINUTE _objIndex[4]
#define II_SECOND _objIndex[5]
#define II_APPLY _objIndex[6]
#define II_RESET _objIndex[7]
#define II_TIMER _objIndex[8]
#define II_CPS _objIndex[9]
#define II_CPM _objIndex[10]
#define II_USV _objIndex[11]
#define II_CPS_MIN _objIndex[12]
#define II_CPS_MAX _objIndex[13]
#define II_EEVENT _objIndex[14]
#define II_EHOUR _objIndex[15]
#define II_EMINUTE _objIndex[16]
#define II_ESECOND _objIndex[17]
#define II_EV_TEMP _objIndex[18]
#define II_EV_PRES _objIndex[19]
#define II_EV_UMID _objIndex[20]
// For time measure
#define TimeMark(n) _timeMark[n] = TimerGetCounts(TIMER0)
#define TimeMeasure(n) ElapsedTimeUs(_timeMark[n], TimerGetCounts(TIMER0))
typedef struct {
U16 cps;
U16 cpm;
F32 evTemp;
U16 evPres;
U8 evUmid;
}dataLog;
// Time related switch
enum PROCESS_ID {
TL_SCREEN, // CPs
};
SDRAM_DATA otGraphics tft;
SDRAM_DATA otTTY tty;
SDRAM_DATA otFILE file;
SDRAM_DATA otRotaryEncoder enc;
SDRAM_DATA otPlot plot;
SDRAM_DATA otWYSIWYG mainPanel;
SDRAM_DATA otMAX7219 tabPanel;
SDRAM_DATA otBME280 env;
U32 _screenSaver = 600;
U32 _counts = 0;
// Environment variables
F32 _eTemperature;
S32 _ePressure, _eUmidity;
// Object index
U16 _objIndex[C_OBJECTS];
S32 _year, _month, _day, _cpm = 0;
S8 _hour, _minute, _second;
S8 _eHour = 0, _eMinute = 0, _eSecond = 0; // Exceptional events time
S32 _eYear, _eMonth, _eDay; // Exceptional events date
U32 _timeMark[16], _pCnt = 0, _cnt, _cpmT = 0;
U32 _cps, _min = 100, _max = 0, _exEvents = 0;
U32 _cpsHist[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, _cpsHistPtr = 0;
F32 _usv;
dataLog * _data;
U32 _iData = 0;
char * _buff;
bool _display = false;
SDRAM_DATA const char * _screen =
" DATE-TIME : YYYY/MM/DD hh:mm:ss CHANGE "
" "
" MEASURES : TIMER s CPSS cp/s CPMMMM cp/m USV.VVV uSv/h RESET "
" "
" STATISTICS : MINN cp/s MAXX cp/s EXEVNT eh:em:es "
" "
" ENVIRONMENT: TEM.P C PRESS mBar UMID % "
" "
" "
" "
" Vert. 10 cp/s Div. - Hor. 20s Div. "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" ";
FAST_CODE_2 void _coreTimerCallBack()
{
U32 cnt;
if(_screenSaver)
{
_screenSaver--;
if(_screenSaver == 0)
{
_screenSaver = 600;
tft.backlight(false);
}
}
FpgaEventGate(false);
cnt = FpgaEventRead();
FpgaEventGate(true);
_counts++;
if(cnt != -1 && cnt >= _pCnt)
{
_cnt = cnt;
_cps = _cnt - _pCnt;
_pCnt = _cnt;
}
else
{
FpgaEventReset();
_cnt = 0;
_pCnt = _cnt;
_cps = 0;
}
// x averaged cps
_cpsHist[_cpsHistPtr++] = _cps;
_cpsHistPtr %= 16;
_cpmT += _cps;
// Evaluate cpm
if((_counts % 60) == 0)
{
_cpm = _cpmT;
_cpmT = 0;
}
_display = true;
}
void tftOn()
{
_screenSaver = 600;
tft.backlight(true);
}
bool TimeCheckUs(U8 n, U32 val)
{
bool ret = false;
U32 et = ElapsedTimeUs(_timeMark[n], TimerGetCounts(TIMER0));
if(et >= val) ret = true;
return ret;
}
bool TimeCheck(U8 n, U32 val)
{
bool ret = false;
U32 et = ElapsedTimeUs(_timeMark[n], TimerGetCounts(TIMER0)) / 1000;
if(et >= val) ret = true;
return ret;
}
void logo(const char * fname, U16 delay = 3000, U16 color = RGB565_BLACK)
{
//printf("Start loading ...\n");
FILE fd = file.fopen(fname, otFILE::A_READ);
if(fd)
{
U16 ** mem;
U16 xw, yw, xo, yo, xs, ys;
U32 nx, rlen, sz, n, i;
U8 ec, co;
tft.size(xs, ys);
file.fread(fd, &xw, 2, rlen);
file.fread(fd, &yw, 2, rlen);
file.fread(fd, &nx, 4, rlen);
file.fread(fd, &ec, 1, rlen);
file.fread(fd, &co, 1, rlen);
sz = xw * yw * 2;
printf("%20s (%03dx%03d) Size = %6u, Frames = %4u, Used memory: %5u KByte\n", fname, xw, yw, sz, nx, nx * sz / 1024);
mem = (U16 **) _malloc(sizeof(U16 *) * nx);
// Allocate frame buffer
for(i = 0; i < nx; i++)
mem[i] = (U16 *) _malloc(sz);
// Read all frames
for(int i = 0; i < nx; i++)
file.fread(fd, mem[i], sz, rlen);
file.fclose(fd);
tft.clear(color);
// Center the image
xo = (xs - xw) / 2;
yo = (ys - yw) / 2;
// Display all frames
for(int n = 0; n < nx; n++)
tft.draw565Bitmap(xo, yo, mem[n], xw, yw);
DelayMs(delay);
// Free all allocated memory
for(i = 0; i < nx; i++)
_free(mem[i]);
_free(mem);
}
else
tty.print("Can't load %s file\n", fname);
//printf("End loading ...\n");
}
void createStringDateTime(char * date, char * time)
{
sprintf(date, "%04d%02d%02d", _year, _month, _day);
if(time)
sprintf(time, "%02d%02d%02d", _hour, _minute, _second);
}
void createPath(const char * mainPath, char * out)
{
createStringDateTime(&out[32], &out[64]);
file.mkdir(mainPath);
sprintf(out, "%s/%s", mainPath, &out[32]);
file.mkdir(out);
sprintf(out, "%s/%s/%s", mainPath, &out[32], &out[64]);
//file.mkdir(out);
}
void createPathDate(const char * mainPath, const char * fp, char * out)
{
createStringDateTime(&out[32], &out[64]);
createPath(mainPath, out);
// Complete file path
_strcat(out, "/"); // Add dir separator
_strcat(out, fp); // Add file name prefix
}
void createFilePath(const char * path, const char * extension, char * output)
{
// Complete file path
_strcpy(output, path);
//_strcat(output, "/"); // Add dir separator
_strcat(output, extension); // Add extension file name
}
void SaveData()
{
char * fileName, * filePath;
fileName = (char *) _malloc(1024);
filePath = (char *) _malloc(1024);
createPath("data", filePath);
createFilePath(filePath, ".txt", fileName);
FILE fd = file.fopen(fileName, otFILE::A_CREATE_ALWAYS | otFILE::A_WRITE);
for(U32 i = 0; i < 3600; i++)
file.fprintf(fd, "%u,%u,%u,%.1lf,%u,%u\n", i, _data[i].cps, _data[i].cpm, _data[i].evTemp, _data[i].evPres, _data[i].evUmid);
file.fclose(fd);
_free(filePath);
_free(fileName);
}
void saveExceptionalEvents()
{
FILE fd = file.fopen("exEvents.txt", otFILE::A_OPEN_APPEND | otFILE::A_WRITE);
file.fprintf(fd, "%4d/%02d/%02d, %02d:%02d:%02d, %u\n", _eYear, _eMonth, _eDay, _eHour, _eMinute, _eSecond, _cps);
file.fclose(fd);
}
void setup()
{
RtcOn();
UartEnableSignals(UART0);
UartSetBaud(UART0, 115200);
setStdout(4096, UART0); // printf output is now directed on UART0
TimerSetAsCounter(TIMER0);
TimerEnable(TIMER0, true);
FpgaInit();
FpgaHiddenPort(HP_EXT_I2C, true);
FpgaPortPxSet(PD_INPUT, PD_INPUT, PD_INPUT, PD_OUTPUT, PD_OUTPUT, PD_OUTPUT, PD_INPUT, PD_INPUT);
FpgaEventFunction(EI_PX_7, EVF_COUNTER, 0); // PX[7] Read event pulses
FpgaEventGate(true);
FpgaEventReset();
I2cBusReset();
I2cBusFrequency(400, 30);
tft.init(otGraphics::ILI9488); // Use ILI9341 or ILI9486 or ILI9488
tft.setRotation(otGraphics::IR_270);
tft.clear();
tft.setTextColor(RGB565_YELLOW, RGB565_BLACK);
tty.init(1, &tft);
tty.print("SYSTEM INIT, PLEASE WAIT ...\n");
tty.print("*** NUCLEAR RAIN ***\n");
tty.print("Code revision: %s\n", __RevisionAlpha__);
tabPanel.init(SA_MAX7219_B);
tabPanel.displayText("-HELLO-");
SpiSet(SA_LCD);
if(file.init())
file.setStdout(printf);
else
{
tty.print("SD not found! System halted.\n");
IDLE;
}
if(!env.init())
{
tty.print("Can't find BME280\n");
IDLE;
}
// Set PX0-1 for encoder phases and PX2 for the button
enc.init(0, 2);
// Temporary buffer
_buff = (char *) _malloc(1024);
// Data buffer
_data = (dataLog *) _malloc(sizeof(dataLog) * 3600);
// Prescaler action 400MHz / 40 (39+1) = 10MHz (100ns)
// Period: 10000000 * 100ns = 1.0s
CoreTimerSetInterruptService(39, 10000000, true, _coreTimerCallBack);
CoreTimerEnable(true);
// Logo
logo("sys/logo.emb");
tft.clear();
// Plot area
plot.init(0, 90, 480, 200, &tft);
plot.setRange(0, MAX_VALUE);
// GUI init
mainPanel.init(C_OBJECTS, _screen, 80, 0, 0, &tft, 1);
II_YEAR = mainPanel.addInteger("YYYY", &_year, otWYSIWYG::OBJ_VAR_S32);
II_MONTH = mainPanel.addInteger("MM", &_month, otWYSIWYG::OBJ_VAR_S32);
II_DAY = mainPanel.addInteger("DD", &_day, otWYSIWYG::OBJ_VAR_S32);
II_HOUR = mainPanel.addInteger("hh", &_hour, otWYSIWYG::OBJ_VAR_S8);
II_MINUTE = mainPanel.addInteger("mm", &_minute, otWYSIWYG::OBJ_VAR_S8);
II_SECOND = mainPanel.addInteger("ss", &_second, otWYSIWYG::OBJ_VAR_S8);
II_APPLY = mainPanel.addButton("CHANGE", 0);
II_RESET = mainPanel.addButton("RESET", 0);
II_TIMER = mainPanel.addInteger("TIMER", &_counts, otWYSIWYG::OBJ_VAR_S32);
II_CPS = mainPanel.addInteger("CPSS", &_cps, otWYSIWYG::OBJ_VAR_S32);
II_CPM = mainPanel.addInteger("CPMMMM", &_cpm, otWYSIWYG::OBJ_VAR_S32);
II_USV = mainPanel.addReal("USV.VVV", &_usv);
II_CPS_MIN= mainPanel.addInteger("MINN", &_min, otWYSIWYG::OBJ_VAR_S32);
II_CPS_MAX= mainPanel.addInteger("MAXX", &_max, otWYSIWYG::OBJ_VAR_S32);
II_EEVENT = mainPanel.addInteger("EXEVNT", &_exEvents, otWYSIWYG::OBJ_VAR_S32);
II_EHOUR = mainPanel.addInteger("eh", &_eHour, otWYSIWYG::OBJ_VAR_S8);
II_EMINUTE= mainPanel.addInteger("em", &_eMinute, otWYSIWYG::OBJ_VAR_S8);
II_ESECOND= mainPanel.addInteger("es", &_eSecond, otWYSIWYG::OBJ_VAR_S8);
II_EV_TEMP= mainPanel.addReal("TEM.P", &_eTemperature);
II_EV_PRES= mainPanel.addInteger("PRESS", &_ePressure, otWYSIWYG::OBJ_VAR_S32);
II_EV_UMID= mainPanel.addInteger("UMID", &_eUmidity, otWYSIWYG::OBJ_VAR_S32);
mainPanel.regen();
}
void loop()
{
int sel = 7, psel = 7, a, i;
bool action = false, edit = false;
mainPanel.setState(sel, otWYSIWYG::OBJ_STAT_SELECTED);
while(1)
{
if(!edit)
{
F32 cpsm = 0;
for(i = 0; i < 16; i++)
cpsm += _cpsHist[i];
cpsm /= 16.0f;
sprintf(_buff, "[%06u] : %6u cp/s : %6.1f cp/s", _cnt, _cps, cpsm);
_usv = (F32) _cpm * 0.0057;
if(_cps < _min) _min = _cps;
if(_cps > _max) _max = _cps;
tft.setTextSize(2);
tft.writeString(300, _buff);
tft.setTextSize(1);
// Tab panel display
SpiSet(SA_MAX7219_B);
sprintf(_buff, "%3u %4.1f", _cps, cpsm);
tabPanel.displayText(_buff);
SpiSet(SA_LCD);
}
if(enc.button(false))
{
switch(sel)
{
case 0: case 1: case 2: case 3:
case 4: case 5: case 6:
case 7:
if(mainPanel.state(sel) == otWYSIWYG::OBJ_STAT_SELECTED)
mainPanel.setState(sel, otWYSIWYG::OBJ_STAT_ACTIVATED);
else
mainPanel.setState(sel, otWYSIWYG::OBJ_STAT_SELECTED);
break;
}
enc.release();
edit = true;
}
a = enc.read();
if(a) tftOn();
// Change parameters if requested
if(mainPanel.state(sel) == otWYSIWYG::OBJ_STAT_SELECTED)
{
// Change the selected object
if(a > 0)
{
sel++;
if(sel > 7) sel = 0;
action = true;
}
if(a < 0)
{
sel--;
if(sel < 0) sel = 7;
action = true;
}
if(action)
{
mainPanel.setState(psel, otWYSIWYG::OBJ_STAT_NORMAL);
action = false;
mainPanel.setState(sel, otWYSIWYG::OBJ_STAT_SELECTED);
psel = sel;
}
}
else
{
if(a || sel == 6 || // Apply
sel == 7) // Reset
// Action for selected object
switch(sel)
{
case 0: // Year
_year += a;
mainPanel.regen(II_YEAR);
break;
case 1: // Month
_month += a;
if(_month < 1) _month = 12;
if(_month > 12) _month = 1;
mainPanel.regen(II_MONTH);
break;
case 2: // Day
_day += a;
if(_day < 1) _day = 31;
if(_day > 31) _day = 1;
mainPanel.regen(II_DAY);
break;
case 3: // Hour
_hour += a;
if(_hour < 0) _hour = 23;
if(_hour > 23) _hour = 0;
mainPanel.regen(II_HOUR);
break;
case 4: // Minute
_minute += a;
if(_minute < 0) _minute = 59;
if(_minute > 59) _minute = 0;
mainPanel.regen(II_MINUTE);
break;
case 5: // Seconds
_second += a;
if(_second < 0) _second = 59;
if(_second > 59) _second = 0;
mainPanel.regen(II_SECOND);
break;
case 6: // Apply
DelayMs(200);
mainPanel.setState(sel, otWYSIWYG::OBJ_STAT_SELECTED);
RtcSetDate(_year, _month, _day);
RtcSetTime(_hour, _minute, _second);
edit = false;
break;
case 7:
_counts = 0;
_pCnt = 0;
_min = 100;
_max = 0;
_exEvents = 0;
_iData = 0;
FpgaEventReset();
edit = false;
mainPanel.setState(sel, otWYSIWYG::OBJ_STAT_SELECTED);
DelayMs(1000);
plot.clear();
break;
}
}
if(_display)
{
_display = false;
if(!edit)
{
plot.addData(_cps);
plot.plot();
RtcGetTime((U8 *) &_hour, (U8 *) &_minute, (U8 *) &_second);
RtcGetDate(&_year, &_month, &_day);
// Show current values
mainPanel.regen(II_YEAR);
mainPanel.regen(II_MONTH);
mainPanel.regen(II_DAY);
mainPanel.regen(II_HOUR);
mainPanel.regen(II_MINUTE);
mainPanel.regen(II_SECOND);
mainPanel.regen(II_TIMER);
}
if(_cps > MAX_VALUE)
{
_exEvents++;
_eHour = _hour;
_eMinute = _minute;
_eSecond = _second;
_eYear = _year;
_eMonth = _month;
_eDay = _day;
saveExceptionalEvents();
}
if(env.read())
{
_eTemperature = env.temperature();
_ePressure = env.pressure();
_eUmidity = env.humidity();
}
_data[_iData].cps = _cps;
_data[_iData].cpm = _cpm;
_data[_iData].evTemp = _eTemperature;
_data[_iData].evPres = _ePressure;
_data[_iData].evUmid = _eUmidity;
_iData++;
if(_iData == 3600)
{
_iData = 0;
SaveData();
}
mainPanel.regen(II_CPS);
mainPanel.regen(II_CPM);
mainPanel.regen(II_USV);
mainPanel.regen(II_CPS_MIN);
mainPanel.regen(II_CPS_MAX);
mainPanel.regen(II_EEVENT);
mainPanel.regen(II_EHOUR);
mainPanel.regen(II_EMINUTE);
mainPanel.regen(II_ESECOND);
mainPanel.regen(II_EV_TEMP);
mainPanel.regen(II_EV_PRES);
mainPanel.regen(II_EV_UMID);
}
}
}