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