aaio

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

aaio.h (9388B)


      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 /* TODO: change default LCD values for Nano! */
     53 
     54 #ifndef AAIO_LCD_PORT
     55 #define AAIO_LCD_PORT PORTL
     56 #endif
     57 
     58 #ifndef AAIO_LCD_DDR
     59 #define AAIO_LCD_DDR DDRL
     60 #endif
     61 
     62 #ifndef AAIO_LCD_SCE
     63 #define AAIO_LCD_SCE PL4
     64 #endif
     65 
     66 #ifndef AAIO_LCD_RST
     67 #define AAIO_LCD_RST PL3
     68 #endif
     69 
     70 #ifndef AAIO_LCD_DC
     71 #define AAIO_LCD_DC  PL2
     72 #endif
     73 
     74 #ifndef AAIO_LCD_DIN
     75 #define AAIO_LCD_DIN PL1
     76 #endif
     77 
     78 #ifndef AAIO_LCD_CLK
     79 #define AAIO_LCD_CLK PL0
     80 #endif
     81 
     82 #endif
     83 
     84 /* Definitions for Arduino Mega 2560 */
     85 
     86 #ifdef AAIO_MEGA2560
     87 
     88 #ifndef AAIO_LED_PIN
     89 #define AAIO_LED_PIN PB7
     90 #endif
     91 
     92 #ifndef AAIO_LED_DDR
     93 #define AAIO_LED_DDR DDRB
     94 #endif
     95 
     96 #ifndef AAIO_LED_PORT
     97 #define AAIO_LED_PORT PORTB
     98 #endif
     99 
    100 #ifndef AAIO_KEYPAD_DDR
    101 #define AAIO_KEYPAD_DDR DDRA
    102 #endif
    103 
    104 #ifndef AAIO_KEYPAD_PORT
    105 #define AAIO_KEYPAD_PORT PORTA
    106 #endif
    107 
    108 #ifndef AAIO_KEYPAD_PIN
    109 #define AAIO_KEYPAD_PIN PINA
    110 #endif
    111 
    112 #ifndef AAIO_LCD_PORT
    113 #define AAIO_LCD_PORT PORTL
    114 #endif
    115 
    116 #ifndef AAIO_LCD_DDR
    117 #define AAIO_LCD_DDR DDRL
    118 #endif
    119 
    120 #ifndef AAIO_LCD_SCE
    121 #define AAIO_LCD_SCE PL4
    122 #endif
    123 
    124 #ifndef AAIO_LCD_RST
    125 #define AAIO_LCD_RST PL3
    126 #endif
    127 
    128 #ifndef AAIO_LCD_DC
    129 #define AAIO_LCD_DC  PL2
    130 #endif
    131 
    132 #ifndef AAIO_LCD_DIN
    133 #define AAIO_LCD_DIN PL1
    134 #endif
    135 
    136 #ifndef AAIO_LCD_CLK
    137 #define AAIO_LCD_CLK PL0
    138 #endif
    139 
    140 #endif
    141 
    142 /* AAIO_FEATURE_LED: internal LED manipulation */
    143 
    144 #ifdef AAIO_FEATURE_LED
    145 
    146 /* init the internal LED I/O */
    147 void aaio_led_init() {
    148   AAIO_LED_DDR |= (1 << AAIO_LED_PIN);
    149 }
    150 
    151 /* toggle the internal LED */
    152 void aaio_led_toggle() {
    153   AAIO_LED_PORT ^= (1 << AAIO_LED_PIN);
    154 }
    155 
    156 /* set the internal LED */
    157 void aaio_led_set(uint8_t v) {
    158   if(v == 0) {
    159     AAIO_LED_PORT &= ~(1 << AAIO_LED_PIN);
    160   } else {
    161     AAIO_LED_PORT |= (1 << AAIO_LED_PIN);
    162   }
    163 }
    164 
    165 #endif
    166 
    167 /* AAIO_FEATURE_USBUART: USB UART I/O */
    168 /* you should define BAUD before this but it will default to 9600 */
    169 
    170 #ifdef AAIO_FEATURE_USBUART
    171 
    172 #ifndef BAUD
    173 #define BAUD 9600
    174 #endif
    175 #include <util/setbaud.h>
    176 #include <stdio.h>
    177 
    178 /* init UART I/O */
    179 void aaio_uart_init(void) {
    180   UBRR0H = UBRRH_VALUE;
    181   UBRR0L = UBRRL_VALUE;
    182   UCSR0C = (1<<UCSZ01) | (1<<UCSZ00); /* 8-bit data */
    183   UCSR0B = (1<<TXEN0) | (1<<RXEN0);   /* enable TX and RX */
    184 }
    185 
    186 /* put a character into the bus */
    187 int aaio_uart_putchar(char c, FILE *stream) {
    188   if(c == '\n') aaio_uart_putchar('\r', stream);
    189   loop_until_bit_is_set(UCSR0A, UDRE0);
    190   UDR0 = c;
    191   return 0;
    192 }
    193 
    194 /* get a character from the bus */
    195 int aaio_uart_getchar(FILE *stream) {
    196   while(!(UCSR0A & (1 << RXC0)));
    197   return (UDR0);
    198 }
    199 
    200 /* get a string of length len from the bus, optionally echoing it back */
    201 int aaio_uart_gets(char *buf, unsigned int len, char echo) {
    202   unsigned int i;
    203   char c;
    204   do {
    205     for(i = 0; i < len - 1; i++) {
    206       c = (char) aaio_uart_getchar(NULL);
    207       if(echo != 0) aaio_uart_putchar(c, NULL);
    208       if(c == '\r' || c == '\n') break;
    209       buf[i] = c;
    210     }
    211   } while (i == 0);
    212   buf[i] = 0; /* guard the buffer */
    213   return i;
    214 }
    215 
    216 /* helper utility functions to redirect stdin or stdout */
    217 
    218 FILE _aaio_uart_input = FDEV_SETUP_STREAM(NULL, aaio_uart_getchar, _FDEV_SETUP_READ);
    219 FILE _aaio_uart_output = FDEV_SETUP_STREAM(aaio_uart_putchar, NULL, _FDEV_SETUP_WRITE);
    220 
    221 void aaio_uart_redirect_stdin() {
    222   stdin = &_aaio_uart_input;
    223 }
    224 
    225 void aaio_uart_redirect_stdout() {
    226   stdout = &_aaio_uart_output;
    227 }
    228 
    229 #endif
    230 
    231 /* AAIO_FEATURE_KEYPAD: 16-key keypad manipulation */
    232 
    233 #ifdef AAIO_FEATURE_KEYPAD
    234 
    235 #include <avr/pgmspace.h>
    236 
    237 /* scan the keyboard and return the associated value */
    238 /* the definition must be stored in PROGMEM */
    239 uint8_t aaio_scankey(const uint8_t keypad_def[4][4]) {
    240   uint8_t row, col, dirmask;
    241   for(col = 0; col < 4; col++) {
    242     AAIO_KEYPAD_DDR = dirmask = 1 << (col + 4);
    243     AAIO_KEYPAD_PORT = 0xf & ~dirmask;
    244     for(row = 0; row < 4; row++) {
    245       _delay_ms(5);
    246       if((AAIO_KEYPAD_PIN & (1<<row)) == 0)
    247         return pgm_read_byte(&keypad_def[col][row]);
    248     }
    249     _delay_ms(5);
    250   }
    251   return 0xff; /* return 0xff if no key press is detected */
    252 }
    253 
    254 #endif
    255 
    256 /* AAIO_FEATURE_LCD_5110: Nokia 5110 display manipulation */
    257 /* the charset must already be defined in aaio_5110_charset.h */
    258 
    259 #ifdef AAIO_FEATURE_LCD_5110
    260 
    261 #include "aaio_5110_charset.h"
    262 
    263 #ifndef AAIO_LCD_CONTRAST
    264 #define AAIO_LCD_CONTRAST 0x40
    265 #endif
    266 
    267 /* Nokia 5110 display data structure */
    268 static struct {
    269   uint8_t screen[504];
    270   uint8_t cursor_x;
    271   uint8_t cursor_y;
    272 } _aaio_lcd = {
    273   .cursor_x = 0,
    274   .cursor_y = 0
    275 };
    276 
    277 /* send data to the LCD (in data or command mode) */
    278 static void _aaio_lcd_write(uint8_t byte, uint8_t is_data) {
    279   register uint8_t i;
    280   AAIO_LCD_PORT &= ~(1 << AAIO_LCD_SCE); /* enable the controller */
    281   if(is_data)
    282     AAIO_LCD_PORT |= (1 << AAIO_LCD_DC);
    283   else
    284     AAIO_LCD_PORT &= ~(1 << AAIO_LCD_DC);
    285   for(i = 0; i < 8; i++) {
    286     /* set data pin to byte state */
    287     if((byte >> (7-i)) & 1)
    288       AAIO_LCD_PORT |= (1 << AAIO_LCD_DIN);
    289     else
    290       AAIO_LCD_PORT &= ~(1 << AAIO_LCD_DIN);
    291     /* blink the clock */
    292     AAIO_LCD_PORT |= (1 << AAIO_LCD_CLK);
    293     AAIO_LCD_PORT &= ~(1 << AAIO_LCD_CLK);
    294   }
    295   AAIO_LCD_PORT |= (1 << AAIO_LCD_SCE); /* disable the controller */
    296 }
    297 
    298 static void _aaio_lcd_write_cmd(uint8_t cmd) {
    299   _aaio_lcd_write(cmd, 0);
    300 }
    301 
    302 static void _aaio_lcd_write_data(uint8_t data) {
    303   _aaio_lcd_write(data, 1);
    304 }
    305 
    306 /* init the LCD (must be called before any other function) */
    307 void aaio_lcd_init(void) {
    308   register unsigned i;
    309   /* set pins as output */
    310   AAIO_LCD_DDR |= (1 << AAIO_LCD_SCE);
    311   AAIO_LCD_DDR |= (1 << AAIO_LCD_RST);
    312   AAIO_LCD_DDR |= (1 << AAIO_LCD_DC);
    313   AAIO_LCD_DDR |= (1 << AAIO_LCD_DIN);
    314   AAIO_LCD_DDR |= (1 << AAIO_LCD_CLK);
    315 
    316   /* reset the display */
    317   AAIO_LCD_PORT |= (1 << AAIO_LCD_RST);
    318   AAIO_LCD_PORT |= (1 << AAIO_LCD_SCE);
    319   _delay_ms(10);
    320   AAIO_LCD_PORT &= ~(1 << AAIO_LCD_RST);
    321   _delay_ms(70);
    322   AAIO_LCD_PORT |= (1 << AAIO_LCD_RST);
    323 
    324   AAIO_LCD_PORT &= ~(1 << AAIO_LCD_SCE); /* enable the controller */
    325   _aaio_lcd_write_cmd(0x21); /* LCD extended commands mode */
    326   _aaio_lcd_write_cmd(0xB0); /* default VOP */
    327   _aaio_lcd_write_cmd(0x04); /* set temperature coefficient */
    328   _aaio_lcd_write_cmd(0x14); /* LCD bias mode 1:48 */
    329   _aaio_lcd_write_cmd(0x20); /* standard commands mode, powered down */
    330   _aaio_lcd_write_cmd(0x09); /* LCD in normal mode */
    331 
    332   /* clear LCD RAM */
    333   _aaio_lcd_write_cmd(0x80);
    334   _aaio_lcd_write_cmd(AAIO_LCD_CONTRAST);
    335   for(i = 0; i < 504; i++) _aaio_lcd_write_data(0);
    336 
    337   /* activate the LCD */
    338   _aaio_lcd_write_cmd(0x08);
    339   _aaio_lcd_write_cmd(0x0C);
    340 }
    341 
    342 /* clear the LCD */
    343 void aaio_lcd_clear(void) {
    344   register unsigned i;
    345   /* set column and row to 0 */
    346   _aaio_lcd_write_cmd(0x80);
    347   _aaio_lcd_write_cmd(0x40);
    348   /* reset the cursor */
    349   _aaio_lcd.cursor_x = 0;
    350   _aaio_lcd.cursor_y = 0;
    351   /* clear everything */
    352   for(i = 0; i < 504; i++) _aaio_lcd.screen[i] = 0;
    353 }
    354 
    355 /* power the LCD on/off */
    356 void aaio_lcd_power(uint8_t on) {
    357   _aaio_lcd_write_cmd(on ? 0x20 : 0x24);
    358 }
    359 
    360 /* set a pixel */
    361 void aaio_lcd_set_pixel(uint8_t x, uint8_t y, uint8_t value) {
    362   uint8_t *byte = &_aaio_lcd.screen[(y >> 3) * 84 + x];
    363   if(value) *byte |= (1 << (y & 7));
    364   else *byte &= ~(1 << (y & 7));
    365 }
    366 
    367 /* write an ASCII character */
    368 void aaio_lcd_write_char(char code, uint8_t scale) {
    369   register uint8_t x, y;
    370   for(x = 0; x < 5*scale; x++)
    371     for(y = 0; y < 7*scale; y++)
    372       if(pgm_read_byte(&CHARSET[code-32][x/scale]) & (1 << y/scale))
    373         aaio_lcd_set_pixel(_aaio_lcd.cursor_x + x, _aaio_lcd.cursor_y + y, 1);
    374       else
    375         aaio_lcd_set_pixel(_aaio_lcd.cursor_x + x, _aaio_lcd.cursor_y + y, 0);
    376   _aaio_lcd.cursor_x += 5*scale + 1;
    377   if(_aaio_lcd.cursor_x >= 84) {
    378     _aaio_lcd.cursor_x = 0;
    379     _aaio_lcd.cursor_y += 7*scale + 1;
    380   }
    381   if(_aaio_lcd.cursor_y >= 48) {
    382     _aaio_lcd.cursor_x = 0;
    383     _aaio_lcd.cursor_y = 0;
    384   }
    385 }
    386 
    387 void aaio_lcd_write_string(const char *str, uint8_t scale) {
    388   while(*str) aaio_lcd_write_char(*str++, scale);
    389 }
    390 
    391 void aaio_lcd_set_cursor(uint8_t x, uint8_t y) {
    392   _aaio_lcd.cursor_x = x;
    393   _aaio_lcd.cursor_y = y;
    394 }
    395 
    396 void aaio_lcd_render(void) {
    397   register unsigned i;
    398   /* set column and row to 0 */
    399   _aaio_lcd_write_cmd(0x80);
    400   _aaio_lcd_write_cmd(0x40);
    401   /* write screen data to the display */
    402   for(i = 0; i < 504; i++) _aaio_lcd_write_data(_aaio_lcd.screen[i]);
    403 }
    404 
    405 #endif
    406 
    407 #endif