#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <util/twi.h>
#define led_toggle() (PORTB ^= _BV(PB5))
static void blink(void)
{
DDRB |= _BV(DDB5);
for (uint8_t i = 0; i < 6; i++) {
led_toggle();
_delay_ms(400);
}
}
/* I2C */
#define I2C_FREQ_HZ 100000U
#define I2C_PRESCALER 4U
#define I2C_WRITE 0x00U
#define I2C_READ 0x01U
void i2c_init(void)
{
/* Prescaler: 4 */
TWSR |= _BV(TWPS0);
/* Bit rate: */
TWBR = (F_CPU / I2C_FREQ_HZ - 16) / (2 * I2C_PRESCALER);
}
static int8_t i2c_rw(uint8_t addr, uint8_t data, uint8_t rw)
{
/* Send START condition */
TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);
loop_until_bit_is_set(TWCR, TWINT);
if ((TWSR & 0xF8) != TW_START) {
return -1;
}
/* Start transmission of address */
TWDR = ((addr << 1) | rw);
TWCR = _BV(TWINT) | _BV(TWEN);
loop_until_bit_is_set(TWCR, TWINT);
if ((TWSR) != TW_MT_SLA_ACK) {
return -1;
}
/* Start transmission of data */
TWDR = data;
TWCR = _BV(TWINT) | _BV(TWEN);
loop_until_bit_is_set(TWCR, TWINT);
if ((TWSR & 0xF8) != TW_MT_DATA_ACK) {
return -1;
}
/* Transmit STOP condition */
TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO);
return 0;
}
int8_t i2c_read(uint8_t addr, uint8_t data)
{
return i2c_rw(addr, data, I2C_READ);
}
int8_t i2c_write(uint8_t addr, uint8_t data)
{
return i2c_rw(addr, data, I2C_WRITE);
}
/* LCD */
/* LCD pins and address definitions */
#define LCD_I2C_ADDR 0x27
#define LCD_BACKLIGHT 0x08
#define LCD_PIN_EN 0x04
#define LCD_PIN_RS 0x01
/* Define LCD display commands */
#define LCD_CMD_CLEAR 0x01 /* Clear the display */
#define LCD_CMD_CURSOR_HOME 0x02 /* Set cursor to the home (0,0) position */
#define LCD_CMD_CURSOR_LSHIFT 0x04 /* Shift cursor left */
#define LCD_CMD_CURSOR_RSHIFT 0x06 /* Shift cursor right */
#define LCD_CMD_DISPLAY_RSHIFT 0x05 /* Shift display right */
#define LCD_CMD_DISPLAY_LSHIFT 0x07 /* Shift display left */
#define LCD_CMD_D_OFF_C_OFF 0x08 /* Display OFF, cursor OFF */
#define LCD_CMD_D_OFF_C_ON 0x0A /* Display OFF, cursor ON */
#define LCD_CMD_D_ON_C_OFF 0x0C /* Display ON, cursor OFF */
#define LCD_CMD_D_ON_C_BLK 0x0E /* Display ON, cursor blinking */
#define LCD_CMD_CURSOR_ROW1 0x80 /* Set the cursor to the first row */
#define LCD_CMD_CURSOR_ROW2 0xC0 /* Set the cursor to the second row */
#define LCD_CMD_4BIT_MODE 0x28 /* Set LCD to operate in 4-bit mode */
#define LCD_CMD_8BIT_MODE 0x38 /* Set LCD to operate in 8-bit mode */
static void lcd_send_cmd(uint8_t cmd)
{
uint8_t upper_nibble = cmd & 0xF0;
uint8_t lower_nibble = (cmd << 4) & 0xF0;
i2c_write(LCD_I2C_ADDR, upper_nibble | 0x0C);
i2c_write(LCD_I2C_ADDR, upper_nibble | 0x08);
i2c_write(LCD_I2C_ADDR, lower_nibble | 0x0C);
i2c_write(LCD_I2C_ADDR, lower_nibble | 0x08);
}
static void lcd_send_data(uint8_t data)
{
uint8_t upper_nibble = data & 0xF0;
uint8_t lower_nibble = (data << 4) & 0xF0;
i2c_write(LCD_I2C_ADDR, upper_nibble | 0x0D);
i2c_write(LCD_I2C_ADDR, upper_nibble | 0x09);
i2c_write(LCD_I2C_ADDR, lower_nibble | 0x0D);
i2c_write(LCD_I2C_ADDR, lower_nibble | 0x09);
}
void lcd_init(void)
{
_delay_ms(50);
lcd_send_cmd(0x30);
_delay_ms(5);
lcd_send_cmd(0x30);
_delay_us(101);
lcd_send_cmd(0x30);
_delay_ms(10);
lcd_send_cmd(0x20);
_delay_ms(10);
lcd_send_cmd(LCD_CMD_4BIT_MODE);
_delay_ms(10);
lcd_send_cmd(LCD_CMD_D_OFF_C_OFF);
_delay_ms(10);
lcd_send_cmd(LCD_CMD_CLEAR);
_delay_ms(10);
lcd_send_cmd(LCD_CMD_CURSOR_RSHIFT);
_delay_ms(10);
lcd_send_cmd(LCD_CMD_D_ON_C_OFF);
_delay_ms(10);
}
void lcd_set_pos(uint8_t row, uint8_t col)
{
if (row)
col |= LCD_CMD_CURSOR_ROW2;
else
col |= LCD_CMD_CURSOR_ROW1;
lcd_send_cmd(col);
}
void lcd_puts(const char *s)
{
while (*s) {
lcd_send_data(*s++);
}
}
void lcd_putchar(char c)
{
lcd_send_data(c);
}
void lcd_clear()
{
lcd_send_cmd(LCD_CMD_CLEAR);
_delay_ms(1);
}
/* MAIN */
int main(void)
{
blink();
for (;;) {
lcd_puts("Hello World");
_delay_ms(2000);
lcd_clear();
_delay_ms(2000);
}
}