aaio

AVR All-In-One helper C library for Arduino boards
git clone git://git.luxferre.top/aaio.git
Log | Files | Refs

commit 2aeda1c29136a55618d2159661fe1a120326b099
Author: Luxferre <lux@ferre>
Date:   Sun, 17 Nov 2024 21:00:37 +0200

first upload

Diffstat:
Aaaio.h | 407+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aaaio_5110_charset.h | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aexample_5110.c | 33+++++++++++++++++++++++++++++++++
Aexample_blonk.c | 27+++++++++++++++++++++++++++
4 files changed, 570 insertions(+), 0 deletions(-)

diff --git a/aaio.h b/aaio.h @@ -0,0 +1,407 @@ +#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 diff --git a/aaio_5110_charset.h b/aaio_5110_charset.h @@ -0,0 +1,103 @@ +#ifndef _AAIO_5110_CHARSET_H +#define _AAIO_5110_CHARSET_H +#include <avr/pgmspace.h> + +const uint8_t CHARSET[][5] PROGMEM = { + { 0x00, 0x00, 0x00, 0x00, 0x00 }, // 20 space + { 0x00, 0x00, 0x5f, 0x00, 0x00 }, // 21 ! + { 0x00, 0x07, 0x00, 0x07, 0x00 }, // 22 " + { 0x14, 0x7f, 0x14, 0x7f, 0x14 }, // 23 # + { 0x24, 0x2a, 0x7f, 0x2a, 0x12 }, // 24 $ + { 0x23, 0x13, 0x08, 0x64, 0x62 }, // 25 % + { 0x36, 0x49, 0x55, 0x22, 0x50 }, // 26 & + { 0x00, 0x05, 0x03, 0x00, 0x00 }, // 27 ' + { 0x00, 0x1c, 0x22, 0x41, 0x00 }, // 28 ( + { 0x00, 0x41, 0x22, 0x1c, 0x00 }, // 29 ) + { 0x14, 0x08, 0x3e, 0x08, 0x14 }, // 2a * + { 0x08, 0x08, 0x3e, 0x08, 0x08 }, // 2b + + { 0x00, 0x50, 0x30, 0x00, 0x00 }, // 2c , + { 0x08, 0x08, 0x08, 0x08, 0x08 }, // 2d - + { 0x00, 0x60, 0x60, 0x00, 0x00 }, // 2e . + { 0x20, 0x10, 0x08, 0x04, 0x02 }, // 2f / + { 0x3e, 0x51, 0x49, 0x45, 0x3e }, // 30 0 + { 0x00, 0x42, 0x7f, 0x40, 0x00 }, // 31 1 + { 0x42, 0x61, 0x51, 0x49, 0x46 }, // 32 2 + { 0x21, 0x41, 0x45, 0x4b, 0x31 }, // 33 3 + { 0x18, 0x14, 0x12, 0x7f, 0x10 }, // 34 4 + { 0x27, 0x45, 0x45, 0x45, 0x39 }, // 35 5 + { 0x3c, 0x4a, 0x49, 0x49, 0x30 }, // 36 6 + { 0x01, 0x71, 0x09, 0x05, 0x03 }, // 37 7 + { 0x36, 0x49, 0x49, 0x49, 0x36 }, // 38 8 + { 0x06, 0x49, 0x49, 0x29, 0x1e }, // 39 9 + { 0x00, 0x36, 0x36, 0x00, 0x00 }, // 3a : + { 0x00, 0x56, 0x36, 0x00, 0x00 }, // 3b ; + { 0x08, 0x14, 0x22, 0x41, 0x00 }, // 3c < + { 0x14, 0x14, 0x14, 0x14, 0x14 }, // 3d = + { 0x00, 0x41, 0x22, 0x14, 0x08 }, // 3e > + { 0x02, 0x01, 0x51, 0x09, 0x06 }, // 3f ? + { 0x32, 0x49, 0x79, 0x41, 0x3e }, // 40 @ + { 0x7e, 0x11, 0x11, 0x11, 0x7e }, // 41 A + { 0x7f, 0x49, 0x49, 0x49, 0x36 }, // 42 B + { 0x3e, 0x41, 0x41, 0x41, 0x22 }, // 43 C + { 0x7f, 0x41, 0x41, 0x22, 0x1c }, // 44 D + { 0x7f, 0x49, 0x49, 0x49, 0x41 }, // 45 E + { 0x7f, 0x09, 0x09, 0x09, 0x01 }, // 46 F + { 0x3e, 0x41, 0x49, 0x49, 0x7a }, // 47 G + { 0x7f, 0x08, 0x08, 0x08, 0x7f }, // 48 H + { 0x00, 0x41, 0x7f, 0x41, 0x00 }, // 49 I + { 0x20, 0x40, 0x41, 0x3f, 0x01 }, // 4a J + { 0x7f, 0x08, 0x14, 0x22, 0x41 }, // 4b K + { 0x7f, 0x40, 0x40, 0x40, 0x40 }, // 4c L + { 0x7f, 0x02, 0x0c, 0x02, 0x7f }, // 4d M + { 0x7f, 0x04, 0x08, 0x10, 0x7f }, // 4e N + { 0x3e, 0x41, 0x41, 0x41, 0x3e }, // 4f O + { 0x7f, 0x09, 0x09, 0x09, 0x06 }, // 50 P + { 0x3e, 0x41, 0x51, 0x21, 0x5e }, // 51 Q + { 0x7f, 0x09, 0x19, 0x29, 0x46 }, // 52 R + { 0x46, 0x49, 0x49, 0x49, 0x31 }, // 53 S + { 0x01, 0x01, 0x7f, 0x01, 0x01 }, // 54 T + { 0x3f, 0x40, 0x40, 0x40, 0x3f }, // 55 U + { 0x1f, 0x20, 0x40, 0x20, 0x1f }, // 56 V + { 0x3f, 0x40, 0x38, 0x40, 0x3f }, // 57 W + { 0x63, 0x14, 0x08, 0x14, 0x63 }, // 58 X + { 0x07, 0x08, 0x70, 0x08, 0x07 }, // 59 Y + { 0x61, 0x51, 0x49, 0x45, 0x43 }, // 5a Z + { 0x00, 0x7f, 0x41, 0x41, 0x00 }, // 5b [ + { 0x02, 0x04, 0x08, 0x10, 0x20 }, // 5c backslash + { 0x00, 0x41, 0x41, 0x7f, 0x00 }, // 5d ] + { 0x04, 0x02, 0x01, 0x02, 0x04 }, // 5e ^ + { 0x40, 0x40, 0x40, 0x40, 0x40 }, // 5f _ + { 0x00, 0x01, 0x02, 0x04, 0x00 }, // 60 ` + { 0x20, 0x54, 0x54, 0x54, 0x78 }, // 61 a + { 0x7f, 0x48, 0x44, 0x44, 0x38 }, // 62 b + { 0x38, 0x44, 0x44, 0x44, 0x20 }, // 63 c + { 0x38, 0x44, 0x44, 0x48, 0x7f }, // 64 d + { 0x38, 0x54, 0x54, 0x54, 0x18 }, // 65 e + { 0x08, 0x7e, 0x09, 0x01, 0x02 }, // 66 f + { 0x0c, 0x52, 0x52, 0x52, 0x3e }, // 67 g + { 0x7f, 0x08, 0x04, 0x04, 0x78 }, // 68 h + { 0x00, 0x44, 0x7d, 0x40, 0x00 }, // 69 i + { 0x20, 0x40, 0x44, 0x3d, 0x00 }, // 6a j + { 0x7f, 0x10, 0x28, 0x44, 0x00 }, // 6b k + { 0x00, 0x41, 0x7f, 0x40, 0x00 }, // 6c l + { 0x7c, 0x04, 0x18, 0x04, 0x78 }, // 6d m + { 0x7c, 0x08, 0x04, 0x04, 0x78 }, // 6e n + { 0x38, 0x44, 0x44, 0x44, 0x38 }, // 6f o + { 0x7c, 0x14, 0x14, 0x14, 0x08 }, // 70 p + { 0x08, 0x14, 0x14, 0x18, 0x7c }, // 71 q + { 0x7c, 0x08, 0x04, 0x04, 0x08 }, // 72 r + { 0x48, 0x54, 0x54, 0x54, 0x20 }, // 73 s + { 0x04, 0x3f, 0x44, 0x40, 0x20 }, // 74 t + { 0x3c, 0x40, 0x40, 0x20, 0x7c }, // 75 u + { 0x1c, 0x20, 0x40, 0x20, 0x1c }, // 76 v + { 0x3c, 0x40, 0x30, 0x40, 0x3c }, // 77 w + { 0x44, 0x28, 0x10, 0x28, 0x44 }, // 78 x + { 0x0c, 0x50, 0x50, 0x50, 0x3c }, // 79 y + { 0x44, 0x64, 0x54, 0x4c, 0x44 }, // 7a z + { 0x00, 0x08, 0x36, 0x41, 0x00 }, // 7b { + { 0x00, 0x00, 0x7f, 0x00, 0x00 }, // 7c | + { 0x00, 0x41, 0x36, 0x08, 0x00 }, // 7d } + { 0x10, 0x08, 0x08, 0x10, 0x08 }, // 7e ~ + { 0x00, 0x00, 0x00, 0x00, 0x00 } // 7f +}; +#endif diff --git a/example_5110.c b/example_5110.c @@ -0,0 +1,33 @@ + +/* AAIO features to use */ +#define AAIO_FEATURE_KEYPAD +#define AAIO_FEATURE_LCD_5110 + +/* include the AAIO header */ +#include "aaio.h" + +/* keypad definition */ +const static uint8_t keypad_def[4][4] PROGMEM = { + {'1' , '2' , '3' , 'A' }, + {'4' , '5' , '6' , 'B' }, + {'7' , '8' , '9' , 'C' }, + {'*' , '0' , '#' , 'D' } +}; + +int main(void) { + aaio_lcd_init(); + aaio_lcd_clear(); + aaio_lcd_write_string("AAIO lib rulez!",1); + aaio_lcd_set_cursor(0, 10); + aaio_lcd_write_string("Nice!", 3); + aaio_lcd_render(); + unsigned char c; + for(;;) { + c = aaio_scankey(keypad_def); + if(c != 0xff) { + aaio_lcd_set_cursor(0, 32); + aaio_lcd_write_char(c, 2); + aaio_lcd_render(); + } + } +} diff --git a/example_blonk.c b/example_blonk.c @@ -0,0 +1,27 @@ +#define BAUD 9600 + +/* AAIO features to use */ +#define AAIO_FEATURE_LED +#define AAIO_FEATURE_USBUART + +/* include the AAIO header */ +#include "aaio.h" + +int main (void) { + aaio_led_init(); + aaio_uart_init(); + aaio_uart_redirect_stdout(); + aaio_uart_redirect_stdin(); + char buf[100]; + while(1) { + printf("command> "); + aaio_uart_gets(buf, sizeof(buf), 1); + printf("You entered: %s\r\n", buf); + if(buf[0] == '0') aaio_led_set(0); + else if(buf[0] == '1') aaio_led_set(1); + else aaio_led_toggle(); + } + return 0; +} + +