Configuring Standard I/O Streaming: Io - Test.c
Configuring Standard I/O Streaming: Io - Test.c
C languages defines a stdio library that performs standard input and output. Typically that standard input and output are defined to be the user's keyboard and monitor. An embedded system does not normally have a keyboard or monitor. The input and output sources for the AVR stdio library must defined. An example of how to define the source of input and output is: io_test.c
#include #include #include #include #include <stdio.h> <avr/sleep.h> <avr/power.h> <avr/pgmspace.h> "usart0.h"
static int my_putchar (char c, FILE *stream) { if (c == '\n') usart0_put( '\r' ); usart0_put( c ); return 0; } static int my_getchar(FILE * stream) { return usart0_get(); } /* * Define the input and output streams. * The stream implemenation uses pointers to functions. */ static FILE my_stream = FDEV_SETUP_STREAM (my_putchar, my_getchar, _FDEV_SETUP_RW);
#define SYSTEM_CLOCK (8000000) int main (void) { PORTC = 0xff; // all off DDRC = 0xff; // drives LEDs clock_prescale_set( clock_div_1 ); // Initialize the standard streams to the user defined one stdout = &my_stream;
stdin = &my_stream; usart0_init( BAUD_RATE(SYSTEM_CLOCK,9600) ); printf_P(PSTR("Hello, world!\n")); // printf("Hello, world!\n"); while ( 1 ) { int ch = getchar(); PORTC = ch; putchar( ch ); } return 0; }
local_stdio.h
local_stdio.c
static int my_putchar (char c, FILE *stream) { if (c == '\n') usart0_put( '\r' ); usart0_put( c ); return 0; } static int my_getchar(FILE * stream) { return usart0_get(); }
static FILE my_stream = FDEV_SETUP_STREAM (my_putchar, my_getchar, _FDEV_SETUP_RW); void local_stdio_init( void ) { stdout = &my_stream; stdin = &my_stream; }
This module is coupled with the usart0 module. The coupling is not obvious.
the setting of the Data Direction Registers, and the setting of the Port Registers
volatile uint8_t * get_ddr( char c ) { switch( c ) { case 'a': case 'A': return &DDRA; case 'b': case 'B': return &DDRB; case 'c': case 'C': return &DDRC; case 'd': case 'D': return &DDRD; default: return 0; } }
The volatile type modify is necessary for I/O registers to ensure that the register is always read or written.
This routine ensures that the buffer is not accessed outside of its bounds.
#include "usart0.h" #include "local_stdio.h" #define SYSTEM_CLOCK (8000000) /* * Read and echo a line. */ static int input_line( char buf[], int sz ) { int i = 0; char c; sz--; // accounts for nul while( (c=getchar()) != '\n' && c != '\r' && i < sz ) { putchar( c ); buf[i] = c; i++; } putchar( '\n' ); buf[i] = '\0'; return i; } volatile uint8_t * get_port( char c ) { switch( c ) { case 'a': case 'A': return &PORTA; case 'b':
case 'B': return case 'c': case 'C': return case 'd': case 'D': return default: return } }
&PORTB;
&PORTC;
&PORTD; 0;
volatile uint8_t * get_ddr( char c ) { switch( c ) { case 'a': case 'A': return &DDRA; case 'b': case 'B': return &DDRB; case 'c': case 'C': return &DDRC; case 'd': case 'D': return &DDRD; default: return 0; } } int main( void ) { char buf[128]; clock_prescale_set( clock_div_1 ); usart0_init( BAUD_RATE(SYSTEM_CLOCK,9600) ); local_stdio_init(); while ( 1 ) { int len = input_line( buf, sizeof(buf) );
char ch; int value; int m; m = sscanf_P(buf, PSTR(" port %c %x"), &ch, &value); if ( 1 == m ) { volatile uint8_t *port = get_port( ch ); printf_P( PSTR("\nPORT%c = %x\n"), ch, *port ); continue; } if ( 2 == m ) { printf_P( PSTR("setting port %c\n"), ch ); volatile uint8_t *port = get_port( ch ); *port = value; continue; } m = sscanf_P(buf, PSTR(" ddr %c %x"), &ch, &value); if ( 1 == m ) { volatile uint8_t *ddr = get_ddr( ch ); printf_P( PSTR("\nDDR%c = %x\n"), ch, *ddr ); continue; } if ( 2 == m ) { printf_P( PSTR("setting ddr %c\n"), ch ); volatile uint8_t *ddr = get_ddr( ch ); *ddr = value; continue; } printf_P( PSTR("unknown command\n") ); } return 0; } scanf will attempt to match the characters in the format string with the input characters. A space matches a sequence of white space characters. scanfreturns the number of successful
conversions.
if ( !strcmp_P( reg, return &PORTA; } else if ( !strcmp_P( return &PORTB; } else if ( !strcmp_P( return &PORTC; } else if ( !strcmp_P( return &PORTD; } else if ( !strcmp_P( return &DDRA; } else if ( !strcmp_P( return &DDRB; } else if ( !strcmp_P( return &DDRC; } else if ( !strcmp_P( return &DDRD; } else if ( !strcmp_P( return &PINA; } else if ( !strcmp_P( return &PINB; } else if ( !strcmp_P( return &PINC; } else if ( !strcmp_P( return &PIND; } else if ( !strcmp_P( return &TCNT0; } else if ( !strcmp_P( return &OCR0A; } else if ( !strcmp_P( return &OCR0B; }
PSTR("porta")) ) {
reg, PSTR("portb")) ) {
reg, PSTR("portc")) ) {
reg, PSTR("portd")) ) {
reg, PSTR("ddra")) ) {
reg, PSTR("ddrb")) ) {
reg, PSTR("ddrc")) ) {
reg, PSTR("ddrd")) ) {
reg, PSTR("pina")) ) {
reg, PSTR("pinb")) ) {
reg, PSTR("pinc")) ) {
reg, PSTR("pind")) ) {
reg, PSTR("tcnt0")) ) {
reg, PSTR("ocr0a")) ) {
reg, PSTR("ocr0b")) ) {
else if ( !strcmp_P( reg, PSTR("tccr0a")) ) { return &TCCR0A; } else if ( !strcmp_P( reg, PSTR("tccr0b")) ) { return &TCCR0B; } return 0; }
A Modified Application
The application to control the device registers is reimplemented using more library function. Reading a line is now performed with fgets. The source for the reimplementation is:
#include #include #include #include #include #include #include <avr/io.h> <avr/power.h> <avr/pgmspace.h> <string.h> <stdio.h> "usart0.h" "local_stdio.h"
reg_lookup.c
#define SYSTEM_CLOCK (8000000) static volatile uint8_t * reg_lookup( const char reg[] ) { if ( !strcmp_P( reg, PSTR("porta")) ) { return &PORTA; } else if ( !strcmp_P( reg, PSTR("portb")) ) { return &PORTB; } else if ( !strcmp_P( reg, PSTR("portc")) ) { return &PORTC; } else if ( !strcmp_P( reg, PSTR("portd")) ) { return &PORTD; } else if ( !strcmp_P( reg, PSTR("ddra")) ) { return &DDRA; } else if ( !strcmp_P( reg, PSTR("ddrb")) ) { return &DDRB; }
else if ( !strcmp_P( return &DDRC; } else if ( !strcmp_P( return &DDRD; } else if ( !strcmp_P( return &PINA; } else if ( !strcmp_P( return &PINB; } else if ( !strcmp_P( return &PINC; } else if ( !strcmp_P( return &PIND; } else if ( !strcmp_P( return &TCNT0; } else if ( !strcmp_P( return &OCR0A; } else if ( !strcmp_P( return &OCR0B; } else if ( !strcmp_P( return &TCCR0A; } else if ( !strcmp_P( return &TCCR0B; } return 0; } int main( void ) { char buf[64];
reg, PSTR("ddrc")) ) {
reg, PSTR("ddrd")) ) {
reg, PSTR("pina")) ) {
reg, PSTR("pinb")) ) {
reg, PSTR("pinc")) ) {
reg, PSTR("pind")) ) {
reg, PSTR("tcnt0")) ) {
reg, PSTR("ocr0a")) ) {
reg, PSTR("ocr0b")) ) {
reg, PSTR("tccr0a")) ) {
reg, PSTR("tccr0b")) ) {
while ( 1 ) { fgets( buf, sizeof(buf), stdin ); char cmd[9]; int value; int m; cmd[8] = '\0'; // %8s and %4x limits the maximum input m = sscanf_P(buf, PSTR("%8s %4x"), cmd, &value); //printf_P( PSTR("debug: %d %s %x\n"), m, cmd, value ); if ( 1 == m ) { volatile uint8_t *reg = reg_lookup( cmd ); if ( !reg ) goto err; printf_P( PSTR("%s : %x\n"), cmd, *reg ); } else if ( 2 == m ) { volatile uint8_t *reg = reg_lookup( cmd ); if ( !reg ) goto err; *reg = value; printf_P( PSTR("%s = %x\n"), cmd, *reg ); } else { printf_P( PSTR("bad command\n") ); } /* * the above code can be rewritten to be cleaner. */ } return 0; }
err:
$(OBJCOPY) -O srec io_test.elf io_test.srec stdio_test.srec: stdio_test.elf $(OBJCOPY) -O srec stdio_test.elf stdio_test.srec reg_lookup.srec: reg_lookup.elf $(OBJCOPY) -O srec reg_lookup.elf reg_lookup.srec io_test.elf: io_test.o usart0.o $(LD) -mmcu=$(MCU) io_test.o usart0.o -o $@ $(SIZE) $@ stdio_test.elf: stdio_test.o usart0.o local_stdio.o $(LD) -mmcu=$(MCU) stdio_test.o usart0.o local_stdio.o -o $@ $(SIZE) $@ reg_lookup.elf: reg_lookup.o usart0.o local_stdio.o $(LD) -mmcu=$(MCU) reg_lookup.o usart0.o local_stdio.o -o $@ $(SIZE) $@ burn: io_test.srec avrdude -P $(TTY) -p $(BMCU) -c stk500v2 -U flash:w:io_test.srec burn_stdio: stdio_test.srec avrdude -P $(TTY) -p $(BMCU) -c stk500v2 -U flash:w:stdio_test.srec burn_reg_lookup: reg_lookup.srec avrdude -P $(TTY) -p $(BMCU) -c stk500v2 -U flash:w:reg_lookup.srec usart0.o: usart0.c usart0.h $(CC) $(CFLAGS) -mmcu=$(MCU) -c usart0.c local_stdio.o: local_stdio.c local_stdio.h $(CC) $(CFLAGS) -mmcu=$(MCU) -c local_stdio.c io_test.o: io_test.c usart0.h $(CC) $(CFLAGS) -mmcu=$(MCU) -c io_test.c stdio_test.o: stdio_test.c usart0.h local_stdio.h $(CC) $(CFLAGS) -mmcu=$(MCU) -c stdio_test.c reg_lookup.o: reg_lookup.c usart0.h local_stdio.h $(CC) $(CFLAGS) -mmcu=$(MCU) -c reg_lookup.c