reimplement UART with interrupt support (now USART)
This commit is contained in:
parent
4342eb802b
commit
b248b82033
|
@ -0,0 +1,172 @@
|
|||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/* Copyright (c) 2015 King Kévin <kingkevin@cuvoodoo.info> */
|
||||
/* this library handles the USART
|
||||
* it uses the TX and RX pins
|
||||
* it can be used with or without interrupts
|
||||
*/
|
||||
|
||||
#include <stdint.h> // Standard Integer Types
|
||||
#include <stdio.h> // Standard IO facilities
|
||||
#include <stdlib.h> // General utilities
|
||||
|
||||
#include <avr/io.h> // AVR device-specific IO definitions
|
||||
#include <avr/interrupt.h> // Interrupts
|
||||
#include <avr/sleep.h> // Power Management and Sleep Modes
|
||||
|
||||
#include "usart.h" // USART header
|
||||
#include <util/setbaud.h> // Helper macros for baud rate calculations
|
||||
|
||||
/* assign input and output streams */
|
||||
FILE usart_input = FDEV_SETUP_STREAM(NULL, (int (*)(struct __file *)) usart_getchar, _FDEV_SETUP_READ);
|
||||
#if defined(USART_INTERRUPT) && (USART_INTERRUPT==1)
|
||||
FILE usart_output = FDEV_SETUP_STREAM((int (*)(char, struct __file *)) usart_putchar_nonblocking, NULL, _FDEV_SETUP_WRITE);
|
||||
FILE usart_io = FDEV_SETUP_STREAM((int (*)(char, struct __file *)) usart_putchar_nonblocking, (int (*)(struct __file *)) usart_getchar, _FDEV_SETUP_RW);
|
||||
#else
|
||||
FILE usart_output = FDEV_SETUP_STREAM((int (*)(char, struct __file *)) usart_putchar_blocking, NULL, _FDEV_SETUP_WRITE);
|
||||
FILE usart_io = FDEV_SETUP_STREAM((int (*)(char, struct __file *)) usart_putchar_blocking, (int (*)(struct __file *)) usart_getchar, _FDEV_SETUP_RW);
|
||||
#endif
|
||||
|
||||
#if defined(USART_INTERRUPT) && (USART_INTERRUPT==1)
|
||||
/* input and output ring buffer, indexes, and available memory */
|
||||
uint8_t rx_buffer[32] = {0};
|
||||
volatile uint8_t rx_i = 0;
|
||||
volatile uint8_t rx_used = 0;
|
||||
uint8_t tx_buffer[32] = {0};
|
||||
volatile uint8_t tx_i = 0;
|
||||
volatile uint8_t tx_used = 0;
|
||||
/* show the user how much received data is ready */
|
||||
volatile uint8_t usart_incoming = 0; // same as rx_used, but since the user can write this variable we don't rely on it
|
||||
#endif
|
||||
|
||||
/* configure USART serial port */
|
||||
void usart_init(void)
|
||||
{
|
||||
UBRR0H = UBRRH_VALUE; // set baurate (calculated from BAUD)
|
||||
UBRR0L = UBRRL_VALUE; // set baurate (calculated from BAUD)
|
||||
#if USE_2X
|
||||
UCSR0A |= (1<<U2X0); // use double speed (set depending on BAUD)
|
||||
#else
|
||||
UCSR0A &= ~(1<<U2X0); // do not use double speed (set depending on BAUD)
|
||||
#endif
|
||||
UCSR0C = (0<<UMSEL00)|(0<<UMSEL01)|(0<<UCSZ02)|(1<<UCSZ01)|(1<<UCSZ00)|(0<<UPM01)|(0<<UPM00)|(0<<USBS0); // async 8N1 bit data
|
||||
UCSR0B = (1<<RXEN0)|(1<<TXEN0); // enable RX and TX
|
||||
#if defined(USART_INTERRUPT) && (USART_INTERRUPT==1)
|
||||
UCSR0B |= (1<<RXCIE0)|(1<<UDRIE0); // enable RX complete and TX data register empty interrupt
|
||||
// reset buffer states
|
||||
rx_i = 0;
|
||||
rx_used = 0;
|
||||
tx_i = 0;
|
||||
tx_used = 0;
|
||||
#else
|
||||
UCSR0B &= ~((1<<RXCIE0)|(1<<UDRIE0)); // disable RX complete and TX data register empty interrupt
|
||||
#endif
|
||||
}
|
||||
|
||||
/* put character on USART stream (blocking) */
|
||||
void usart_putchar_blocking(char c, FILE *stream)
|
||||
{
|
||||
if (c == '\n') { // add carrier return before line feed. this is recommended for most UART terminals
|
||||
usart_putchar_blocking('\r', stream);
|
||||
}
|
||||
#if defined(USART_INTERRUPT) && (USART_INTERRUPT==1)
|
||||
// idle until buffer is empty
|
||||
while (tx_used) {
|
||||
set_sleep_mode(SLEEP_MODE_IDLE);
|
||||
sleep_mode();
|
||||
}
|
||||
#endif
|
||||
while (!(UCSR0A&(1<<UDRE0))); // wait for transmit register to empty
|
||||
UDR0 = c; // put data in transmit register
|
||||
}
|
||||
|
||||
/* ensure all data has been transmitted */
|
||||
void usart_flush()
|
||||
{
|
||||
#if defined(USART_INTERRUPT) && (USART_INTERRUPT==1)
|
||||
while (tx_used) { // idle until buffer is empty
|
||||
set_sleep_mode(SLEEP_MODE_IDLE);
|
||||
sleep_mode();
|
||||
}
|
||||
#endif
|
||||
while (!(UCSR0A&(1<<TXC0))); // wait for transmission to complete
|
||||
UCSR0A |= (1<<TXC0); // clear transmit flag
|
||||
}
|
||||
|
||||
/* get character from USART stream (blocking) */
|
||||
char usart_getchar(FILE *stream)
|
||||
{
|
||||
#if defined(USART_INTERRUPT) && (USART_INTERRUPT==1)
|
||||
// idle until data is available
|
||||
while (!rx_used) {
|
||||
set_sleep_mode(SLEEP_MODE_IDLE);
|
||||
sleep_mode();
|
||||
}
|
||||
char to_return = rx_buffer[rx_i];
|
||||
rx_i = (rx_i+1)%sizeof(rx_buffer); // update used buffer
|
||||
rx_used--; // update used buffer
|
||||
usart_incoming = rx_used; // update available data
|
||||
return to_return;
|
||||
#else
|
||||
while (!(UCSR0A&(1<<RXC0))); // wait until data exists
|
||||
return UDR0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(USART_INTERRUPT) && (USART_INTERRUPT==1)
|
||||
ISR(USART_RX_vect) { // USART receive interrupt
|
||||
// only save data if there is space in the buffer
|
||||
if (rx_used>=sizeof(rx_buffer)) {
|
||||
return;
|
||||
}
|
||||
cli();
|
||||
rx_buffer[(rx_i+rx_used)%sizeof(rx_buffer)] = UDR0; // put character in buffer
|
||||
rx_used++; // update used buffer
|
||||
usart_incoming = rx_used; // update available data
|
||||
sei();
|
||||
}
|
||||
|
||||
/* put character on USART stream (non-blocking using a buffer) */
|
||||
void usart_putchar_nonblocking(char c, FILE *stream)
|
||||
{
|
||||
if (c == '\n') { // add carrier return before line feed. this is recommended for most UART terminals
|
||||
usart_putchar_nonblocking('\r', stream);
|
||||
}
|
||||
if (UCSR0A&(1<<UDRE0)) { // if transmit register is empty immediatly send the data
|
||||
UDR0 = c; // put data in transmit register
|
||||
} else { // save into buffer
|
||||
// idle until buffer has some space
|
||||
while (tx_used>=sizeof(tx_buffer)) {
|
||||
set_sleep_mode(SLEEP_MODE_IDLE);
|
||||
sleep_mode();
|
||||
}
|
||||
cli();
|
||||
tx_buffer[(tx_i+tx_used)%sizeof(tx_buffer)] = c; // put character in buffer
|
||||
tx_used++; // update used buffer
|
||||
sei();
|
||||
}
|
||||
}
|
||||
|
||||
ISR(USART_UDRE_vect) { // USART transmit register interrupt
|
||||
if (!tx_used) { // no data in the buffer to transmit
|
||||
return;
|
||||
}
|
||||
UDR0 = tx_buffer[tx_i]; // put data in transmit register
|
||||
tx_i = (tx_i+1)%sizeof(rx_buffer); // update location on buffer
|
||||
tx_used--; // update used size
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/* Copyright (c) 2015 King Kévin <kingkevin@cuvoodoo.info> */
|
||||
/* This library handles the USART configuration */
|
||||
|
||||
/* use interrupts */
|
||||
#define USART_INTERRUPT 1
|
||||
/* set tolerance to 3% to allow 115200 baudrate with 16 MHz clock, else use 9600 for default <2% */
|
||||
#define BAUD_TOL 3
|
||||
/* serial baudrate, in bits per second (with 8N1 8 bits, no parity bit, 1 stop bit settings) */
|
||||
#define BAUD 9600
|
||||
|
||||
/* input & output streams */
|
||||
FILE usart_output;
|
||||
FILE usart_input;
|
||||
FILE usart_io;
|
||||
|
||||
/* show the user how much received data is ready */
|
||||
volatile uint8_t usart_incoming;
|
||||
|
||||
/* configure serial port */
|
||||
void usart_init(void);
|
||||
/* put character on USART stream (blocking) */
|
||||
void usart_putchar_blocking(char c, FILE *stream);
|
||||
/* ensure all data has been transmitted */
|
||||
void usart_flush();
|
||||
/* get character from USART stream (blocking) */
|
||||
char usart_getchar(FILE *stream);
|
||||
#if defined(USART_INTERRUPT) && (USART_INTERRUPT==1)
|
||||
/* put character on USART stream (non-blocking using a buffer) */
|
||||
void usart_putchar_nonblocking(char c, FILE *stream);
|
||||
#endif
|
Loading…
Reference in New Issue