I got the Hello World! Finally serial communication part is working, next part is to get display refreshing and sift register writing to work efficiently as it must be constantly multiplexed.
And this is how I develop AVR code. Pretty eh?
And code that produces “Hello World!” output:
/* Ketturi Fox 2013 * File: main.c * Project: VFD Driver * Project folder: https://www.dropbox.com/sh/24stwfan7eqthdz/DxebGKKUiF * */ #ifndef F_CPU //#define F_CPU 18432000UL // 18.432 Mhz #define F_CPU 16000000UL // 16 Mhz #endif #define USART_BAUDRATE 19200 //UART baud rate, 19200 works with 16Mhz clock, 38400 works too(tested) #include <inttypes.h> #include <stdint.h> #include <avr/io.h> #include <avr/interrupt.h> #include <avr/pgmspace.h> #include <util/delay.h> // Macros for bit manipulation #define SET_BIT(target, bit) ((target) |= _BV((unsigned char) (bit))) #define CLR_BIT(target, bit) ((target) &= ~(_BV((unsigned char) (bit)))) #define DISPLAY_SIZE 32 //digits #define LONG_DELAY 450 //Delays for multiplexing #define SHORT_DELAY 50 //need to be made interrupts/something useful during delays /* * Font for segments * generated with kukkafontti by magnificent hauvakukka */ // First byte == PORT_A // Second byte == PORT_C //{ (d,c,e,g2,g1,f,b,a),(n/c,i,j,h,m,k,l,dp) }, // ASCII, control charters not handled here, only for reference //some charters, like 1-5 need to be re-purposed as ä,ö,Ä,Ö and forwarded from 132, 148, 142, 153 //handling d.p. should be rethough, now it leaves empty charter left to it //darn I love ASCII <3 const unsigned char font[128][2] = { { 0b00000000, 0b00000000 }, //NUL Null \0 ^@ (null charter, but shows as empty block dunno) { 0b11111111, 0b01111111 }, //can be used like test charter lighting all segments { 0b00000000, 0b00000000 }, { 0b00000000, 0b00000000 }, { 0b00000000, 0b00000000 }, { 0b00000000, 0b00000000 }, { 0b00000000, 0b00000000 }, //ACK Acknowledge (send for data successfully received) { 0b00000000, 0b00000000 }, //BEL Bell \a ^G (make audible notification for email etc) { 0b00000000, 0b00000000 }, //BS Backspace \b ^H (remove last charter from textBuffer) { 0b00000000, 0b00000000 }, { 0b00000000, 0b00000000 }, //LF Linefeed \n ~J (clear textBuffer) { 0b00000000, 0b00000000 }, { 0b00000000, 0b00000000 }, //FF Formfeed \f ^L (clear textBuffer, for compatibly) { 0b00000000, 0b00000000 }, //CR Carriage return \r ^M (Return "cursor" to first digit) { 0b00000000, 0b00000000 }, { 0b00000000, 0b00000000 }, { 0b00000000, 0b00000000 }, { 0b00000000, 0b00000000 }, //DC1 Device control (Turn leds on/off or sumthing) { 0b00000000, 0b00000000 }, //DC2 { 0b00000000, 0b00000000 }, //DC3 { 0b00000000, 0b00000000 }, //DC4 { 0b00000000, 0b00000000 }, //NAK neg. acknowledge (Houston, we have problem) { 0b00000000, 0b00000000 }, { 0b00000000, 0b00000000 }, { 0b00000000, 0b00000000 }, { 0b00000000, 0b00000000 }, { 0b00000000, 0b00000000 }, { 0b00000000, 0b00000000 }, //ESC Escape \e ^[ (dunno, not needed) { 0b00000000, 0b00000000 }, { 0b00000000, 0b00000000 }, { 0b00000000, 0b00000000 }, { 0b00000000, 0b00000000 }, { 0b00000000, 0b00000000 }, //" " space or empty block { 0b01000010, 0b00000001 }, //"!" { 0b00000100, 0b01000000 }, { 0b11011010, 0b01000010 }, { 0b11011101, 0b01000010 }, { 0b01000100, 0b00101000 }, { 0b11101001, 0b01010100 }, { 0b00000000, 0b00100000 }, { 0b00000000, 0b00100100 }, { 0b00000000, 0b00011000 }, { 0b00011000, 0b01111110 }, { 0b00011000, 0b01000010 }, { 0b00000000, 0b00001000 }, { 0b00011000, 0b00000000 }, { 0b00000000, 0b00000001 }, // "." (light dot in display) { 0b00000000, 0b00101000 }, { 0b11100111, 0b00101000 }, { 0b01000010, 0b00000000 }, //"0" { 0b10111011, 0b00000000 }, { 0b11011011, 0b00000000 }, { 0b01011110, 0b00000000 }, { 0b11011101, 0b00000000 }, { 0b11111101, 0b00000000 }, { 0b01000011, 0b00000000 }, { 0b11111111, 0b00000000 }, { 0b11011111, 0b00000000 }, { 0b00000000, 0b01000010 }, //"9" { 0b00000000, 0b01001000 }, { 0b00000000, 0b00100100 }, { 0b10011000, 0b00000000 }, { 0b00000000, 0b00011000 }, { 0b00010011, 0b00000011 }, //"?" { 0b11101011, 0b00000010 }, { 0b01111111, 0b00000000 }, //"A" { 0b11010011, 0b01000010 }, { 0b10100101, 0b00000000 }, { 0b11000011, 0b01000010 }, { 0b10101101, 0b00000000 }, { 0b00101101, 0b00000000 }, { 0b11110101, 0b00000000 }, { 0b01111110, 0b00000000 }, { 0b10000001, 0b01000010 }, { 0b11100010, 0b00000000 }, { 0b00101100, 0b00100100 }, { 0b10100100, 0b00000000 }, { 0b01100110, 0b00110000 }, { 0b01100110, 0b00010100 }, { 0b11100111, 0b00000000 }, { 0b00111111, 0b00000000 }, { 0b11100111, 0b00000100 }, { 0b00111111, 0b00000100 }, { 0b11010001, 0b00010000 }, { 0b00000001, 0b01000010 }, { 0b11100110, 0b00000000 }, { 0b00100100, 0b00101000 }, { 0b01100110, 0b00001100 }, { 0b00000000, 0b00111100 }, { 0b00000000, 0b00110010 }, { 0b10000001, 0b00101000 }, //"Z" { 0b10100101, 0b00000000 }, { 0b00000000, 0b00010100 }, { 0b11000011, 0b00000000 }, { 0b00000000, 0b00001100 }, { 0b10000000, 0b00000000 }, { 0b00000000, 0b00010000 }, { 0b10101000, 0b00000010 }, //"a" { 0b10101100, 0b00000100 }, { 0b10111000, 0b00000000 }, { 0b11010010, 0b00001000 }, { 0b10101000, 0b00001000 }, { 0b00101101, 0b00000000 }, { 0b11010010, 0b00100000 }, { 0b00101100, 0b00000010 }, { 0b00000000, 0b00000010 }, { 0b11000010, 0b00000000 }, { 0b00000000, 0b01100110 }, { 0b00100100, 0b00000000 }, { 0b01111000, 0b00000010 }, { 0b00101000, 0b00000010 }, { 0b11111000, 0b00000000 }, { 0b00101100, 0b00010000 }, { 0b01010010, 0b00100000 }, { 0b00101000, 0b00000000 }, { 0b10010000, 0b00000100 }, { 0b10101100, 0b00000000 }, { 0b11100000, 0b00000000 }, { 0b01000000, 0b00000100 }, { 0b01100000, 0b00001100 }, { 0b00011000, 0b00001100 }, { 0b11000000, 0b00000100 }, { 0b10001000, 0b00001000 }, //"z" { 0b10001001, 0b00011000 }, { 0b00000000, 0b01000010 }, { 0b10010001, 0b00100100 }, { 0b00000100, 0b00110000 }, { 0b11111111, 0b01111110 } //DEL (also not needed, ignored charter" }; /* * Port & device init */ void boot(void) { // Set port directions and pull-ups DDRA = 0b11111111; DDRC = 0b11111111; DDRB = 0b00001111; } void writeBitToShiftRegister(int bit){ /* Shiftregister configuration: *Strobe (outputs enabled) PB0 *Latch enable (Latch clock) PB1 *Clock PB2 *Data In PB3 * *Data is entered with L -> H clock edge *While Latch Enable is H, Data -> Outputs *Strobe puts outputs on when L. (can be tied to L) */ #define Strobe_PORT PORTB #define Latch_PORT PORTB #define Clock_PORT PORTB #define Data_PORT PORTB #define Strobe_BIT 0 #define Latch_BIT 1 #define Clock_BIT 2 #define Data_BIT 3 #define SET_Strobe ((Strobe_PORT) |= _BV(Strobe_BIT)) #define CLR_Strobe ((Strobe_PORT) &= ~(_BV(Strobe_BIT))) #define SET_Latch ((Latch_PORT) |= _BV(Latch_BIT)) #define CLR_Latch ((Latch_PORT) &= ~(_BV(Latch_BIT))) #define SET_Clock ((Clock_PORT) |= _BV(Clock_BIT)) #define CLR_Clock ((Clock_PORT) &= ~(_BV(Clock_BIT))) #define SET_Data ((Data_PORT) |= _BV(Data_BIT)) #define CLR_Data ((Data_PORT) &= ~(_BV(Data_BIT))) SET_Strobe; //Put all outputs low _delay_us(0.010); CLR_Latch; //Latch low(keep previoysly stored data in latch) _delay_us(0.010); if (bit == 1) //Load data from function to shift register input SET_Data; else CLR_Data; _delay_us(0.150); //Setup time, DATA IN before rising CLOCK edge CLR_Clock; //Start Clock cycle should last 1000ns _delay_us(0.500); //Pulse furation, CLOCK High SET_Clock; //End Clock cycle _delay_us(0.500); //Pulse duration, CLOCK Low SET_Latch; //Move data to latch (New data in latch) CLR_Strobe; //Enable output buffers } unsigned char textBuffer[32]; //here goes the text on display void refreshDisplay() { //draws every digit to display, must be executed constantly int i; // Make sure everything is off. PORTA = 0; PORTC = 0; // Clear shift register to be sure. for (i = 0; i < DISPLAY_SIZE; i++) { writeBitToShiftRegister(0); } // Select first display digit. writeBitToShiftRegister(1); // Loop through all digits for (i = 0; i < DISPLAY_SIZE; i++) { // Read next character. // The character is masked to make sure it doesn't go outside font range. const unsigned char c = textBuffer[i] & 0b01111111; // Read bytes for corresponding segment configuration. const unsigned char byteA = font[c][0]; const unsigned char byteB = font[c][1]; // Turn segments on. PORTA = byteA; PORTC = byteB; // Wait. interrupt/other stuff here, delay is stupid _delay_us(LONG_DELAY); // Turn segments off. PORTA = 0; PORTC = 0; // Wait. _delay_us(SHORT_DELAY); // Prepare for next display module. writeBitToShiftRegister(0); } } void uart_init(uint16_t baudrate) { UBRRL = baudrate; UBRRH = (baudrate >> 8); UCSRC = (1 << URSEL) | (3 << UCSZ0); UCSRB = (1 << RXEN) | (1 << TXEN); // Enable RX and TX } void uart_putchar(char c) { //puts one charter to serial send buffer loop_until_bit_is_set(UCSRA, UDRE); // Wait until data register empty. UDR = c; } char uart_getchar(void) { //gets 1 charter from serial receive buffer loop_until_bit_is_set(UCSRA, RXC); // Wait until data exists. return UDR; } int main(void){ //textBuffer = "Hello World! "; //refreshDisplay(); //loop here to get charter from uart_getchar(); //and put it to textBuffer, and then refreshDisplay(); //interrupts etc. stuff would be nice uart_init((((F_CPU / (USART_BAUDRATE * 16UL))) - 1)); //initialize uart at calculated baudrate while(1){ uart_putchar('H'); uart_putchar('e'); uart_putchar('l'); uart_putchar('l'); uart_putchar('o'); uart_putchar(' '); uart_putchar('w'); uart_putchar('o'); //just stupid testing uart_putchar('r'); uart_putchar('l'); uart_putchar('d'); uart_putchar('!'); uart_putchar('\n'); uart_putchar('\r'); } return 0; }