#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/delay.h>
typedef unsigned char BYTE;
#define TOP_SEGMENT 1<<PD0
#define TOP_RIGHT_SEGMENT 1<<PD1
#define BOTTOM_RIGHT_SEGMENT 1<<PD2
#define BOTTOM_SEGMENT 1<<PD3
#define BOTTOM_LEFT_SEGMENT 1<<PD4
#define TOP_LEFT_SEGMENT 1<<PD5
#define MIDDLE_SEGMENT 1<<PD6
inline void SEGMENT_ON(BYTE segment) { PORTD &= (~segment); }
inline void SEGMENT_OFF(BYTE segment) { PORTD |= segment; }
#define DIGIT_SELECT_ONE 1<<PB1
#define DIGIT_SELECT_TWO 1<<PB2
#define DIGIT_SELECT_THREE 1<<PB3
#define DIGIT_SELECT_FOUR 1<<PB4
#define DIGIT_SELECT_NUM 3
inline void DIGIT_ON(BYTE digit) { PORTB |= digit; }
inline void DIGIT_OFF(BYTE digit) { PORTB &= (~digit); }
//#define GET_TIMER_COUNT_FROM_MS(NumMS,Prescale) (int)((1000000.0/Prescale) / (1000.0/NumMS) - 1.0)
//Let's define a variable called brightness that varies from:
//5000 blindingly bright (15.7mA current draw per digit)
//2000 shockingly bright (11.4mA current draw per digit)
//1000 pretty bright (5.9mA)
//500 normal (3mA)
//200 dim but readable (1.4mA)
//50 dim but readable (0.56mA)
//5 dim but readable (0.31mA)
//1 dim but readable in dark (0.28mA)
#define DISPLAY_BRIGHTNESS 1000
BYTE GDisplayDigits[DIGIT_SELECT_NUM];
inline void SelectDigit(BYTE digitNum)
{
switch (digitNum)
{
case 0:
{
DIGIT_OFF(DIGIT_SELECT_TWO);
DIGIT_OFF(DIGIT_SELECT_THREE);
DIGIT_OFF(DIGIT_SELECT_FOUR);
DIGIT_ON(DIGIT_SELECT_ONE);
}
break;
case 1:
{
DIGIT_OFF(DIGIT_SELECT_ONE);
DIGIT_OFF(DIGIT_SELECT_THREE);
DIGIT_OFF(DIGIT_SELECT_FOUR);
DIGIT_ON(DIGIT_SELECT_TWO);
}
break;
case 2:
{
DIGIT_OFF(DIGIT_SELECT_ONE);
DIGIT_OFF(DIGIT_SELECT_TWO);
DIGIT_OFF(DIGIT_SELECT_FOUR);
DIGIT_ON(DIGIT_SELECT_THREE);
}
break;
case 3:
{
DIGIT_OFF(DIGIT_SELECT_ONE);
DIGIT_OFF(DIGIT_SELECT_TWO);
DIGIT_OFF(DIGIT_SELECT_THREE);
DIGIT_ON(DIGIT_SELECT_FOUR);
}
break;
default:
{
DIGIT_OFF(DIGIT_SELECT_ONE);
DIGIT_OFF(DIGIT_SELECT_TWO);
DIGIT_OFF(DIGIT_SELECT_THREE);
DIGIT_OFF(DIGIT_SELECT_FOUR);
}
break;
};
}
void DisplayNumberOnSelectedDigit(BYTE number)
{
switch (number)
{
case 0:
{
SEGMENT_ON(TOP_SEGMENT);
SEGMENT_ON(TOP_RIGHT_SEGMENT);
SEGMENT_ON(BOTTOM_RIGHT_SEGMENT);
SEGMENT_ON(BOTTOM_SEGMENT);
SEGMENT_ON(BOTTOM_LEFT_SEGMENT);
SEGMENT_ON(TOP_LEFT_SEGMENT);
SEGMENT_OFF(MIDDLE_SEGMENT);
}
break;
case 1:
{
SEGMENT_OFF(TOP_SEGMENT);
SEGMENT_ON(TOP_RIGHT_SEGMENT);
SEGMENT_ON(BOTTOM_RIGHT_SEGMENT);
SEGMENT_OFF(BOTTOM_SEGMENT);
SEGMENT_OFF(BOTTOM_LEFT_SEGMENT);
SEGMENT_OFF(TOP_LEFT_SEGMENT);
SEGMENT_OFF(MIDDLE_SEGMENT);
}
break;
case 2:
{
SEGMENT_ON(TOP_SEGMENT);
SEGMENT_ON(TOP_RIGHT_SEGMENT);
SEGMENT_OFF(BOTTOM_RIGHT_SEGMENT);
SEGMENT_ON(BOTTOM_SEGMENT);
SEGMENT_ON(BOTTOM_LEFT_SEGMENT);
SEGMENT_OFF(TOP_LEFT_SEGMENT);
SEGMENT_ON(MIDDLE_SEGMENT);
}
break;
case 3:
{
SEGMENT_ON(TOP_SEGMENT);
SEGMENT_ON(TOP_RIGHT_SEGMENT);
SEGMENT_ON(BOTTOM_RIGHT_SEGMENT);
SEGMENT_ON(BOTTOM_SEGMENT);
SEGMENT_OFF(BOTTOM_LEFT_SEGMENT);
SEGMENT_OFF(TOP_LEFT_SEGMENT);
SEGMENT_ON(MIDDLE_SEGMENT);
}
break;
case 4:
{
SEGMENT_OFF(TOP_SEGMENT);
SEGMENT_ON(TOP_RIGHT_SEGMENT);
SEGMENT_ON(BOTTOM_RIGHT_SEGMENT);
SEGMENT_OFF(BOTTOM_SEGMENT);
SEGMENT_OFF(BOTTOM_LEFT_SEGMENT);
SEGMENT_ON(TOP_LEFT_SEGMENT);
SEGMENT_ON(MIDDLE_SEGMENT);
}
break;
case 5:
{
SEGMENT_ON(TOP_SEGMENT);
SEGMENT_OFF(TOP_RIGHT_SEGMENT);
SEGMENT_ON(BOTTOM_RIGHT_SEGMENT);
SEGMENT_ON(BOTTOM_SEGMENT);
SEGMENT_OFF(BOTTOM_LEFT_SEGMENT);
SEGMENT_ON(TOP_LEFT_SEGMENT);
SEGMENT_ON(MIDDLE_SEGMENT);
}
break;
case 6:
{
SEGMENT_ON(TOP_SEGMENT);
SEGMENT_OFF(TOP_RIGHT_SEGMENT);
SEGMENT_ON(BOTTOM_RIGHT_SEGMENT);
SEGMENT_ON(BOTTOM_SEGMENT);
SEGMENT_ON(BOTTOM_LEFT_SEGMENT);
SEGMENT_ON(TOP_LEFT_SEGMENT);
SEGMENT_ON(MIDDLE_SEGMENT);
}
break;
case 7:
{
SEGMENT_ON(TOP_SEGMENT);
SEGMENT_ON(TOP_RIGHT_SEGMENT);
SEGMENT_ON(BOTTOM_RIGHT_SEGMENT);
SEGMENT_OFF(BOTTOM_SEGMENT);
SEGMENT_OFF(BOTTOM_LEFT_SEGMENT);
SEGMENT_OFF(TOP_LEFT_SEGMENT);
SEGMENT_OFF(MIDDLE_SEGMENT);
}
break;
case 8:
{
SEGMENT_ON(TOP_SEGMENT);
SEGMENT_ON(TOP_RIGHT_SEGMENT);
SEGMENT_ON(BOTTOM_RIGHT_SEGMENT);
SEGMENT_ON(BOTTOM_SEGMENT);
SEGMENT_ON(BOTTOM_LEFT_SEGMENT);
SEGMENT_ON(TOP_LEFT_SEGMENT);
SEGMENT_ON(MIDDLE_SEGMENT);
}
break;
case 9:
{
SEGMENT_ON(TOP_SEGMENT);
SEGMENT_ON(TOP_RIGHT_SEGMENT);
SEGMENT_ON(BOTTOM_RIGHT_SEGMENT);
SEGMENT_ON(BOTTOM_SEGMENT);
SEGMENT_OFF(BOTTOM_LEFT_SEGMENT);
SEGMENT_ON(TOP_LEFT_SEGMENT);
SEGMENT_ON(MIDDLE_SEGMENT);
}
break;
default:
{
SEGMENT_OFF(TOP_SEGMENT);
SEGMENT_OFF(TOP_RIGHT_SEGMENT);
SEGMENT_OFF(BOTTOM_RIGHT_SEGMENT);
SEGMENT_OFF(BOTTOM_SEGMENT);
SEGMENT_OFF(BOTTOM_LEFT_SEGMENT);
SEGMENT_OFF(TOP_LEFT_SEGMENT);
SEGMENT_OFF(MIDDLE_SEGMENT);
}
break;
};
}
void SetNumberToDisplay(int16_t number)
{
#if DIGIT_SELECT_NUM == 4
GDisplayDigits[0] = number / 1000;
number -= GDisplayDigits[0]*1000;
GDisplayDigits[1] = number / 100;
number -= GDisplayDigits[1]*100;
GDisplayDigits[2] = number / 10;
number -= GDisplayDigits[2]*10;
GDisplayDigits[3] = number / 1;
#elif DIGIT_SELECT_NUM == 3
GDisplayDigits[0] = number / 100;
number -= GDisplayDigits[0]*100;
GDisplayDigits[1] = number / 10;
number -= GDisplayDigits[1]*10;
GDisplayDigits[2] = number / 1;
#else
#error digit select num define not 3 or 4
#endif
}
void DisplayCurrentNumber()
{
for (BYTE digit = 0; digit < DIGIT_SELECT_NUM; ++digit)
{
SelectDigit(digit);
DisplayNumberOnSelectedDigit(GDisplayDigits[digit]);
_delay_us(DISPLAY_BRIGHTNESS);
}
SelectDigit(-1);
}
int ADCsingleREAD(uint8_t adcToUse)
{
int ADCval;
ADMUX &= (0b11111000); //Clear out the channels we're reading
ADMUX |= adcToUse; //Set the new channel we're going to read
ADCSRA |= (1 << ADSC); // Start the ADC conversion
while(!(ADCSRA & (1 << ADIF))); // Wait for the ADC to finish
//Clear ADIF by writing one to it
ADCSRA|=(1<<ADIF);
ADCval = ADCL;
ADCval = (ADCH << 8) + ADCval; // ADCH is read so ADC can be updated again
return ADCval;
}
int main(void)
{
DDRD = TOP_SEGMENT | TOP_RIGHT_SEGMENT | BOTTOM_RIGHT_SEGMENT | BOTTOM_SEGMENT | BOTTOM_LEFT_SEGMENT | TOP_LEFT_SEGMENT | MIDDLE_SEGMENT;
DDRB = DIGIT_SELECT_ONE | DIGIT_SELECT_TWO | DIGIT_SELECT_THREE | DIGIT_SELECT_FOUR;
PORTD = 0;
PORTB = 0;
//Initialize the analog to digital converter
ADMUX = (1 << REFS0); // use AVcc as the reference
ADMUX &= ~(1 << ADLAR); // clear for 10 bit resolution
ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1); // 64 prescale for 8Mhz and enabled the ADC with ADEN
while(1)
{
int moistureSensorValue = ADCsingleREAD(ADC5D);
#if DIGIT_SELECT_NUM == 4
if (moistureSensorValue > 9999 || moistureSensorValue < 0)
moistureSensorValue = 9999;
#elif DIGIT_SELECT_NUM == 3
if (moistureSensorValue > 999 || moistureSensorValue < 0)
moistureSensorValue = 999;
#else
#error unknown digit select num
#endif
const float runningAverageLERP = 0.95f;
float moistureSensorValueAve = runningAverageLERP*moistureSensorValueAve + (1.0f-runningAverageLERP)*moistureSensorValue;
SetNumberToDisplay((int16_t)moistureSensorValueAve);
DisplayCurrentNumber();
}
}