Hello, in my project, i want to use a water level sensor, LED's to represent water depth, and a LCD screen with I2C to represent the water level in words. However, i am having timing issues between the ISR's for the LED's and the writing of characters on the LCD. I have tried shifting the position of the codes but from what i can see, i cannnot seem to seperate the ISR's from the main loop code. my understanding is that the main loop runs automatically, and the ISR's are called accoring to their initialisaiton. However, is there a way to run both codes simultanesouly, so that the 1 ms of Timer0 does not affect the speed at which the I2C and LCD need to print out the characters? it seems that the LCD prints out characters but gets stuck after a while and does not continue looping. Thank you. The code dealing with the motor is an extension, however i want to understand how to fix this problem first.
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <string.h>
#include <stdio.h>
#include <util/twi.h>
#include <stdlib.h>
#include <math.h>
#define LCD_RS 0 // Register select (0-instruction, 1-data)
#define LCD_Rw 1 // Read (1) or Write (0)
#define LCD_EN 2 // Enable (1->0) transition fetchs data
#define LCD_BL 3 // LCD Backlight
char line1[100]; // string buffers for varying chracter lines
char line2[100];
volatile int led_blink_time = 0;
volatile int adc_value;
unsigned int on_off = 0;
void init_twi(); // Initialise Atmegar328P-TWI(I2C, two wire interface)
int putchar_twi(uint8_t ch); // ensures that byte information is corectly sent, stored and written for the LCD.
// LCD+TWI functions to send a byte/string
void init_lcd(); // formats LCD to properly display characters.
void lcd_light(int on); // turns on the backlight of the LCD.
void putchar_lcd(uint8_t ch, int rs); // Connects 2x 4bits into a byte to be sent to the LCD.
void puts_lcd_a(char *s);
void puts_lcd_b(char *s);
// Delay functions used to interface LCD
void delay1ms(); // delay functions used to properly set up LCD
void delay15ms();
void ADC_init(void)
{
ADMUX |= (1<<6);
ADCSRA |= 0b10111111;
ADCSRA |= (1<<6);
}
void Timer0_Int(void)
{
TCCR0A = (1<<WGM01);
TCCR0B = (1<<CS00)|(1<<CS01);
TIMSK0 = (1<<OCIE0A);
OCR0A = 250-1;
}
ISR(TIMER0_COMPA_vect)
{
led_blink_time++;
}
ISR(ADC_vect)
{
adc_value = ADC;
if(adc_value < 550 && led_blink_time > 500)
{
PORTD &= ~((1<<1)|(1<<2));
PORTD ^= (1<<0);
led_blink_time = 0;
}
if(adc_value < 650 && adc_value > 550 && led_blink_time > 500)
{
PORTD |= (1<<0);
PORTD &= ~(1<<2);
PORTD ^= (1<<1);
led_blink_time = 0;
}
if(adc_value < 690 && adc_value > 650 && led_blink_time > 500)
{
PORTD |= (1<<0)|(1<<1);
PORTD ^= (1<<2);
led_blink_time = 0;
}
if(adc_value > 690)
{
PORTD |= (1<<0)|(1<<1)|(1<<2);
}
}
int main(void)
{
DDRD = 0b00000111;
init_twi(); // Initialise Atmegar328P-TWI(I2C, two wire interface)
init_lcd(); // Initialise LCD HD44780 + PCF8574T (I2C)
ADC_init();
Timer0_Int();
sei(); // Global variabe used for ISR.
while(1)
{
if(on_off == 0)
{
putchar_lcd(0b10000000, 0);
sprintf(line1, "Motor: OFF"); // stores first line of LCD in string line1
puts_lcd_a(line1); // sends string to for loop for LCD writing.
}
if(on_off == 1)
{
putchar_lcd(0b10000000, 0);
sprintf(line1, "Motor: ON"); // stores first line of LCD in string line1
puts_lcd_a(line1); // sends string to for loop for LCD writing.
}
if(adc_value < 550)
{
putchar_lcd(0b11000000, 0);
sprintf(line2, "Water: 0/4-1/4"); // stores second line of LCD in string line2
puts_lcd_b(line2); // sends string to for loop for LCD writing
}
if(adc_value < 650 && adc_value > 550)
{
putchar_lcd(0b11000000, 0);
sprintf(line2, "Water: 1/4-2/4"); // stores second line of LCD in string line2
puts_lcd_b(line2); // sends string to for loop for LCD writing
}
if(adc_value < 690 && adc_value > 650)
{
putchar_lcd(0b11000000, 0);
sprintf(line2, "Water: 2/4-3/4"); // stores second line of LCD in string line2
puts_lcd_b(line2); // sends string to for loop for LCD writing
}
if(adc_value > 690)
{
putchar_lcd(0b11000000, 0);
sprintf(line2, "Water: 3/4-4/4"); // stores second line of LCD in string line2
puts_lcd_b(line2); // sends string to for loop for LCD writing
}
}
}
void init_twi()
{
PORTC = (1<<DDC5)|(1<<DDC4); // Activate the pull-up resistors for SDA/SCL(TWI).
// Otherwise, you need to attach external pull-ups
TWSR = ((1<<TWPS1)|(1<<TWPS0)); // PS-64, sets TWI clock to 1Kbps, if ~ is used, then bps is faster.
TWBR = 124; // Bit rate: TWBR = ((F_CPU / 1000) - 16) / (2*PS)
TWDR = 0xFF; // Default content = SDA released.
TWCR = (1<<TWEN); // Enable TWI-interface and release TWI pins.
}
int putchar_twi(uint8_t ch)
{
uint8_t addr = 0x27; // LCD default address (0x27). Check your LCD board
// 1. Start (From Table 26.2, page 270, Atmega328p datasheet (single chip))
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN) | (1<<TWEA);
while (!(TWCR & (1<<TWINT)));
if ((TWSR & 0xF8) != TW_START)
return -1;
// 2. Send SLA+W (Write Mode)
TWDR = (addr << 1) | (TW_WRITE); // SLA+W
TWCR = (1<<TWINT)|(1<<TWEN); // Start transmission
while (!(TWCR & (1<<TWINT)));
if ((TWSR & 0xF8) != TW_MT_SLA_ACK)
return -2;
// 3. Send Data #1 (actual data)
TWDR = ch; // Data (at the sub-address register)
TWCR = (1<<TWINT)|(1<<TWEN); // Start transmission
while (!(TWCR & (1<<TWINT)));
if ((TWSR & 0xF8) != TW_MT_DATA_ACK)
return -4;
// 4. Stop condition
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
return 0;
}
void init_lcd()
{
uint8_t ch;
// Atmega hardware reset doesn't affect LCD, so we do software reset here (32ms+)
// function set 1 (repeat 3 times). Ref) Fig. 24 @ Page 46, HD44780U datasheet
delay15ms();
delay15ms();
ch = 0x30|(1<<LCD_BL); // reset (0x30)
for (int i=0; i<3; i++){
ch |= (1<<LCD_EN);
putchar_twi(ch);
delay15ms();
ch &= ~(1<<LCD_EN);
putchar_twi(ch);
delay15ms();
}
// Send command (0x20) to set LCD to 4-bit mode
// Ref) Table 12 @ Page 42, HD44780U datasheet
ch = 0x20|(1<<LCD_BL);
ch |= (1<<LCD_EN);
putchar_twi(ch);
delay1ms();
ch &= ~(1<<LCD_EN);
putchar_twi(ch);
delay1ms();
// [TASK 2] Complete the code:
// Send commands to instruction register (RS=0)
// to initialise LCD. A byte is splited into two 4-bits
// Ref) Table 12 @ Page 42, HD44780U datasheet
// Ref) Fig. 11 @ Page 28, HD44780U datasheet
// The instructions to be sent in sequence are:
// - (set 2-line mode with font size)
putchar_lcd(0b00101000, 0);
// - (display off)
putchar_lcd(0b00001000, 0);
// - (clear display)
putchar_lcd(0b00000001, 0);
// - (set entry mode)
putchar_lcd(0b0000000110, 0);
// - (display on and cursor blinking)
putchar_lcd(0b00001110, 0);
// Now you can send data to data register (RS=1)
// To move the cursor to next line first column (0x40 in LCD)
// you need to send an instruction with (RS=0)
;
}
void putchar_lcd(uint8_t ch, int rs)
{
uint8_t logi;
logi = ((ch >> 4 & 0x0F) << 4)|(1<<LCD_BL)|rs; //High nibble
logi |= (1<<LCD_EN);
putchar_twi(logi);
delay1ms();
logi &= ~(1<<LCD_EN);
putchar_twi(logi);
delay1ms();
logi = ((ch & 0X0F) << 4)|(1<<LCD_BL)|rs; //LOW nibble
logi |= (1<<LCD_EN);
putchar_twi(logi);
delay1ms();
logi &= ~(1<<LCD_EN);
putchar_twi(logi);
delay1ms();
}
void puts_lcd_a(char *s)
{
unsigned int i = 0;
for(i = 0; i < strlen(line1); i++)
{
putchar_lcd(*(s+i), 1);
}
}
void puts_lcd_b(char *s)
{
unsigned int i = 0;
for(i = 0; i < strlen(line2); i++)
{
putchar_lcd(*(s+i), 1);
}
}
void lcd_light(int on)
{
char temp = 0x0|(1<<LCD_RS); // write to data reg
if (on==1){
temp |= (1<<LCD_BL); // back light bit on
}
putchar_twi(temp);
delay1ms();
}
void delay1ms()
{
TCCR2B = 0x04;
while(!TIFR2);
TCCR2B = 0x00;
TCNT2 = 0;
TIFR2 |= 0x01;
}
void delay15ms()
{
TCCR2B = 0x07;
while(!TIFR2);
TCCR2B = 0x00;
TCNT2 = 0;
TIFR2 |= 0x01;
}