406 lines
9.1 KiB
C
406 lines
9.1 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
|
|
|
|
#ifndef AAIO_LCD_PORT
|
|
#define AAIO_LCD_PORT PORTC
|
|
#endif
|
|
|
|
#ifndef AAIO_LCD_DDR
|
|
#define AAIO_LCD_DDR DDRC
|
|
#endif
|
|
|
|
#ifndef AAIO_LCD_SCE
|
|
#define AAIO_LCD_SCE PC4
|
|
#endif
|
|
|
|
#ifndef AAIO_LCD_RST
|
|
#define AAIO_LCD_RST PC3
|
|
#endif
|
|
|
|
#ifndef AAIO_LCD_DC
|
|
#define AAIO_LCD_DC PC2
|
|
#endif
|
|
|
|
#ifndef AAIO_LCD_DIN
|
|
#define AAIO_LCD_DIN PC1
|
|
#endif
|
|
|
|
#ifndef AAIO_LCD_CLK
|
|
#define AAIO_LCD_CLK PC0
|
|
#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
|