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