AVR driver for a group of 7-seg displays (with 70hc4094), controlled by UART.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

276 lines
5.5 KiB

9 years ago
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdint.h>
#include <stdbool.h>
#include "lib/iopins.h"
#include "lib/uart.h"
// Configure what pins to use
#define IO_DATA D2
#define IO_CLK D3
#define IO_STR D4
/*
| LED display driver
|
| Two chained 70hc4094 are connected.
|
| One drives common cathodes, the other
| the segments.
|
| Display is controlled over UART at 115200 baud.
|
| Commands:
|
| R - reset (clear display)
|
| Aaaaaaaaa - set value using ASCII. Supported are digits and letters A-F.
| Period ('.') adds a decimal point to the last-entered symbol
| (does not move "cursor"). It is not possible to add DP to the
| last symbol using ASCII mode.
|
| Bbbbbbbbb - Set segments using binary mode (no conversion).
| The bytes have the following format: 0bHGFEDCBA
|
| Ll - Set brightness. `l` is a byte (0-255) determining the level.
|
*/
// ---- Segment definitions -----------------------
#define SEG_A 0x01
#define SEG_B 0x02
#define SEG_C 0x04
#define SEG_D 0x08
#define SEG_E 0x10
#define SEG_F 0x20
#define SEG_G 0x40
#define SEG_DP 0x80
/** Digits */
enum {
SYM_BLANK = 0,
SYM_0 = SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F,
SYM_1 = SEG_B | SEG_C,
SYM_2 = SEG_A | SEG_B | SEG_G | SEG_E | SEG_D,
SYM_3 = SEG_B | SEG_C | SEG_A | SEG_D | SEG_G,
SYM_4 = SEG_F | SEG_G | SEG_B | SEG_C,
SYM_5 = SEG_A | SEG_F | SEG_G | SEG_C | SEG_D,
SYM_6 = SEG_A | SEG_F | SEG_E | SEG_D | SEG_C | SEG_G,
SYM_7 = SEG_A | SEG_B | SEG_C,
SYM_8 = SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G,
SYM_9 = SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G,
SYM_A = SEG_A | SEG_B | SEG_C | SEG_E | SEG_F | SEG_G,
SYM_B = SEG_C | SEG_D | SEG_E | SEG_F | SEG_G,
SYM_C = SEG_A | SEG_D | SEG_E | SEG_F,
SYM_D = SEG_B | SEG_C | SEG_D | SEG_E | SEG_G,
SYM_E = SEG_A | SEG_D | SEG_E | SEG_F | SEG_G,
SYM_F = SEG_A | SEG_E | SEG_F | SEG_G,
};
/** Conversion table */
const uint8_t num2seg[16] PROGMEM = {
SYM_0, SYM_1, SYM_2, SYM_3,
SYM_4, SYM_5, SYM_6, SYM_7,
SYM_8, SYM_9, SYM_A, SYM_B,
SYM_C, SYM_D, SYM_E, SYM_F
};
// ---- Routines for loading data to the display registers ----------
void disp_load(uint8_t place, uint8_t segments)
{
uint16_t w;
if (segments == 0) {
w = 0;
} else {
w = (1 << (place + 8)) | segments;
}
for (uint8_t i = 0; i < 16; i++) {
// bit value to data line
set_pin(IO_DATA, (bool)(w & 0x8000));
// pulse clock
pin_high(IO_CLK);
pin_low(IO_CLK);
w <<= 1;
}
// pulse STR
pin_high(IO_STR);
pin_low(IO_STR);
}
/** Brightness table (for smoother brightness adjustment) */
const uint8_t BRIGHT_128[128] PROGMEM = {
0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4,
5, 5, 6, 6, 6, 7, 7, 8, 8, 8, 9, 10, 10, 10, 11, 12, 13, 14,
14, 15, 16, 17, 18, 20, 21, 22, 24, 26, 27, 28, 30, 31, 32, 34, 35, 36,
38, 39, 40, 41, 42, 44, 45, 46, 48, 49, 50, 52, 54, 56, 58, 59, 61, 63,
65, 67, 68, 69, 71, 72, 74, 76, 78, 80, 82, 85, 88, 90, 92, 95, 98, 100,
103, 106, 109, 112, 116, 119, 122, 125, 129, 134, 138, 142, 147, 151,
153, 156, 160, 163, 165, 170, 175, 180, 185, 190, 195, 200, 207, 214, 218,
221, 225, 228, 232, 234, 241, 248, 254, 255
};
/** Buffer for input */
uint8_t screen_buf[8];
uint8_t scr_buf_i;
/** Array of currently displayed segments */
uint8_t screen[8];
uint8_t level = 255;
uint8_t delay_l = 255;
enum {
STATE_NONE,
STATE_ASCII,
STATE_BINARY,
STATE_BRIGHTNESS
} state;
inline void cpy_buf2scr(void)
{
for (uint8_t i = 0; i < 8; i++) {
screen[i] = screen_buf[i];
}
}
// Incoming data IRQ
ISR(USART_RX_vect)
{
uint8_t b = uart_rx();
uart_tx(b); // send back
switch (state) {
case STATE_NONE:
// Choose what to do
if (b >= 'a' && b <= 'z') {
b -= 'a' - 'A'; // make uppercase
}
switch (b) {
case 'A':
state = STATE_ASCII;
scr_buf_i = 0;
break;
case 'B':
state = STATE_BINARY;
scr_buf_i = 0;
break;
case 'R':
for (uint8_t i = 0; i < 8; i++) {
screen[i] = SYM_BLANK;
}
break;
case 'L':
state = STATE_BRIGHTNESS;
break;
default:
break;
}
break;
case STATE_BRIGHTNESS:
level = b;
delay_l = pgm_read_byte(&BRIGHT_128[level >> 1]);
state = STATE_NONE;
break;
case STATE_ASCII:
if (b >= 'a' && b <= 'z') {
b -= 'a' - 'A'; // make uppercase
}
if (b >= '0' && b <= '9') {
// numbers
screen_buf[scr_buf_i++] = pgm_read_byte(&num2seg[b - '0']);
} else if (b >= 'A' && b <= 'F') {
// hex
screen_buf[scr_buf_i++] = pgm_read_byte(&num2seg[10 + (b - 'A')]);
} else if (b == '-') {
// numbers
screen_buf[scr_buf_i++] = SEG_G; // minus
} else if (b == '.') {
// add period to previous symbol
if (scr_buf_i > 0) {
screen_buf[scr_buf_i - 1] |= SEG_DP;
}
} else {
// default - blank
screen_buf[scr_buf_i++] = SYM_BLANK;
}
if (scr_buf_i == 8) {
cpy_buf2scr();
state = STATE_NONE;
}
break;
case STATE_BINARY:
screen_buf[scr_buf_i++] = b; // no processing
if (scr_buf_i == 8) {
cpy_buf2scr();
state = STATE_NONE;
}
break;
}
}
void main()
{
_uart_init(8); // set usart @ 115200
as_output(IO_DATA);
as_output(IO_CLK);
as_output(IO_STR);
uart_isr_rx(true);
sei();
while (1) {
// display loop
for (register uint8_t i = 0; i < 8; i++) {
disp_load(i, screen[i]);
for (register uint8_t j = 0; j < 255; j++) {
if (j == delay_l) disp_load(0, 0);
__builtin_avr_delay_cycles(5);
}
}
}
}