#include <string.h>
#include <inttypes.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <time.h>
#include <avr/interrupt.h>
#include <avr/io.h>
/****************************************************************************/
/******************** MACROS FOR LCD WIRING AND WRITING *********************/
/****************************************************************************/
#define LCD4 PD2
#define LCD5 PD3
#define LCD6 PD4
#define LCD7 PD5
#define ENCODER_DT PB2
#define ENCODER_CLOCK PB1
#define ENABLE PB3
#define RS PB4
#define ANALOG_RESOLUTION 1024
#define MAX_EXPOSURE_TIME 24
#define EXPOSURE_STEP ANALOG_RESOLUTION / MAX_EXPOSURE_TIME
#define DDRAM_BASE_ADDRESS0 0x00
#define DDRAM_BASE_ADDRESS1 0x40
#define DDRAM_BASE_ADDRESS2 0x14
#define DDRAM_BASE_ADDRESS3 0x54
#define DDRAM_POT_VALUE DDRAM_BASE_ADDRESS2 + 17
#define CONFIG_SIZE 3
#define PORTD_DATA_MASK 0b11000011
#define DATA_MASK 0b00001111
// Typical page format for 20x4 LCD
typedef struct{
char* ln1;
char* ln2;
char* ln3;
char* ln4;
short page;
}lcdText;
/****************************************************************************/
/**************************** LCD WRITING FUNCTIONS *************************/
/****************************************************************************/
/* Variables */
bool intialisation_complete = false;
bool triggered = true;
/* Functions */
void clearDisplay();
void set4BitMode();
void returnHome();
void displayOnNoCursor();
void writeData(unsigned char data);
void writeStringData(char* data);
void shiftCursor(char direction, char distance);
void setDDRAMAddr(char addr);
void pulseEnable();
/****************************************************************************/
/****************** CONFIGURATIONS CONTROL USER INTERFACE *******************/
/****************************************************************************/
/* Variables */
bool pinStatus = false;
bool LCDupdated = false;
bool DDRAMupdated = false;
lcdText* HOMElcd;
lcdText* EXPOSURElcd;
lcdText* NUTRIENTSlcd;
lcdText* WATERFLOWlcd;
lcdText* currentLCD;
lcdText* nextLCD;
char currentDDRAM;
char nextDDRAM;
uint8_t encoder_counter = 0;
int aState;
int aLastState;
char config_cursor[CONFIG_SIZE] = {DDRAM_BASE_ADDRESS1+18, DDRAM_BASE_ADDRESS2+18, DDRAM_BASE_ADDRESS3+18};
lcdText* configurations[CONFIG_SIZE];
short config_cursor_counter = 0;
short exposureTime = 0;
/* Functions */
lcdText* create_lcdText(char* ln1, char* ln2, char* ln3, char* ln4, short page);
void lcdTextInit(lcdText* homeScreen);
uint8_t value_from_potentiometer(uint16_t potValue, uint16_t step);
void write_pot_value(uint8_t potValue, char step);
/****************************************************************************/
/*************************** SETUP AND MAIN LOOP ****************************/
/****************************************************************************/
void setup() {
Serial.begin(9600);
//LCD pins and setup
DDRD |= (1<<DDD2) | (1<<DDD3) | (1<<DDD4) | (1<<DDD5);
DDRB |= (1<<DDB0) | (1<<DDB3) | (1<<DDB4) | (1<<DDB5);
PORTB &= ~(1<<ENABLE) & ~(1<<RS) & ~(1<<DDB1) & ~(1<<DDB2);
aLastState = digitalRead(ENCODER_DT);
LCDinit4();
//Interrupts
pinMode(A5, INPUT);
pinMode(8, OUTPUT);
pinMode(6, INPUT_PULLUP);
pinMode(7, INPUT_PULLUP);
pinMode(0, INPUT_PULLUP);
PCICR |= 1<<PCIE2;
PCMSK2 |= (1<<PCINT22) | (1<<PCINT23) | (1<<PCINT16);
// LCD config pages
HOMElcd = create_lcdText("Choose a config", "[1] Exposure [ ]","[2] Nutrients [ ]", "[3] Water flow [ ]", 0);
EXPOSURElcd = create_lcdText("Configure Exposure", "Current exposure: ", "Set exposure to: ", "(back) (store)", 0);
NUTRIENTSlcd = create_lcdText("Configure Nutrients", "Current Nutrients: ", "Set nutrients to: ", "(back) (store)", 0);
WATERFLOWlcd = create_lcdText("Configure Waterflow", "Current Waterflow: ", "Set waterflow to: ", "(back) (store)", 0);
configurations[0]=EXPOSURElcd;
configurations[1]=NUTRIENTSlcd;
configurations[2]=WATERFLOWlcd;
currentLCD = HOMElcd;
nextLCD = currentLCD;
lcdTextInit(currentLCD);
currentDDRAM = config_cursor[config_cursor_counter];
nextDDRAM=currentDDRAM;
setDDRAMAddr(currentDDRAM);
intialisation_complete = true;
}
void loop() {
aState = digitalRead(ENCODER_DT);
if(aState != aLastState){
if(digitalRead(ENCODER_CLOCK) != aState){
encoder_counter++;
}
else{
encoder_counter--;
}
Serial.println("Position");
Serial.println(encoder_counter);
}
aLastState = aState;
//if(LCDupdated){
// lcdTextInit(nextLCD);
// LCDupdated = false;
//}
//if(DDRAMupdated && !LCDupdated){
// setDDRAMAddr(nextDDRAM);
// DDRAMupdated = false;
//}
}
/****************************************************************************/
/*********************** INTERRUPT SERVICE ROUTINES *************************/
/****************************************************************************/
ISR (PCINT2_vect)
{
if(!intialisation_complete){
return;
}
if (digitalRead(6) == LOW && digitalRead(7) != LOW){
digitalWrite(8,triggered);
triggered = !triggered;
config_cursor_counter = ++config_cursor_counter % CONFIG_SIZE;
nextDDRAM = config_cursor[config_cursor_counter];
DDRAMupdated = true;
setDDRAMAddr(config_cursor[config_cursor_counter]);
}
if(digitalRead(7) == LOW){
clearDisplay();
nextLCD = configurations[config_cursor_counter];
LCDupdated = true;
lcdTextInit(configurations[config_cursor_counter]);
}
if(digitalRead(0) == LOW){
clearDisplay();
config_cursor_counter = 0;
nextDDRAM = config_cursor[config_cursor_counter];
nextLCD = HOMElcd;
LCDupdated = true;
DDRAMupdated = true;
lcdTextInit(HOMElcd);
setDDRAMAddr(config_cursor[config_cursor_counter]);
}
}
/****************************************************************************/
/****************** CONFIGURATIONS CONTROL USER INTERFACE *******************/
/****************************************************************************/
lcdText* create_lcdText(char* ln1, char* ln2, char* ln3, char* ln4, short page){
lcdText* newLcdText = (lcdText*)malloc(sizeof(lcdText));
assert(newLcdText);
newLcdText->ln1 = malloc(strlen(ln1)+1);
assert(newLcdText->ln1);
strcpy(newLcdText->ln1, ln1);
newLcdText->ln2 = malloc(strlen(ln2)+1);
assert(newLcdText->ln2);
strcpy(newLcdText->ln2, ln2);
newLcdText->ln3 = malloc(strlen(ln3)+1);
assert(newLcdText->ln3);
strcpy(newLcdText->ln3, ln3);
newLcdText->ln4 = malloc(strlen(ln4)+1);
assert(newLcdText->ln4);
strcpy(newLcdText->ln4, ln4);
newLcdText->page = page;
return newLcdText;
}
void lcdTextInit(lcdText* homeScreen){
setDDRAMAddr(DDRAM_BASE_ADDRESS0);
writeStringData(homeScreen->ln1);
setDDRAMAddr(DDRAM_BASE_ADDRESS1);
writeStringData(homeScreen->ln2);
setDDRAMAddr(DDRAM_BASE_ADDRESS2);
writeStringData(homeScreen->ln3);
setDDRAMAddr(DDRAM_BASE_ADDRESS3);
writeStringData(homeScreen->ln4);
}
/****************************************************************************/
/***************************** LCD WRITING FUNCTIONS ************************/
/****************************************************************************/
void pulseEnable(){
PORTB |= (1<<ENABLE);
delayMicroseconds(1);
PORTB &= ~(1<<ENABLE);
}
void setDDRAMAddr(char addr){
PORTB &= ~(1<<ENABLE) & ~(1<<RS);
PORTD &= PORTD_DATA_MASK;
addr |= B10000000;
PORTD |= (addr>>2) & ~PORTD_DATA_MASK;
pulseEnable();
PORTD &= PORTD_DATA_MASK;
PORTD |= (addr<<2) & ~PORTD_DATA_MASK;
pulseEnable();
}
void shiftCursor(char direction, char distance){
short shiftMask = direction << 4;
PORTB &= ~(1<<ENABLE) & ~(1<<RS);
for(short i = 0; i<distance;i++){
PORTD &= PORTD_DATA_MASK;
PORTD |= (1<<LCD4);
pulseEnable();
PORTD &= PORTD_DATA_MASK;
PORTD |= shiftMask;
pulseEnable();
delay(1000);
}
}
void set4BitMode(){
PORTD &= PORTD_DATA_MASK;
PORTD |= (1<<LCD5);
pulseEnable();
}
void clearDisplay(){
PORTD &= PORTD_DATA_MASK;
pulseEnable();
PORTD |= (1<<LCD4);
pulseEnable();
}
void returnHome(){
PORTB &= ~(1<<ENABLE) & ~(1<<RS);
PORTD &= PORTD_DATA_MASK;
pulseEnable();
PORTD |= (1<<LCD5);
pulseEnable();
}
void displayOnNoCursor(){
PORTD &= PORTD_DATA_MASK;
pulseEnable();
PORTD |= (1<<LCD6) | (1<<LCD7) | (1<<LCD4);
pulseEnable();
}
void LCDinit4(){
set4BitMode();
clearDisplay();
returnHome();
displayOnNoCursor();
}
/**** Writing functions for 4 bit mode LCD ****/
void writeData(unsigned char data){
unsigned char dataCpy = data;
unsigned char portData;
PORTB |= (1<<RS);
//Clear used data bits for PORTD
PORTD &= PORTD_DATA_MASK;
// Put MS4B of data to the right position
// eg. data=0b01000010, save=0b00010000
portData = (dataCpy>>2) & ~PORTD_DATA_MASK;
PORTD |= portData;
pulseEnable();
//Clear used data bits for PORTD
PORTD &= PORTD_DATA_MASK;
// Put LS4B of data to the right position
// eg. data=0b01000010, save=0b00001000
portData = (dataCpy<<2) & ~PORTD_DATA_MASK;
PORTD |= portData;
pulseEnable();
}
void writeStringData(char* data){
char lenStr = strlen(data);
for(char i = 0; i < lenStr; i++){
writeData(data[i]);
}
}