Vous êtes sur la page 1sur 13

Configuring Standard I/O Streaming

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; }

A Library For Standard Streams


Since the initialization of the USART is common, a module for this is:
#ifndef LOCAL_STDIO_H #define LOCAL_STDIO_H void local_stdio_init( void ); #endif

local_stdio.h

The implementation is:


#include #include #include #include #include <stdio.h> <avr/sleep.h> <avr/power.h> <avr/pgmspace.h> "usart0.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.

Application Using The Stdio Library


The following application allows:

the setting of the Data Direction Registers, and the setting of the Port Registers

with commands from a PC. The PC is attached with a serial connection.

Accessing The Registers By Lookup


The address of the data direction and port registers are accessed with a character, 'a' for PORTA, 'b' for PORTB and so on. The character is sent as part of a command.
volatile uint8_t * get_port( char c ) { switch( c ) { case 'a': case 'A': return &PORTA; case 'b': case 'B': return &PORTB; case 'c': case 'C': return &PORTC; case 'd': case 'D': return &PORTD; default: return 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; } }

The volatile type modify is necessary for I/O registers to ensure that the register is always read or written.

Echoing The Command


The characters received from the PC are immediately echoed. This aids in debugging when a terminal emualtor is used.
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; }

This routine ensures that the buffer is not accessed outside of its bounds.

Main Routine For stdio_test.c


The main routine reads a line, parses the line with scanf and then executes either a read or write operation on the selected port. stdio_test.c
#include #include #include #include <avr/io.h> <avr/power.h> <avr/pgmspace.h> <stdio.h>

#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.

Using The String Library


The string library can be used to match registers with the addresses. The strcmp function compares two character strings. The following routine returns the register with the matching name.
static volatile uint8_t * reg_lookup( const char reg[] ) {

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")) ) {

clock_prescale_set( clock_div_1 ); usart0_init( BAUD_RATE(SYSTEM_CLOCK, 9600) ); local_stdio_init();

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:

Notice the use of a goto statement.

Makefile for examples


Makefile
MCU= atmega644 BMCU = m644 CC= avr-gcc LD= avr-gcc CFLAGS= -Os OBJCOPY = avr-objcopy SIZE = avr-size TTY = /dev/ttyS0 io_test.srec: io_test.elf

$(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

clean: rm -f *.o *.elf *.srec

Vous aimerez peut-être aussi