Files
aaio/aaio.h
T
2024-11-17 21:00:37 +02:00

408 lines
9.2 KiB
C

#ifndef _AAIO_H
#define _AAIO_H
/*
AVR All-in-one: a single-header library in pure C (tailored to avr-gcc)
for some typical AVR ATmega series usecases
Tested on: Arduino Mega 2560 (ATmega2560), Arduino Nano (ATmega328P)
Individually selectable features:
* controlling internal LED (overridable to any other pin);
* controlling Nokia 5110 displays;
* reading the state of typical 16-key keypads;
* USB-based serial input/output.
All functions and macros are prefixed in order to avoid collision with
other C libraries.
Created by Luxferre in 2024, released into public domain
*/
/* basic includes */
#include <avr/io.h>
#include <util/delay.h>
/* Predefined profiles for Arduino Mega 2560 and Arduino Nano */
/* All parameters can be overridden before the include */
/* Definitions for Arduino Nano */
#ifdef AAIO_NANO
#ifndef AAIO_LED_PIN
#define AAIO_LED_PIN PB5
#endif
#ifndef AAIO_LED_DDR
#define AAIO_LED_DDR DDRB
#endif
#ifndef AAIO_LED_PORT
#define AAIO_LED_PORT PORTB
#endif
#ifndef AAIO_KEYPAD_DDR
#define AAIO_KEYPAD_DDR DDRD
#endif
#ifndef AAIO_KEYPAD_PORT
#define AAIO_KEYPAD_PORT PORTD
#endif
#ifndef AAIO_KEYPAD_PIN
#define AAIO_KEYPAD_PIN PIND
#endif
/* TODO: change default LCD values for Nano! */
#ifndef AAIO_LCD_PORT
#define AAIO_LCD_PORT PORTL
#endif
#ifndef AAIO_LCD_DDR
#define AAIO_LCD_DDR DDRL
#endif
#ifndef AAIO_LCD_SCE
#define AAIO_LCD_SCE PL4
#endif
#ifndef AAIO_LCD_RST
#define AAIO_LCD_RST PL3
#endif
#ifndef AAIO_LCD_DC
#define AAIO_LCD_DC PL2
#endif
#ifndef AAIO_LCD_DIN
#define AAIO_LCD_DIN PL1
#endif
#ifndef AAIO_LCD_CLK
#define AAIO_LCD_CLK PL0
#endif
#endif
/* Definitions for Arduino Mega 2560 */
#ifdef AAIO_MEGA2560
#ifndef AAIO_LED_PIN
#define AAIO_LED_PIN PB7
#endif
#ifndef AAIO_LED_DDR
#define AAIO_LED_DDR DDRB
#endif
#ifndef AAIO_LED_PORT
#define AAIO_LED_PORT PORTB
#endif
#ifndef AAIO_KEYPAD_DDR
#define AAIO_KEYPAD_DDR DDRA
#endif
#ifndef AAIO_KEYPAD_PORT
#define AAIO_KEYPAD_PORT PORTA
#endif
#ifndef AAIO_KEYPAD_PIN
#define AAIO_KEYPAD_PIN PINA
#endif
#ifndef AAIO_LCD_PORT
#define AAIO_LCD_PORT PORTL
#endif
#ifndef AAIO_LCD_DDR
#define AAIO_LCD_DDR DDRL
#endif
#ifndef AAIO_LCD_SCE
#define AAIO_LCD_SCE PL4
#endif
#ifndef AAIO_LCD_RST
#define AAIO_LCD_RST PL3
#endif
#ifndef AAIO_LCD_DC
#define AAIO_LCD_DC PL2
#endif
#ifndef AAIO_LCD_DIN
#define AAIO_LCD_DIN PL1
#endif
#ifndef AAIO_LCD_CLK
#define AAIO_LCD_CLK PL0
#endif
#endif
/* AAIO_FEATURE_LED: internal LED manipulation */
#ifdef AAIO_FEATURE_LED
/* init the internal LED I/O */
void aaio_led_init() {
AAIO_LED_DDR |= (1 << AAIO_LED_PIN);
}
/* toggle the internal LED */
void aaio_led_toggle() {
AAIO_LED_PORT ^= (1 << AAIO_LED_PIN);
}
/* set the internal LED */
void aaio_led_set(uint8_t v) {
if(v == 0) {
AAIO_LED_PORT &= ~(1 << AAIO_LED_PIN);
} else {
AAIO_LED_PORT |= (1 << AAIO_LED_PIN);
}
}
#endif
/* AAIO_FEATURE_USBUART: USB UART I/O */
/* you should define BAUD before this but it will default to 9600 */
#ifdef AAIO_FEATURE_USBUART
#ifndef BAUD
#define BAUD 9600
#endif
#include <util/setbaud.h>
#include <stdio.h>
/* init UART I/O */
void aaio_uart_init(void) {
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
UCSR0C = (1<<UCSZ01) | (1<<UCSZ00); /* 8-bit data */
UCSR0B = (1<<TXEN0) | (1<<RXEN0); /* enable TX and RX */
}
/* put a character into the bus */
int aaio_uart_putchar(char c, FILE *stream) {
if(c == '\n') aaio_uart_putchar('\r', stream);
loop_until_bit_is_set(UCSR0A, UDRE0);
UDR0 = c;
return 0;
}
/* get a character from the bus */
int aaio_uart_getchar(FILE *stream) {
while(!(UCSR0A & (1 << RXC0)));
return (UDR0);
}
/* get a string of length len from the bus, optionally echoing it back */
int aaio_uart_gets(char *buf, unsigned int len, char echo) {
unsigned int i;
char c;
do {
for(i = 0; i < len - 1; i++) {
c = (char) aaio_uart_getchar(NULL);
if(echo != 0) aaio_uart_putchar(c, NULL);
if(c == '\r' || c == '\n') break;
buf[i] = c;
}
} while (i == 0);
buf[i] = 0; /* guard the buffer */
return i;
}
/* helper utility functions to redirect stdin or stdout */
FILE _aaio_uart_input = FDEV_SETUP_STREAM(NULL, aaio_uart_getchar, _FDEV_SETUP_READ);
FILE _aaio_uart_output = FDEV_SETUP_STREAM(aaio_uart_putchar, NULL, _FDEV_SETUP_WRITE);
void aaio_uart_redirect_stdin() {
stdin = &_aaio_uart_input;
}
void aaio_uart_redirect_stdout() {
stdout = &_aaio_uart_output;
}
#endif
/* AAIO_FEATURE_KEYPAD: 16-key keypad manipulation */
#ifdef AAIO_FEATURE_KEYPAD
#include <avr/pgmspace.h>
/* scan the keyboard and return the associated value */
/* the definition must be stored in PROGMEM */
uint8_t aaio_scankey(const uint8_t keypad_def[4][4]) {
uint8_t row, col, dirmask;
for(col = 0; col < 4; col++) {
AAIO_KEYPAD_DDR = dirmask = 1 << (col + 4);
AAIO_KEYPAD_PORT = 0xf & ~dirmask;
for(row = 0; row < 4; row++) {
_delay_ms(5);
if((AAIO_KEYPAD_PIN & (1<<row)) == 0)
return pgm_read_byte(&keypad_def[col][row]);
}
_delay_ms(5);
}
return 0xff; /* return 0xff if no key press is detected */
}
#endif
/* AAIO_FEATURE_LCD_5110: Nokia 5110 display manipulation */
/* the charset must already be defined in aaio_5110_charset.h */
#ifdef AAIO_FEATURE_LCD_5110
#include "aaio_5110_charset.h"
#ifndef AAIO_LCD_CONTRAST
#define AAIO_LCD_CONTRAST 0x40
#endif
/* Nokia 5110 display data structure */
static struct {
uint8_t screen[504];
uint8_t cursor_x;
uint8_t cursor_y;
} _aaio_lcd = {
.cursor_x = 0,
.cursor_y = 0
};
/* send data to the LCD (in data or command mode) */
static void _aaio_lcd_write(uint8_t byte, uint8_t is_data) {
register uint8_t i;
AAIO_LCD_PORT &= ~(1 << AAIO_LCD_SCE); /* enable the controller */
if(is_data)
AAIO_LCD_PORT |= (1 << AAIO_LCD_DC);
else
AAIO_LCD_PORT &= ~(1 << AAIO_LCD_DC);
for(i = 0; i < 8; i++) {
/* set data pin to byte state */
if((byte >> (7-i)) & 1)
AAIO_LCD_PORT |= (1 << AAIO_LCD_DIN);
else
AAIO_LCD_PORT &= ~(1 << AAIO_LCD_DIN);
/* blink the clock */
AAIO_LCD_PORT |= (1 << AAIO_LCD_CLK);
AAIO_LCD_PORT &= ~(1 << AAIO_LCD_CLK);
}
AAIO_LCD_PORT |= (1 << AAIO_LCD_SCE); /* disable the controller */
}
static void _aaio_lcd_write_cmd(uint8_t cmd) {
_aaio_lcd_write(cmd, 0);
}
static void _aaio_lcd_write_data(uint8_t data) {
_aaio_lcd_write(data, 1);
}
/* init the LCD (must be called before any other function) */
void aaio_lcd_init(void) {
register unsigned i;
/* set pins as output */
AAIO_LCD_DDR |= (1 << AAIO_LCD_SCE);
AAIO_LCD_DDR |= (1 << AAIO_LCD_RST);
AAIO_LCD_DDR |= (1 << AAIO_LCD_DC);
AAIO_LCD_DDR |= (1 << AAIO_LCD_DIN);
AAIO_LCD_DDR |= (1 << AAIO_LCD_CLK);
/* reset the display */
AAIO_LCD_PORT |= (1 << AAIO_LCD_RST);
AAIO_LCD_PORT |= (1 << AAIO_LCD_SCE);
_delay_ms(10);
AAIO_LCD_PORT &= ~(1 << AAIO_LCD_RST);
_delay_ms(70);
AAIO_LCD_PORT |= (1 << AAIO_LCD_RST);
AAIO_LCD_PORT &= ~(1 << AAIO_LCD_SCE); /* enable the controller */
_aaio_lcd_write_cmd(0x21); /* LCD extended commands mode */
_aaio_lcd_write_cmd(0xB0); /* default VOP */
_aaio_lcd_write_cmd(0x04); /* set temperature coefficient */
_aaio_lcd_write_cmd(0x14); /* LCD bias mode 1:48 */
_aaio_lcd_write_cmd(0x20); /* standard commands mode, powered down */
_aaio_lcd_write_cmd(0x09); /* LCD in normal mode */
/* clear LCD RAM */
_aaio_lcd_write_cmd(0x80);
_aaio_lcd_write_cmd(AAIO_LCD_CONTRAST);
for(i = 0; i < 504; i++) _aaio_lcd_write_data(0);
/* activate the LCD */
_aaio_lcd_write_cmd(0x08);
_aaio_lcd_write_cmd(0x0C);
}
/* clear the LCD */
void aaio_lcd_clear(void) {
register unsigned i;
/* set column and row to 0 */
_aaio_lcd_write_cmd(0x80);
_aaio_lcd_write_cmd(0x40);
/* reset the cursor */
_aaio_lcd.cursor_x = 0;
_aaio_lcd.cursor_y = 0;
/* clear everything */
for(i = 0; i < 504; i++) _aaio_lcd.screen[i] = 0;
}
/* power the LCD on/off */
void aaio_lcd_power(uint8_t on) {
_aaio_lcd_write_cmd(on ? 0x20 : 0x24);
}
/* set a pixel */
void aaio_lcd_set_pixel(uint8_t x, uint8_t y, uint8_t value) {
uint8_t *byte = &_aaio_lcd.screen[(y >> 3) * 84 + x];
if(value) *byte |= (1 << (y & 7));
else *byte &= ~(1 << (y & 7));
}
/* write an ASCII character */
void aaio_lcd_write_char(char code, uint8_t scale) {
register uint8_t x, y;
for(x = 0; x < 5*scale; x++)
for(y = 0; y < 7*scale; y++)
if(pgm_read_byte(&CHARSET[code-32][x/scale]) & (1 << y/scale))
aaio_lcd_set_pixel(_aaio_lcd.cursor_x + x, _aaio_lcd.cursor_y + y, 1);
else
aaio_lcd_set_pixel(_aaio_lcd.cursor_x + x, _aaio_lcd.cursor_y + y, 0);
_aaio_lcd.cursor_x += 5*scale + 1;
if(_aaio_lcd.cursor_x >= 84) {
_aaio_lcd.cursor_x = 0;
_aaio_lcd.cursor_y += 7*scale + 1;
}
if(_aaio_lcd.cursor_y >= 48) {
_aaio_lcd.cursor_x = 0;
_aaio_lcd.cursor_y = 0;
}
}
void aaio_lcd_write_string(const char *str, uint8_t scale) {
while(*str) aaio_lcd_write_char(*str++, scale);
}
void aaio_lcd_set_cursor(uint8_t x, uint8_t y) {
_aaio_lcd.cursor_x = x;
_aaio_lcd.cursor_y = y;
}
void aaio_lcd_render(void) {
register unsigned i;
/* set column and row to 0 */
_aaio_lcd_write_cmd(0x80);
_aaio_lcd_write_cmd(0x40);
/* write screen data to the display */
for(i = 0; i < 504; i++) _aaio_lcd_write_data(_aaio_lcd.screen[i]);
}
#endif
#endif