Article Sections
C Program: Interface Embedded Linux to Terminal
This program is to allow a Serial port and GPIO port on a Wrap2C or similar Embedded Linux board to control relays, operate LEDs and use a simple PIC based Terminal with two line display and 3x4 keypad to connect as User Interface.
It also shows how "non-Blocking" serial I/O is done on Linux. Applies to PC also. If there is no data to read, the program continues. Normal behaviour is that the program reading a serial port waits for ever for either a character, or even by default an "enter key" press (EOL) to read the buffer.
It includes a Turbo NiMH charger that signals "end of charge" all via GPIO port pins.
#include
#include
#include
#include
#include
#include
#include
// these make the serial port work!
#include
#include
#include
// these added for scripts
#include
// for parsing
#include
#define LPC_ADDR 0xF410 //GPIOBASE10
#define GPIOBASE14 0xF414
//select ONLY ONE PAIR of the next four lines:
#define TARGET // for production on Wrap PC Engines 1E or 1C
#define DEVICE_NAME "/dev/ttyS0"
//#define DESKTOP // Running on a regular desktop / laptop Linux
//#define DEVICE_NAME "/dev/ttyUSB0"
const int halfbyte = 4;
// LED PIN If not mentioned, DON'T connect!
const int LPC_LINK = 0; // GPIO32 3 Link or Status
const int LPC_RXSIG = 1; // GPIO33 5 RX level
const int LPC_RXSN = 2; // GPIO34 7 RX S/N
const int LPC_PIN9 = 3; // GPIO35 9 TX Status
const int LPC_IN_LO = 4; // GPIO36 20 INPUT: 1=Batt Low, 0=Normal
const int LPC_CHARGE = 5; // GPIO37 11 Charge
const int GPIO38 = 6; // Unknown function
const int LPC_IN_HI = 7; // GPIO39 19 INPUT: 1=Normal, 0=Charged
// 18 3.3V Wrap Vs
// 2,4,6,8,10,12,17 GND
const int ALL_LEDS_RED = 0;
const int ALL_LEDS_GREEN = 15;
#define KEY1 ( 48 + 1)
#define KEY2 ( 48 + 2)
#define KEY3 ( 48 + 3)
#define KEY4 ( 48 + 4)
#define KEY5 ( 48 + 5)
#define KEY6 ( 48 + 6)
#define KEY7 ( 48 + 7)
#define KEY8 ( 48 + 8)
#define KEY9 ( 48 + 9)
#define KEY0 ( 48 + 0)
#define KEYSTAR 42
#define KEYHASH 35
#define KEYUP (48 + 1)
#define KEYDN (48 + 7)
//Read mask GPIO 1010 0000 for battery state:
// msb ..lsb
// 0xx0 xxxx = Charged
// 1xx0 xxxx = Normal
// 1xx1 xxxx = Low
const int LOOP_INTERVAL = 4000; //4ms = 250 times a second
const int PULSE_CHARGE_DUTYCYCLE = 15;
const int RECHARGE_DELAY = 30000;
const int PULSE_COLOUR = 5;
const int PULSE_FLASH = 40;
const int PULSE_STICK =5;
char STICK_CHARS[7];
char frequencies[150][10];
char baseNames[150][17];
int MAX_BASES = 149;
int pulsesColour = 0; //to modulate colour of LED.
int pulsesStick = 0;
int ledSequence[7];
int lpcDataOut = 0;
int waitingForPage = 0;
int pulseFlash = 0; //to toggle flash on off.
int pulsedCharge = 0;
int flashToggle = 0; // on/off or bicolour flash
int chargeToggle = 0; // A/C only enables charging
char spinningStick ;
char escape = 27;
// low sensor: Bat low =1, Bat Ok = 0
// hi sensor: Bat Ok = 1, bat charged = 0
// lsb .. msb
// so xxxx 1001 = LOW, Pulse Charging
// so xxxx 1xx0 = Normal, Turbo charge
// so xxxx 0xx0 = Full Charge, Trickle charge
const int BATT_LOW = 9;
const int BATT_NORMAL = 8;
const int BATT_FULL = 0;
enum testerStates {TS_DISP_OFF, TS_BATT_LOW, TS_TURBO_CHARGE, TS_TRICKLE_CHARGE, TS_DISP_FREQ, TS_DISP_BASE, TS_ENTER_FREQ, TS_ENTER_BASE, TS_DISP_WAIT};
enum testerStates thisState = TS_TRICKLE_CHARGE;
enum testerStates lastState = TS_DISP_OFF;
int entryCountdown; //Reset to last base
struct timeval timenow;
struct timeval exectime;
#define BASE_LIST_FILENAME "baselist.csv"
char baseName[17];
int thisBase = 0;
char thisFreq[10];
int serport; //the device for I/O
int initport (char *deviceName){
int fd;
struct termios options;
serport = open(deviceName, O_RDWR | O_NOCTTY | O_NDELAY);
#ifdef DESKTOP
if (serport == -1) {
perror("open_port fail: ");
perror(DEVICE_NAME);
return -1;
}
#endif
//Next line not liked by gcc so we get existing one
//bzero(&options,sizeof(options));
fd = serport;
tcgetattr(fd,&options);
options.c_cflag = (B2400 |CRTSCTS |CS8| CLOCAL |CREAD);
// options.c_cflag &= ~PARENB;
// options.c_cflag &= ~CSTOPB;
// options.c_cflag &= ~CSIZE ;
options.c_iflag |= IGNPAR ;
options.c_oflag = 0;
//options.c_oflag = options.c_iflag;
options.c_lflag = 0 ; //no echo, no buffer till ENTER key
options.c_cc[VMIN] = 0; // don't wait for any characters
options.c_cc[VTIME] = 0; // no intercharacter delay
tcflush(fd,TCIFLUSH);
tcsetattr(fd,TCSANOW,&options);
fcntl(fd,F_SETFL,FNDELAY); //nonblocking
cfsetispeed(&options, B2400);
cfsetospeed(&options, B2400);
return serport;
}
// End initport
int readsch(int fd){
int readError = 0;
char portbuff[200];
readError = read(fd,portbuff,1);
readError-- ; // so we can hsav nulls
if (readError >= 0)
readError = portbuff[0];
return readError;
}
// End readsch
int writesch(int fd, char chtosend){
int writeError = 0;
char portbuff[200];
portbuff[0] = chtosend;
writeError = write(fd,&portbuff,1);
return writeError;
}
// End writesch
int writesstr(int fd, char * strtosend){
int writeError = 0;
int sendLen = strlen(strtosend);
if (strtosend[sendLen-1] ==' ')
sendLen--;
writeError = write(fd,strtosend,sendLen);
return writeError;
}
// End writesstr
void lcdClear(int fd) {
char spaces32home[80];
strcpy(spaces32home, " ");
spaces32home[0] = 'n';
spaces32home[33] = 'n';
spaces32home[34] = ' ';
writesstr(fd,spaces32home);
#ifdef DESKTOP
// writesch(fd,escape);
// writesch(fd,'H');
#endif
}
// End lcdClear
void lcdLamp(int fd, int lit){
const char CTRLP = 16;
char lcdcmd[5];
strcpy(lcdcmd,"p1");
lcdcmd[0] = CTRLP;
if (lit > 0 )
lcdcmd[1] = '1';
else
lcdcmd[1] = '0';
writesstr(fd, lcdcmd);
#ifdef DESKTOP
// writesch(fd,escape);
// writesch(fd,'H');
#endif
}
// End lcdLamp
void lcdHome(int fd) {
char newline = 13;
writesch(fd,newline);
#ifdef DESKTOP
// writesch(fd,escape);
// writesch(fd,'H');
#endif
}
// End lcdHome
void lcdBargraph(int fd, int barsize){
int sigcount = 0;
char lcdbuff[17];
if (barsize > 16)
barsize = 16;
if (barsize < 0)
strcpy(lcdbuff," * NO SIGNAL! * ");
else
strcpy(lcdbuff," "); // 16 chars I hope!
for (sigcount=0; sigcount';
writesstr(fd,lcdbuff);
}
// End lcdBargraph
void tickTockInit(int loopInterval){
/* Get current time */
gettimeofday(&timenow,NULL);
/* Set future time for action */
exectime.tv_sec = timenow.tv_sec;
exectime.tv_usec = timenow.tv_usec + loopInterval;
if (exectime.tv_usec > 1000000) {
exectime.tv_usec = exectime.tv_usec - 1000000;
exectime.tv_sec++;
}
}
void tickTockWait(int waitInterval) {/* Get current time */
int waiting = 1;
while (waiting){
gettimeofday(&timenow,NULL);
if ((exectime.tv_sec < timenow.tv_sec)
||((exectime.tv_sec == timenow.tv_sec)
&& (exectime.tv_usec <= timenow.tv_usec))) {
/* Set future time for action */
exectime.tv_sec = timenow.tv_sec;
exectime.tv_usec = timenow.tv_usec + waitInterval;
if (exectime.tv_usec > 1000000) {
exectime.tv_usec = exectime.tv_usec - 1000000;
exectime.tv_sec++;
}
waiting = 0;
}
}
//update all the timers, the colour sequence index changes every time
pulsesColour++;
if (pulsesColour > (PULSE_COLOUR))
pulsesColour = 0;
if ((thisState == TS_DISP_FREQ) ||(thisState==TS_DISP_BASE)) {
lpcDataOut = ledSequence[pulsesColour];
}
pulseFlash++;
if (pulseFlash > (PULSE_FLASH)){
pulseFlash = 0;
// spins at twice the flashing speed
if (pulsesStick > PULSE_STICK)
pulsesStick = 0;
pulsesStick++;
if (flashToggle > 0) {
flashToggle = 0;
} else{
flashToggle = 1;
}
if (thisState == TS_DISP_WAIT) {
lcdLamp(serport,0); // LEDS off
lcdClear(serport);
writesstr(serport,"WAIT ... ");
writesch(serport,STICK_CHARS[pulsesStick]);
writesstr(serport," == ");
writesch(serport,STICK_CHARS[pulsesStick]);
lcdHome(serport);
lpcDataOut = 0;
}
}
pulsedCharge++;
if (pulsedCharge > PULSE_CHARGE_DUTYCYCLE)
pulsedCharge = 0;
#ifdef TARGET
/* Output LPC_CHARGE..32 values as doubleword/long to GPIOBASE offset 10 */
outl(lpcDataOut,LPC_ADDR);
#endif
}
//End tickTockWait
void initLPCbusAsIO(){
#define PMR_ADDR 0x9030
#define GPIOBASE20 0xF420
#define GPIOBASE24 0xF424
#ifdef TARGET
/* Allow program to use higher I/O ports */
iopl(3);
/* Allow program to access required I/O */
ioperm(PMR_ADDR,4,1);
ioperm(GPIOBASE20,4,1);
ioperm(GPIOBASE24,4,1);
/* Switch the LPC port to GPIO mode */
outl(inl(PMR_ADDR) & ~(1 << 16 | 1 << 14), PMR_ADDR);
/* Select GPIO to change (32) */
outl(32,GPIOBASE20);
/* Setup this GPIO for output */
outl(67,GPIOBASE24);
/* Select GPIO to change (33) */
outl(33,GPIOBASE20);
/* Setup this GPIO for output */
outl(67,GPIOBASE24);
/* Select GPIO to change (34) */
outl(34,GPIOBASE20);
/* Setup this GPIO for output */
outl(67,GPIOBASE24);
/* Select GPIO to change (35) */
outl(35,GPIOBASE20);
/* Setup this GPIO for output */
outl(67,GPIOBASE24);
/* Select GPIO to change (36) */
outl(36,GPIOBASE20);
/* Setup this GPIO for input only */
outl(68,GPIOBASE24);
/* Select GPIO to change (37) */
outl(37,GPIOBASE20);
/* Setup this GPIO for output */
outl(67,GPIOBASE24);
/* Select GPIO to change (39) */
outl(39,GPIOBASE20);
/* Setup this GPIO for input only */
outl(68,GPIOBASE24);
/* Remove I/O permissions */
ioperm(PMR_ADDR,4,0);
ioperm(GPIOBASE20,4,0);
ioperm(GPIOBASE24,4,0);
#endif
}
//End initLPCbusAsIO
void displaySignalFreq(int siglevel, char *freqText ){
lcdLamp(serport,1);
lcdClear(serport);
lcdBargraph(serport,(siglevel)); // assume only 1 to 8 used
writesstr(serport,"FREQ: ");
if (strlen(freqText) > 9)
freqText[9] =' ';
writesstr(serport,freqText);
lcdHome(serport);
}
//end DisplaySignalFreq
void displaySignalText(int siglevel, char *sigLabel ){
lcdLamp(serport,1);
lcdClear(serport);
lcdBargraph(serport,siglevel); // assume only 1 to 8 used
if (strlen(sigLabel) > 16)
sigLabel[16] =' ';
writesstr(serport,sigLabel);
lcdHome(serport);
}
//end DisplaySignalFreq
void showBase(int baseNum ){
if (baseNum < 0)
baseNum = 0;
if (baseNum > MAX_BASES)
baseNum = MAX_BASES;
lcdLamp(serport,1);
lcdHome(serport);
writesstr(serport," ");
lcdHome(serport);
if (strlen(baseNames[baseNum]) > 16)
baseNames[baseNum][16] =' ';
writesstr(serport,baseNames[baseNum]);
lcdHome(serport);
}
void showNewFreq (char * newFreq) {
lcdHome(serport);
writesstr(serport," New : ");
writesstr(serport, newFreq);
}
/***************************************************
Scripts:
get_webpages.sh Does wget on the webpages
and creates "/rw/siglevels.csv"
put_frequency.sh Does wget on form post to set
frequency and creates "/rw/donefreqpost.txt"
***************************************************/
int fileExists (char *newFile){
FILE *fp;
fp = fopen(newFile,"r");
if (fp == NULL){ // not ready yet
return (-1);
}else {
return (1);
}
}
// End File Exists
void modemAskStatus(){
if (waitingForPage < 1) {
system("./test_webpages.sh &");
waitingForPage = 1;
}
}
//End modemAskStatus
int modemStatusRead(char *resultsTxt){
FILE *fp;
int readQty =0;
if (waitingForPage > 0) {
fp = fopen("/rw/siglevels.csv","r");
if (fp == NULL){ // not ready yet
readQty = -1;
}else {
readQty=fread(resultsTxt,1,79,fp);
fclose(fp);
if (readQty > 5) {
system("rm /rw/siglevels.csv");
waitingForPage = 0;
}else {
readQty = -1;
}
}
}else{
modemAskStatus();
}
return (readQty);
}
// End modemStatusRead
void modemSetFreq(char *newFrequency){
char commandstr[80];
while (waitingForPage > 0)
modemStatusRead(commandstr);
strcpy(commandstr, "./test_frequency.sh ");
strcat(commandstr, newFrequency);
strcat(commandstr, " &");
system(commandstr);
}
// End ModemSetFreq
int modemFreqDone(){
FILE *fp;
int readQty;
char results[80];
readQty = -1;
strcpy(results," ");
fp = fopen("/rw/donefreqpost.txt","r");
if (fp == NULL){ // not ready yet
readQty = -1;
}else {
readQty=fread(results,1,5,fp);
fclose(fp);
if (readQty > 4) {
system("rm /rw/donefreqpost.txt");
}else {
readQty = -1;
}
}
return (readQty);
}
// End modemFreqDone
// find nth-1 comma, if n zero the start
// read till nth comma or end of string
// convert substring to Int.
int parseNthString (char *sourceTxt, int posparm, char *newBuff){
int lineLen = 0;
int startCh = 0;
int endCh = 0;
long resultNum = 0;
int indexCh = 0;
int indexBuff = 0;
lineLen = strlen(sourceTxt);
if (posparm < 1)
posparm = 1;
if (posparm == 1){
startCh = 0;
} else {
indexCh = 1;
while (indexCh < lineLen) {
if (sourceTxt[indexCh] == ','){
startCh = indexCh +1;
posparm-- ;
if (posparm < 2){
while ((startCh < lineLen) && (sourceTxt[startCh] == ' ')){
startCh++;
} //eat leading spaces
indexCh = lineLen;
}
}
indexCh++ ;
}
}
// now find the end
endCh = lineLen; //if there is no last comma
indexCh = startCh +1;
while (indexCh < lineLen){
if ((sourceTxt[indexCh] == ',') ||(sourceTxt[indexCh] == 'n')){
endCh = indexCh;
indexCh = lineLen;
}
indexCh++ ;
}
// now copy the substring
indexBuff = 0;
indexCh = startCh;
while (indexCh < endCh){
newBuff[indexBuff] = sourceTxt[indexCh];
indexBuff++;
indexCh++;
}
newBuff[indexBuff] = ' ';
return indexBuff;
}
// End parseNthInt
int readBaseList (char * fname){
// read file line by line
FILE *fp;
int readQty =0;
int lineLen = 0;
int buffUsed = 0;
int buffIndex = 0;
int readFreq = 1;
int chrIndex = 0;
const MAX_BUFFER = 7000;
char buffer[7000];
fp = fopen(fname,"r");
#ifdef DESKTOP
printf("Open file %s n",fname);
#endif
if (fp ==NULL){ // not there
readQty = -1;
#ifdef DESKTOP
printf("Open file %s did fail! n",fname);
#endif
}else {
#ifdef DESKTOP
printf("Read the buffer? n");
#endif
buffUsed=fread(buffer,1, (MAX_BUFFER-1),fp);
// close the file?
fclose(fp);
buffer[buffUsed] = ' ';
#ifdef DESKTOP
printf("The buffer is Read! size %dn",buffUsed);
#endif
readQty =0; //index to arrays
for (buffIndex = 0; buffIndex < buffUsed; buffIndex++) {
if (buffer[buffIndex] == 'n') {
baseNames[readQty][chrIndex] = ' ';
chrIndex = 0;
#ifdef DESKTOP
printf("item: %d >%s< [%s]n",readQty, frequencies[readQty], baseNames[readQty]);
#endif
readQty++ ;
readFreq = 1;
}else{
// add code here to eat " or _ if desired
if (buffer[buffIndex] == ',') {
frequencies[readQty][chrIndex] = ' ';
readFreq = 0;
chrIndex = -1;
} else {
if ((chrIndex < 0) && (buffer[buffIndex] != ' ')) {
chrIndex = 0;
}else {
if (readFreq > 0) {
frequencies[readQty][chrIndex] = buffer[buffIndex];
chrIndex++;
}else {
baseNames[readQty][chrIndex] = buffer[buffIndex];
chrIndex++;
} //reading an entry
} // skip spaces after a comma only
} //skip comma and swtich to base name
}// newline and switch to frequency
} // Read & process buffer
} // open the file
return (readQty );
}
// End readBaseList
int lookupBaseNum( char *freqText){
// assume the list is ordered
long unknownFreq ;
long actualBaseFreq;
int baseNum;
char * pEnd;
baseNum = 0;
unknownFreq = strtol(freqText,&pEnd,10);
actualBaseFreq = strtol(frequencies[baseNum],&pEnd,10);
// printf("unknown: %d, Actual:%d n",unknownFreq,actualBaseFreq);
while ((baseNum <= MAX_BASES)&& (actualBaseFreq < unknownFreq)) {
baseNum++;
actualBaseFreq = strtol(frequencies[baseNum],&pEnd,10);
// printf("base: %d, unknown: %d, Actual:%d n",baseNum, unknownFreq,actualBaseFreq);
}
if (baseNum == (MAX_BASES -1)) { //penultimate entry is always reserved for Unknown
strcpy(frequencies[baseNum],freqText);
}
return (baseNum);
}
//End lookupBaseName
int lookupBaseFreq(int baseNum,char *newFreq){
if (baseNum < 0)
baseNum = 0;
if (baseNum > MAX_BASES)
baseNum = MAX_BASES;
strcpy(newFreq,frequencies[baseNum]);
return(1);
}
//End lookUpBaseFreq
// echo "$rxSig, $rxSN, $txPwr, $freq, $loaded_page" > $resultsfile
int displayRxLevel(char *resultsTxt, int showName){
//masks for LEDs
const int TX_MASK_HI = 8; //0b00001000;
//const int TX_MASK_LO = 0b11110111;
const int SN_MASK_HI = 4; // 0b00000100;
// const int SN_MASK_LO = 0b11111011;
const int RX_MASK_HI = 2; // 0b00000010;
// const int RX_MASK_LO = 0b11111101;
const int LK_MASK_HI = 1; // 0b00000001;
// const int LK_MASK_LO = 0b11111110;
// for signal levels
const int rxSigRedThresh=-16;
const int rxSigAmbThresh=-7;
const int rxSNRedThresh=21;
const int rxSNAmbThresh=25;
const int txPwrRedThresh=47;
const int txPwrAmbThresh=45;
const int txPwrLowThresh=6;
//
char newBuff[80] ;
int parmSize = 0;
int rxSig = 0;
int rxSN = 0;
int txPwr = 0;
int loadedPage =0;
char rxFreq[11] ;
char * pEnd;
char baseName [20];
int barSize =0 ;
strcpy(newBuff,"This is junk ********!!!!");
if (strlen (resultsTxt) > 16){
parmSize = parseNthString (resultsTxt, 1, newBuff);
if (parmSize > 0){
rxSig = strtol(newBuff,&pEnd,10);
}
parmSize = parseNthString (resultsTxt, 2, newBuff);
if (parmSize > 0){
rxSN = strtol(newBuff,&pEnd,10);
}
parmSize = parseNthString (resultsTxt, 3, newBuff);
if (parmSize > 0){
txPwr = strtol(newBuff,&pEnd,10);
}
strcpy(rxFreq,"100000000"); //minimum?
parmSize = parseNthString (resultsTxt, 4, rxFreq);
if (strlen(rxFreq) > 10){
rxFreq[9] =' ';
}
parmSize = parseNthString (resultsTxt, 5, newBuff);
if (parmSize > 0){
loadedPage = strtol(newBuff,&pEnd,10);
}
// adjust RxSig bar size 0 = threshold
barSize = ((rxSig - rxSigRedThresh) +1) /2; // 2dB per dot
if (loadedPage > 0) {
loadedPage = lookupBaseNum(rxFreq);
if (showName < 1) {
displaySignalFreq(barSize,rxFreq);
strcpy(thisFreq,rxFreq);
}else{
displaySignalText(barSize,(baseNames[loadedPage]));
}
} else {
loadedPage = -1;
displaySignalText(-1," NO CONNECTION! ");
}
//set txSig, rxSN, rxSig, loadedPage between 0 to 7
/************************************************************************
ledSequence[PULSE_COLOUR+2] is array of LED states to colour them
0th element not used
0 1 2 3 4 5
0 0 0 0 0 0 0 = red
1 1 0 0 0 0 0 = ambered
2 1 1 0 0 0 0 = amber
3 1 1 1 0 0 0 = orange
4 1 1 1 1 0 0 = sodium
5 1 1 1 1 1 0 = yellow
6 1 1 1 1 1 1 = green
Bit positions:
0b0 0 0 c t n r l
c = Charge
t = Tx
n = RX S/N
r = RX Level
l = Link (not used)
*************************************************************************/
// set each signal to 1, 4 or 7
if (rxSig < rxSigRedThresh) {
rxSig = 1;
}else{
if (rxSig < rxSigAmbThresh){
rxSig = 4;
}else{
rxSig = 7;
}
}
//rxSNThresh high is good
if (rxSN < rxSNRedThresh) {
rxSN = 1;
}else{
if (rxSN < rxSNAmbThresh){
rxSN = 4;
}else{
rxSN = 7;
}
}
// txPwrThresh high is bad
if ((txPwr > txPwrRedThresh)||(txPwr < txPwrLowThresh)) {
txPwr = 1;
}else{
if (txPwr > txPwrAmbThresh){
txPwr = 4;
}else{
txPwr = 7;
}
}
int timeIndex = 0;
for (timeIndex =0; (timeIndex < (PULSE_COLOUR+1)); timeIndex++){
ledSequence[timeIndex] = 0;
if (timeIndex < txPwr)
ledSequence[timeIndex] |= TX_MASK_HI;
if (timeIndex < rxSN)
ledSequence[timeIndex] |= SN_MASK_HI;
if (timeIndex < rxSig)
ledSequence[timeIndex] |= RX_MASK_HI;
if (loadedPage > -1)
ledSequence[timeIndex] |= LK_MASK_HI;
}
}
modemAskStatus();
return loadedPage;
}
//End
/******
ARGUMENTS: none used
******/
int main(int argc, char *argv[]) {
const int MAX_INPUT_DELAY = 100000;
int statusFile;
int resultsReady = -1; //no web page
int batterystate = 0;
int lastbatterystate = 0;
int rechargeDelayCount = 0;
int webpage;
int mybuff ='?';
int inputTimeOut = 0;
char newFreq[11];
int newBase = 0;
char signalLevelsText[80] = "";
int freqCursorPos = 0;
void initTS_ENTER_FREQ() {
inputTimeOut = MAX_INPUT_DELAY;
strcpy(newFreq,thisFreq);
freqCursorPos = 0;
newFreq[freqCursorPos]= mybuff;
freqCursorPos++ ;
lcdClear(serport);
showNewFreq(newFreq);
writesstr(serport,"Freq: ");
writesstr(serport, thisFreq);
thisState = TS_ENTER_FREQ;
}
void initTS_TRICKLE_CHARGE() {
lcdLamp(serport,0);
lcdClear(serport);
writesstr(serport," BATTERY is ");
writesstr(serport," fully charged!");
lcdHome(serport);
lpcDataOut =0;
}
void initTS_TURBO_CHARGE() {
lcdLamp(serport,1);
lcdClear(serport);
writesstr(serport," BATTERY IS ");
writesstr(serport," NORMAL ");
lcdHome(serport);
}
void initTS_BATT_LOW(){
lcdLamp(serport,0);
lcdClear(serport);
writesstr(serport," BATTERY IS LOW ");
lcdHome(serport);
lpcDataOut =0;
}
void checkForWebPage(){
if (flashToggle == 1) {
resultsReady = modemStatusRead( signalLevelsText);
if (resultsReady > 0) {
// display new results
thisBase =displayRxLevel(signalLevelsText,0);
if (thisBase > -1){
lastState = thisState;
thisState = TS_DISP_FREQ;
}
}
}
}
/**********************************************************
MAIN PROGRAM
**********************************************************/
// Select serial port for our LCD and set it up
serport = initport(DEVICE_NAME);
#ifdef TARGET
initLPCbusAsIO(); // Setup LPC for our own I/o!
/* Allow program to use higher I/O ports */
/* Allow program to access required I/O */
iopl(3);
ioperm(LPC_ADDR,4,1);
ioperm(GPIOBASE14,4,1);
#endif
/*************************************************
if BATT_LOW we care about nothing else
we only pulse charge. Do nothing else
else If we are in EntryMode then KEEP ENTERING
If we confirm then set new frequency
If time out then fetch results.
else if resultsReady is -1 then no modem, we must be charging
if battery goes from Normal to Charged we trickle
else if resultsReady is 0 then we wait
else if resultsReady is 1 then we get them AND see if the user wants do do anything new
User choice
**************************************************/
/*************************************************/
/* MAIN LOOP */
//modemAskStatus();
strcpy(STICK_CHARS, " +*X*+ ");
MAX_BASES = (readBaseList (BASE_LIST_FILENAME ))-1;
#ifdef DESKTOP
printf ("list of Bases read! n");
batterystate = BATT_NORMAL;
#endif
strcpy(signalLevelsText,"No");
tickTockInit(LOOP_INTERVAL);
while (1) {
tickTockWait(LOOP_INTERVAL);
#ifdef TARGET
//Read battery state
// 9 = 1001 is mask
// 0 = Charged (Trickle), 9 = Low (Pulse), 8 = Normal (Turbo)
batterystate = (inl(GPIOBASE14) >> halfbyte) & 9;
#endif
if (batterystate == BATT_LOW){
if (thisState != TS_BATT_LOW) {
lastState=thisState;
thisState= TS_BATT_LOW;
initTS_BATT_LOW();
}
}
switch (thisState) {
case (TS_ENTER_FREQ):
case (TS_ENTER_BASE):
// Continue user entry
mybuff = readsch(serport);
if (mybuff < 0){
inputTimeOut-- ;
if (inputTimeOut < 1) {
if (thisState == TS_ENTER_FREQ){
thisState =TS_DISP_FREQ;
modemAskStatus();
}
if (thisState ==TS_ENTER_BASE){
thisState =TS_DISP_BASE;
modemAskStatus();
}
}
} else {
//some user entry
inputTimeOut = MAX_INPUT_DELAY;
if (thisState == TS_ENTER_FREQ){
switch (mybuff){
case (KEY1):
case (KEY2):
case (KEY3):
case (KEY4):
case (KEY5):
case (KEY6):
case (KEY7):
case (KEY8):
case (KEY9):
case (KEY0):
newFreq[freqCursorPos]= mybuff;
freqCursorPos++;
if (freqCursorPos > 8){
newFreq[freqCursorPos] = ' ';
freqCursorPos = 0;
}
showNewFreq(newFreq);
break;
case (KEYSTAR):// switch to base Mode
lastState = thisState;
thisState = TS_ENTER_BASE;
break;
case (KEYHASH)://Select entry as freq
modemSetFreq(newFreq);
lastState = thisState;
thisState = TS_DISP_WAIT;
break;
}
} //endIf enterFreq
if (thisState ==TS_ENTER_BASE){
switch (mybuff){
case (KEYUP):
newBase--;
if (newBase < 0 )
newBase = MAX_BASES;
showBase(newBase);
break;
case (KEYDN):
newBase++;
if (newBase > MAX_BASES)
newBase = 0;
showBase(newBase);
break;
case (KEYSTAR):
thisBase = newBase;
lookupBaseFreq(thisBase,newFreq);
modemSetFreq(newFreq);
lastState = thisState;
thisState = TS_DISP_WAIT;
break;
case (KEYHASH):// switch to Freq Mode
lastState = thisState;
thisState = TS_ENTER_FREQ;
initTS_ENTER_FREQ();
break;
}
} //endIf enterBase
//end user entry
} //end if mybuff else
break;
case (TS_DISP_FREQ):
modemAskStatus();
resultsReady = modemStatusRead( signalLevelsText);
if (resultsReady > 0) {
// display new results
thisBase =displayRxLevel(signalLevelsText,0);
if (thisBase < 0){
lastState = thisState;
thisState = TS_TRICKLE_CHARGE;
}
}
mybuff = readsch(serport); //see if we want to enter
switch (mybuff){
case (KEY1):
case (KEY2):
case (KEY3):
case (KEY4):
case (KEY5):
case (KEY6):
case (KEY7):
case (KEY8):
case (KEY9):
case (KEY0):
lastState = thisState;
thisState = TS_ENTER_FREQ;
initTS_ENTER_FREQ();
break;
case (KEYSTAR):// switch to base Mode
lastState = thisState;
thisState = TS_DISP_BASE;
break;
case (KEYHASH)://Do nothing!
break;
} // end key read
break;
case (TS_DISP_BASE):
modemAskStatus();
resultsReady = modemStatusRead( signalLevelsText);
if (resultsReady > 0) {
// display new results
thisBase =displayRxLevel(signalLevelsText,1);
if (thisBase < 0){
lastState = thisState;
thisState = TS_TRICKLE_CHARGE;
initTS_TRICKLE_CHARGE();
}
}
mybuff = readsch(serport); //see if we want to enter
switch (mybuff){
case (KEY4):
case (KEY5):
case (KEY6):
case (KEY0):
break;
case (KEY1):
case (KEY2):
case (KEY3):
inputTimeOut = MAX_INPUT_DELAY;
lastState = thisState;
thisState = TS_ENTER_BASE;
newBase = thisBase;
newBase--;
if (newBase < 0)
newBase = MAX_BASES;
showBase(newBase);
break;
case (KEY7):
case (KEY8):
case (KEY9):
inputTimeOut = MAX_INPUT_DELAY;
lastState = thisState;
thisState = TS_ENTER_BASE;
newBase = thisBase;
newBase++;
if (newBase > MAX_BASES)
newBase = 0;
showBase(newBase);
break;
case (KEYSTAR):// Do nothing!
break;
case (KEYHASH):// switch to Frequency Display
lastState = thisState;
thisState = TS_DISP_FREQ;
break;
} // end key press
break;
case (TS_DISP_WAIT): //caused by Modem Reset!
if (modemFreqDone() > 0 ) {
if (lastState ==TS_ENTER_BASE){
thisState = TS_DISP_BASE;
}else{
thisState = TS_DISP_FREQ;
}
}
break;
case (TS_BATT_LOW): // No RX LEDs and Red TX LED
if ((batterystate == BATT_NORMAL) &&(lastState != TS_TURBO_CHARGE)) {
lastState = thisState;
thisState = TS_TURBO_CHARGE;
initTS_TURBO_CHARGE();
} else{
if (batterystate == BATT_FULL) {
lastState = thisState;
thisState = TS_TRICKLE_CHARGE;
initTS_TRICKLE_CHARGE();
} else {
if (pulsedCharge ==1 ) {
if (chargeToggle > 0){
lpcDataOut |= 32; // 0b00010000 charge pin high
} else {
lpcDataOut &= (255-32); // 0b11101111; // charge pin low
}
} else {
lpcDataOut = 0;
}
}
}
break;
case (TS_TURBO_CHARGE): //LEDS ON but alternate
if (batterystate == BATT_FULL) {
lastState = thisState;
thisState = TS_TRICKLE_CHARGE;
initTS_TRICKLE_CHARGE();
}
if (flashToggle == 1){
lpcDataOut =4; // 0b00000100 RX LEDS Green/Red
} else {
lpcDataOut = 2; // 0b00000010 RX LEDS Red/Green
}
if (chargeToggle > 0){
lpcDataOut |= 32; // 0b00010000 charge pin high
chargeToggle = 0;
} else {
lpcDataOut &= (255 - 32); //0b11101111 charge pin low
chargeToggle = 1;
}
checkForWebPage();
break;
case (TS_TRICKLE_CHARGE): // No RX LEDs and Red TX LED
// charging hysterisis don't switch to turbo if we have been on Turbo
if ((batterystate == BATT_NORMAL) &&(lastState != TS_TURBO_CHARGE)) {
lastState = thisState;
thisState = TS_TURBO_CHARGE;
initTS_TURBO_CHARGE();
}
checkForWebPage();
break;
}//end state machine switch
}
// end main loop
// Remove I/O permissions
#ifdef TARGET
ioperm(LPC_ADDR,4,0);
ioperm(GPIOBASE14,4,0);
#endif
return 1;
}
I'm not a Real C programmer, nor Linux/UNIX guru so don't that this is the best or even a correct solution. However it works on a Geode based Wrap2C board from PCengines.
Good stuff. To bo honest, I'm more of an arduino type when it comes to embedde stuff. It was Microchip's awful IDEs that turned me off PICs, well, that, and the fact that I'm too old for low level stuff like timers and locks.
That might have something to do with the fact that I like Python these days too...
