aaio

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

aaio.h (10249B)


      1 #ifndef _AAIO_H
      2 #define _AAIO_H
      3 /*
      4   AVR All-in-one: a single-header library in pure C (tailored to avr-gcc)
      5   for some typical AVR ATmega series usecases
      6   Tested on: Arduino Mega 2560 (ATmega2560), Arduino Nano (ATmega328P)
      7   Individually selectable features:
      8   * controlling internal LED (overridable to any other pin);
      9   * controlling Nokia 5110 displays;
     10   * reading the state of typical 16-key keypads;
     11   * USB-based serial input/output.
     12   All functions and macros are prefixed in order to avoid collision with
     13   other C libraries.
     14   Created by Luxferre in 2024, released into public domain
     15 */
     16 
     17 /* basic includes */
     18 #include <avr/io.h>
     19 #include <util/delay.h>
     20 
     21 /* Predefined profiles for Arduino Mega 2560 and Arduino Nano */
     22 /* All parameters can be overridden before the include */
     23 
     24 /* Definitions for Arduino Nano */
     25 
     26 #ifdef AAIO_NANO
     27 
     28 #ifndef AAIO_LED_PIN
     29 #define AAIO_LED_PIN PB5
     30 #endif
     31 
     32 #ifndef AAIO_LED_DDR
     33 #define AAIO_LED_DDR DDRB
     34 #endif
     35 
     36 #ifndef AAIO_LED_PORT
     37 #define AAIO_LED_PORT PORTB
     38 #endif
     39 
     40 #ifndef AAIO_KEYPAD_DDR
     41 #define AAIO_KEYPAD_DDR DDRD
     42 #endif
     43 
     44 #ifndef AAIO_KEYPAD_PORT
     45 #define AAIO_KEYPAD_PORT PORTD
     46 #endif
     47 
     48 #ifndef AAIO_KEYPAD_PIN
     49 #define AAIO_KEYPAD_PIN PIND
     50 #endif
     51 
     52 #ifndef AAIO_LCD_PORT
     53 #define AAIO_LCD_PORT PORTC
     54 #endif
     55 
     56 #ifndef AAIO_LCD_DDR
     57 #define AAIO_LCD_DDR DDRC
     58 #endif
     59 
     60 #ifndef AAIO_LCD_SCE
     61 #define AAIO_LCD_SCE PC4
     62 #endif
     63 
     64 #ifndef AAIO_LCD_RST
     65 #define AAIO_LCD_RST PC3
     66 #endif
     67 
     68 #ifndef AAIO_LCD_DC
     69 #define AAIO_LCD_DC  PC2
     70 #endif
     71 
     72 #ifndef AAIO_LCD_DIN
     73 #define AAIO_LCD_DIN PC1
     74 #endif
     75 
     76 #ifndef AAIO_LCD_CLK
     77 #define AAIO_LCD_CLK PC0
     78 #endif
     79 
     80 #endif
     81 
     82 /* Definitions for Arduino Mega 2560 */
     83 
     84 #ifdef AAIO_MEGA2560
     85 
     86 #ifndef AAIO_LED_PIN
     87 #define AAIO_LED_PIN PB7
     88 #endif
     89 
     90 #ifndef AAIO_LED_DDR
     91 #define AAIO_LED_DDR DDRB
     92 #endif
     93 
     94 #ifndef AAIO_LED_PORT
     95 #define AAIO_LED_PORT PORTB
     96 #endif
     97 
     98 #ifndef AAIO_KEYPAD_DDR
     99 #define AAIO_KEYPAD_DDR DDRA
    100 #endif
    101 
    102 #ifndef AAIO_KEYPAD_PORT
    103 #define AAIO_KEYPAD_PORT PORTA
    104 #endif
    105 
    106 #ifndef AAIO_KEYPAD_PIN
    107 #define AAIO_KEYPAD_PIN PINA
    108 #endif
    109 
    110 #ifndef AAIO_LCD_PORT
    111 #define AAIO_LCD_PORT PORTL
    112 #endif
    113 
    114 #ifndef AAIO_LCD_DDR
    115 #define AAIO_LCD_DDR DDRL
    116 #endif
    117 
    118 #ifndef AAIO_LCD_SCE
    119 #define AAIO_LCD_SCE PL4
    120 #endif
    121 
    122 #ifndef AAIO_LCD_RST
    123 #define AAIO_LCD_RST PL3
    124 #endif
    125 
    126 #ifndef AAIO_LCD_DC
    127 #define AAIO_LCD_DC  PL2
    128 #endif
    129 
    130 #ifndef AAIO_LCD_DIN
    131 #define AAIO_LCD_DIN PL1
    132 #endif
    133 
    134 #ifndef AAIO_LCD_CLK
    135 #define AAIO_LCD_CLK PL0
    136 #endif
    137 
    138 #endif
    139 
    140 /* AAIO_FEATURE_LED: internal LED manipulation */
    141 
    142 #ifdef AAIO_FEATURE_LED
    143 
    144 /* init the internal LED I/O */
    145 void aaio_led_init() {
    146   AAIO_LED_DDR |= (1 << AAIO_LED_PIN);
    147 }
    148 
    149 /* toggle the internal LED */
    150 void aaio_led_toggle() {
    151   AAIO_LED_PORT ^= (1 << AAIO_LED_PIN);
    152 }
    153 
    154 /* set the internal LED */
    155 void aaio_led_set(register uint8_t v) {
    156   if(v == 0) {
    157     AAIO_LED_PORT &= ~(1 << AAIO_LED_PIN);
    158   } else {
    159     AAIO_LED_PORT |= (1 << AAIO_LED_PIN);
    160   }
    161 }
    162 
    163 #endif
    164 
    165 /* AAIO_FEATURE_USBUART: USB UART I/O */
    166 /* you should define BAUD before this but it will default to 9600 */
    167 
    168 #ifdef AAIO_FEATURE_USBUART
    169 
    170 #ifndef BAUD
    171 #define BAUD 9600
    172 #endif
    173 #include <util/setbaud.h>
    174 #include <stdio.h>
    175 
    176 /* init UART I/O */
    177 void aaio_uart_init(void) {
    178   UBRR0H = UBRRH_VALUE;
    179   UBRR0L = UBRRL_VALUE;
    180   UCSR0C = (1<<UCSZ01) | (1<<UCSZ00); /* 8-bit data */
    181   UCSR0B = (1<<TXEN0) | (1<<RXEN0);   /* enable TX and RX */
    182 }
    183 
    184 /* put a character into the bus */
    185 int aaio_uart_putchar(register char c, FILE *stream) {
    186   if(c == '\n') aaio_uart_putchar('\r', stream);
    187   loop_until_bit_is_set(UCSR0A, UDRE0);
    188   UDR0 = c;
    189   return 0;
    190 }
    191 
    192 /* get a character from the bus */
    193 int aaio_uart_getchar(FILE *stream) {
    194   while(!(UCSR0A & (1 << RXC0)));
    195   return (UDR0);
    196 }
    197 
    198 /* put a null-terminated string into the bus */
    199 void aaio_uart_puts(char *buf) {
    200   while(*buf)
    201     aaio_uart_putchar(*(buf++), NULL);
    202 }
    203 
    204 /* print a number into the bus */
    205 void aaio_uart_printnum(register unsigned int value) {
    206   register uint8_t d;
    207   register uint32_t threshold = 1000000000;
    208   for(;threshold > 0;threshold /= 10) {
    209     if(value > threshold) {
    210       d = (value / threshold) % 10;
    211       aaio_uart_putchar('0' + d, NULL);
    212     }
    213   }
    214 }
    215 
    216 /* get a string of length len from the bus, optionally echoing it back */
    217 int aaio_uart_gets(char *buf, register unsigned int len, register char echo) {
    218   register unsigned int i;
    219   register char c;
    220   do {
    221     for(i = 0; i < len - 1; i++) {
    222       c = (char) aaio_uart_getchar(NULL);
    223       if(echo != 0) aaio_uart_putchar(c, NULL);
    224       if(c == '\r' || c == '\n') break;
    225       buf[i] = c;
    226     }
    227   } while (i == 0);
    228   buf[i] = 0; /* guard the buffer */
    229   return i;
    230 }
    231 
    232 /* helper utility functions to redirect stdin or stdout */
    233 
    234 FILE _aaio_uart_input = FDEV_SETUP_STREAM(NULL, aaio_uart_getchar, _FDEV_SETUP_READ);
    235 FILE _aaio_uart_output = FDEV_SETUP_STREAM(aaio_uart_putchar, NULL, _FDEV_SETUP_WRITE);
    236 
    237 void aaio_uart_redirect_stdin() {
    238   stdin = &_aaio_uart_input;
    239 }
    240 
    241 void aaio_uart_redirect_stdout() {
    242   stdout = &_aaio_uart_output;
    243 }
    244 
    245 #endif
    246 
    247 /* AAIO_FEATURE_KEYPAD: 16-key keypad manipulation */
    248 
    249 #ifdef AAIO_FEATURE_KEYPAD
    250 
    251 #include <avr/pgmspace.h>
    252 
    253 /* scan the keyboard and return the associated value */
    254 /* the definition must be stored in PROGMEM */
    255 uint8_t aaio_scankey(const uint8_t keypad_def[4][4]) {
    256   register uint8_t row, col, dirmask;
    257   for(col = 0; col < 4; col++) {
    258     AAIO_KEYPAD_DDR = dirmask = 1 << (col + 4);
    259     AAIO_KEYPAD_PORT = 0xf & ~dirmask;
    260     for(row = 0; row < 4; row++) {
    261       _delay_ms(5);
    262       if((AAIO_KEYPAD_PIN & (1<<row)) == 0)
    263         return pgm_read_byte(&keypad_def[col][row]);
    264     }
    265     _delay_ms(5);
    266   }
    267   return 0xff; /* return 0xff if no key press is detected */
    268 }
    269 
    270 #endif
    271 
    272 /* AAIO_FEATURE_LCD_5110: Nokia 5110 display manipulation */
    273 /* the charset must already be defined in aaio_5110_charset.h */
    274 
    275 #ifdef AAIO_FEATURE_LCD_5110
    276 
    277 #include "aaio_5110_charset.h"
    278 
    279 #ifndef AAIO_LCD_CONTRAST
    280 #define AAIO_LCD_CONTRAST 0x40
    281 #endif
    282 
    283 /* Nokia 5110 display data structure */
    284 static struct {
    285   uint8_t screen[504];
    286   uint8_t cursor_x;
    287   uint8_t cursor_y;
    288 } _aaio_lcd = {
    289   .cursor_x = 0,
    290   .cursor_y = 0
    291 };
    292 
    293 /* send data to the LCD (in data or command mode) */
    294 static void _aaio_lcd_write(register uint8_t byte, register uint8_t is_data) {
    295   register uint8_t i;
    296   AAIO_LCD_PORT &= ~(1 << AAIO_LCD_SCE); /* enable the controller */
    297   if(is_data)
    298     AAIO_LCD_PORT |= (1 << AAIO_LCD_DC);
    299   else
    300     AAIO_LCD_PORT &= ~(1 << AAIO_LCD_DC);
    301   for(i = 0; i < 8; i++) {
    302     /* set data pin to byte state */
    303     if((byte >> (7-i)) & 1)
    304       AAIO_LCD_PORT |= (1 << AAIO_LCD_DIN);
    305     else
    306       AAIO_LCD_PORT &= ~(1 << AAIO_LCD_DIN);
    307     /* blink the clock */
    308     AAIO_LCD_PORT |= (1 << AAIO_LCD_CLK);
    309     AAIO_LCD_PORT &= ~(1 << AAIO_LCD_CLK);
    310   }
    311   AAIO_LCD_PORT |= (1 << AAIO_LCD_SCE); /* disable the controller */
    312 }
    313 
    314 static void _aaio_lcd_write_cmd(register uint8_t cmd) {
    315   _aaio_lcd_write(cmd, 0);
    316 }
    317 
    318 static void _aaio_lcd_write_data(register uint8_t data) {
    319   _aaio_lcd_write(data, 1);
    320 }
    321 
    322 /* init the LCD (must be called before any other function) */
    323 void aaio_lcd_init(void) {
    324   register unsigned i;
    325   /* set pins as output */
    326   AAIO_LCD_DDR |= (1 << AAIO_LCD_SCE);
    327   AAIO_LCD_DDR |= (1 << AAIO_LCD_RST);
    328   AAIO_LCD_DDR |= (1 << AAIO_LCD_DC);
    329   AAIO_LCD_DDR |= (1 << AAIO_LCD_DIN);
    330   AAIO_LCD_DDR |= (1 << AAIO_LCD_CLK);
    331 
    332   /* reset the display */
    333   AAIO_LCD_PORT |= (1 << AAIO_LCD_RST);
    334   AAIO_LCD_PORT |= (1 << AAIO_LCD_SCE);
    335   _delay_ms(10);
    336   AAIO_LCD_PORT &= ~(1 << AAIO_LCD_RST);
    337   _delay_ms(70);
    338   AAIO_LCD_PORT |= (1 << AAIO_LCD_RST);
    339 
    340   AAIO_LCD_PORT &= ~(1 << AAIO_LCD_SCE); /* enable the controller */
    341   _aaio_lcd_write_cmd(0x21); /* LCD extended commands mode */
    342   _aaio_lcd_write_cmd(0xB0); /* default VOP */
    343   _aaio_lcd_write_cmd(0x04); /* set temperature coefficient */
    344   _aaio_lcd_write_cmd(0x14); /* LCD bias mode 1:48 */
    345   _aaio_lcd_write_cmd(0x20); /* standard commands mode, powered down */
    346   _aaio_lcd_write_cmd(0x09); /* LCD in normal mode */
    347 
    348   /* clear LCD RAM */
    349   _aaio_lcd_write_cmd(0x80);
    350   _aaio_lcd_write_cmd(AAIO_LCD_CONTRAST);
    351   for(i = 0; i < 504; i++) _aaio_lcd_write_data(0);
    352 
    353   /* activate the LCD */
    354   _aaio_lcd_write_cmd(0x08);
    355   _aaio_lcd_write_cmd(0x0C);
    356 }
    357 
    358 /* clear the LCD */
    359 void aaio_lcd_clear(void) {
    360   register unsigned i;
    361   /* set column and row to 0 */
    362   _aaio_lcd_write_cmd(0x80);
    363   _aaio_lcd_write_cmd(0x40);
    364   /* reset the cursor */
    365   _aaio_lcd.cursor_x = 0;
    366   _aaio_lcd.cursor_y = 0;
    367   /* clear everything */
    368   for(i = 0; i < 504; i++) _aaio_lcd.screen[i] = 0;
    369 }
    370 
    371 /* power the LCD on/off */
    372 void aaio_lcd_power(register uint8_t on) {
    373   _aaio_lcd_write_cmd(on ? 0x20 : 0x24);
    374 }
    375 
    376 /* set a pixel */
    377 void aaio_lcd_set_pixel(register uint8_t x, register uint8_t y, register uint8_t value) {
    378   uint8_t *byte = &_aaio_lcd.screen[(y >> 3) * 84 + x];
    379   if(value) *byte |= (1 << (y & 7));
    380   else *byte &= ~(1 << (y & 7));
    381 }
    382 
    383 /* write an ASCII character */
    384 void aaio_lcd_write_char(register char code, register uint8_t scale) {
    385   register uint8_t x, y;
    386   for(x = 0; x < 5*scale; x++)
    387     for(y = 0; y < 7*scale; y++)
    388       if(pgm_read_byte(&CHARSET[code-32][x/scale]) & (1 << y/scale))
    389         aaio_lcd_set_pixel(_aaio_lcd.cursor_x + x, _aaio_lcd.cursor_y + y, 1);
    390       else
    391         aaio_lcd_set_pixel(_aaio_lcd.cursor_x + x, _aaio_lcd.cursor_y + y, 0);
    392   _aaio_lcd.cursor_x += 5*scale + 1;
    393   if(_aaio_lcd.cursor_x >= 84) {
    394     _aaio_lcd.cursor_x = 0;
    395     _aaio_lcd.cursor_y += 7*scale + 1;
    396   }
    397   if(_aaio_lcd.cursor_y >= 48) {
    398     _aaio_lcd.cursor_x = 0;
    399     _aaio_lcd.cursor_y = 0;
    400   }
    401 }
    402 
    403 void aaio_lcd_write_string(const char *str, register uint8_t scale) {
    404   while(*str) aaio_lcd_write_char(*str++, scale);
    405 }
    406 
    407 void aaio_lcd_set_cursor(register uint8_t x, register uint8_t y) {
    408   _aaio_lcd.cursor_x = x;
    409   _aaio_lcd.cursor_y = y;
    410 }
    411 
    412 void aaio_lcd_render(void) {
    413   register unsigned i;
    414   /* set column and row to 0 */
    415   _aaio_lcd_write_cmd(0x80);
    416   _aaio_lcd_write_cmd(0x40);
    417   /* write screen data to the display */
    418   for(i = 0; i < 504; i++) _aaio_lcd_write_data(_aaio_lcd.screen[i]);
    419 }
    420 
    421 #endif
    422 
    423 /* PRNG feature */
    424 
    425 #ifdef AAIO_FEATURE_RNG
    426 
    427 static uint32_t _aaio_rng_state;
    428 
    429 void aaio_srand() {
    430   _aaio_rng_state = 0xdeadbeef;
    431 }
    432 
    433 uint32_t aaio_rand() {
    434 	register uint32_t x = _aaio_rng_state;
    435 	x ^= x << 13;
    436 	x ^= x >> 17;
    437 	x ^= x << 5;
    438 	return _aaio_rng_state = x;
    439 }
    440 
    441 #endif
    442 
    443 #endif