Liste der Anhänge anzeigen (Anzahl: 2)
Senden und empfangen auf dem UART mit ISR kompatibel zur bisherigen RP6lib
Hallo...
ich hab vor einiger Zeit mal versuchsweise die RP6uart.c umgeschrieben so das sie per ISR sendet.
da ich den Code grade wieder vor liegen habe, poste ich ihn mal zur allgemeinen Begutachtung.
Anlass für die Änderungen waren immer wieder Laufzeitprobleme im Zusammenhang mit UART
Ausgaben sowie teilweise Datenmüll auf der Receiver.
Es wurden noch ein paar Dinge korrigiert.
U.a kann man nun das Zeichen 0x00 zuverlässig empfangen.
Die Sendefunktion blockt nur bis im Software SendeBuffer wieder Platz ist, sonst wird normal weiter gearbeitet.
Den Rest erledigt die SendeISR. Die ReceiverISR prüft nun auf Fehler und nimmt nur Zeichen an wenn kein Datenmüll anliegt.
Bei erkanntem Software Bufferoverrun setzt die Lesefunktion den LeseBuffer zurück.
Die dazu aufgerufene Funktion clearReceptionBuffer(); kann man gut für eigenes Debuging nutzen.
Man kann alle alten Funktionen wie gehabt nutzen, es gibt aber zusätlich 2 Ersatzfunktionen
mit Namen serial_sendc und serial_getc, serial_sendc macht das gleiche wie writeChar, serial_getc
liest das Zeichen jedoch auf ein Pointer (eines buffers) und gibt im Returnwert 0/1 zurück ob ein Zeichen
gelesen wurde. Wenn serial_getc eine 1 zurück gibt ist das Zeichen auf der Bufferadresse gültig!
Auch wenn 0x00 übertragen wurde. Der Sende und Empfangsteil ist unabhängig voneinander,
man kann auch unterschiedlich große Buffer in der .h einstellen. Die Pointer in den Ringbuffern
nutzen jeweils nur noch head und tail. Die Codegröße hat sich kaum verändert, einige Vars
konnte ich einsparen, dafür kommt nun ein weiterer SendeBuffer mit aktuell 32 byte Ram hinzu.
Ich hoffe, die geänderte lib findet Anklang, vielleicht baut man sie sich ja dauerhaft ein.
Wäre schön wenn dazu Feedback kommt.
Dazu die alten Dateien RP6uart.c und RP6uart.h umbenennen -> z.b. so _RP6...
Neue Dateien in den Verzeichnissen anlegen und den Code unten rein schieben.
Oder einfach die angehängten Dateien nutzen.
Die RP6uart.c
Code:
/* ****************************************************************************
* File: RP6uart.c
*
* ISR(USART_RXC_vect) geändert. erkennt Frame errors
* Umbau auf serial_getc, erkennt nun 0x00 chars
* ISR zum senden
*/
/*****************************************************************************/
// Includes:
#include "RP6uart.h"
/*****************************************************************************/
// new UART transmit functions:
// Data for Ringbuffer
static volatile t_txbuffer tx_buff;
/**
* UART send ISR.
* Handles transmission from circular buffer.
*/
ISR(USART_UDRE_vect, ISR_BLOCK) {
if (tx_buff.head != tx_buff.tail) {
UDR = tx_buff.ring[tx_buff.tail];
tx_buff.tail = (tx_buff.tail + 1) % UART_SEND_BUFFER_SIZE;
}
else
UCSRB &= ~(1 << UDRIE);
}
/**
* send function, nonblocking if free space in buffer
*/
uint8_t serial_sendc(unsigned char data) {
uint8_t next = ((tx_buff.head + 1) % UART_SEND_BUFFER_SIZE);
while (next == tx_buff.tail);
tx_buff.ring[tx_buff.head] = data;
tx_buff.head = next;
UCSRB |= (1 << UDRIE);
return 1;
}
/**
* compatibility implementation
* old writeChar function, use serial_sendc instead
*/
void writeChar(char ch)
{
serial_sendc(ch);
}
/**
* Writes a null terminated string or buffer from SRAM to UART.
* Uses serial_sendc
* Example:
* writeString("RP6 Robot System\n");
*/
void writeString(char *string)
{
while(*string)
serial_sendc(*string++);
}
/**
* Writes a null terminated string from flash program memory to UART.
* Uses serial_sendc
* Example:
* writeNStringP(PSTR("RP6 Robot System\n"));
* // There is also a Macro that makes life easier and
* // you can simply write:
* writeString_P("RP6 Robot System\n");
*/
void writeNStringP(const char *pstring)
{
unsigned char c;
for (;(c = pgm_read_byte_near(pstring++));serial_sendc(c));
}
/**
* Writes a string with specified length and offset from SRAM to UART.
* Uses serial_sendc
* Example:
* writeStringLength("RP6 Robot Sytem\n",16,0);
* // would output: "RP6 Robot Sytem\n"
* writeStringLength("RP6 Robot Sytem\n",11,4);
* // would output: "Robot System"
* writeStringLength("RP6 Robot Sytem\n",40,4);
* // would output: "Robot System\n"
* // No matter if the specified length is 40 characters!
*/
void writeStringLength(char *string, uint8_t length, uint8_t offset)
{
for(string = &string[offset]; *string && length; length--)
serial_sendc(*string++);
}
/**
* Write a number (with specified base) to the UART.
* Example:
* // Write a hexadecimal number to the UART:
* writeInteger(0xAACC,16);
* // Instead of 16 you can also write "HEX" as this is defined in the
* // RP6RobotBaseLib.h :
* writeInteger(0xAACC, HEX);
* // Other Formats:
* writeInteger(1024,DEC); // Decimal
* writeInteger(044,OCT); // Ocal
* writeInteger(0b11010111,BIN); // Binary
*/
void writeInteger(int16_t number, uint8_t base)
{
char buffer[17];
itoa(number, &buffer[0], base);
writeString(&buffer[0]);
}
/**
* Same as writeInteger, but with defined length.
* Example:
* // Write a hexadecimal number to the UART:
* writeIntegerLength(0xAACC, 16, 8);
* // Instead of 16 you can also write "HEX" as this is defined in the
* // RP6RobotBaseLib.h :
* writeIntegerLength(0xAACC, HEX, 8);
* // Other Formats:
* writeIntegerLength(1024,DEC,6); // Decimal
* writeIntegerLength(044,OCT,4); // Ocal
* writeIntegerLength(0b11010111,BIN,8); // Binary
*/
void writeIntegerLength(int16_t number, uint8_t base, uint8_t length)
{
char buffer[17];
itoa(number, &buffer[0], base);
int8_t cnt = length - strlen(buffer);
if(cnt > 0) {
for(; cnt > 0; cnt--, writeChar('0'));
writeString(&buffer[0]);
}
else
writeStringLength(&buffer[0],length,-cnt);
}
/*****************************************************************************/
// new UART receive functions:
// Data for Ringbuffer
static volatile t_rxbuffer rx_buff;
/**
* UART receive ISR.
* Handles reception to circular buffer, handles errors.
*/
ISR(USART_RXC_vect, ISR_BLOCK) {
if ( ! (UCSRA & ((1<<FE)|(1<<DOR)|(1<<PE)))) {
volatile uint8_t data = UDR;
volatile uint8_t next = ((rx_buff.head + 1) % UART_RECEIVE_BUFFER_SIZE);
if (next != rx_buff.tail) {
rx_buff.ring[rx_buff.head] = data;
rx_buff.head = next;
} else
rx_buff.uart_error=rx_buff.uart_error + 0x80;
}
else {
volatile uint8_t data __attribute__((unused)) = UDR;
rx_buff.uart_error++;
}
}
/**
* Read a char from the circular buffer to a pointer.
* Reset error counter on frame errors, do buffer reset on overflow
* returns 1 if a valid char is received, 0 if not
* Example:
*
* // [...]
* if(getBufferLength())
if (serial_getc(&receivedData[data_position++]))
found_a_char();
* // [...]
*
*/
uint8_t serial_getc(unsigned char *data) {
if (rx_buff.uart_error < 0x80){
if (rx_buff.head == rx_buff.tail)
return 0;
*data = rx_buff.ring[rx_buff.tail];
rx_buff.tail = (rx_buff.tail + 1) % UART_RECEIVE_BUFFER_SIZE;
rx_buff.uart_error = 0;
return 1;
} else {
clearReceptionBuffer();
return 0;
}
}
/**
* compatibility implementation
* old readChar function, use serial_getc instead
* -> there was no way to check if a received char was NULL or the buffer was even empty
* Example:
* // [...]
* if(getBufferLength())
* receivedData[data_position++] = readChar();
* // [...]
*
*/
char readChar(void)
{
unsigned char data = 0;
serial_getc(&data);
return data;
}
/**
* this function copies numberOfChars chars to buf.
* It also returns the number of characters really copied to the buffer!
* Just in case that there were fewer chars in the buffer...
*/
uint8_t readChars(unsigned char *buf, uint8_t numberOfChars)
{
uint8_t i = 0;
while(serial_getc(&buf[i]) && (i != numberOfChars))
i++;
return i;
}
/**
* Returns the current number of elements in the buffer.
* Example:
* s. readChar function above!
*/
uint8_t getBufferLength(void)
{
return ((rx_buff.head + UART_RECEIVE_BUFFER_SIZE - rx_buff.tail) % UART_RECEIVE_BUFFER_SIZE);
}
/**
* Clears the reception buffer - it disables UART Receive interrupt for a short period of time.
* if we execute this, something is going really wrong with the buffer (overflow)
*/
void clearReceptionBuffer(void)
{
UCSRB &= ~(1 << RXCIE);
rx_buff.head = 0;
rx_buff.tail = 0;
rx_buff.uart_error = 0;
UCSRB |= (1 << RXCIE);
// alert the programmer urgently, we are in heavy trouble
}
// EOF
und die passende RP6uart.h
Code:
/* ****************************************************************************
* File: RP6uart.h
* Version: new 1.00
* Target: RP6 Base & Processor Expansion - ATMEGA32 @8.00 or 16.00MHz
* ****************************************************************************
*/
#ifndef RP6UART_H
#define RP6UART_H
/*****************************************************************************/
// Includes:
#include <avr/pgmspace.h> // Program memory (=Flash ROM) access routines.
#include <stdlib.h> // C standard functions (e.g. itoa...)
#include <string.h>
#include <avr/io.h> // I/O Port definitions
#include <avr/interrupt.h> // Interrupt macros (e.g. cli(), sei())
/*****************************************************************************/
// UART
// TX:
uint8_t serial_sendc(unsigned char);
void writeChar(char);
void writeStringLength(char *, uint8_t, uint8_t );
void writeString(char *);
void writeNStringP(const char *);
#define writeString_P(__pstr) writeNStringP((PSTR(__pstr)))
#define UART_SEND_BUFFER_SIZE 32 // Default buffer size is 32!
typedef struct {
volatile uint8_t ring[UART_SEND_BUFFER_SIZE];
volatile uint8_t head;
volatile uint8_t tail;
} t_txbuffer;
#define HEX 16
#define DEC 10
#define OCT 8
#define BIN 2
void writeInteger(int16_t, uint8_t);
void writeIntegerLength(int16_t, uint8_t, uint8_t);
// RX:
#define UART_RECEIVE_BUFFER_SIZE 32 // Default buffer size is 32!
// t_buffer.uart_error shoud be always 0, if it contains Values from 1-127,
// we found Hardware Problems like Baud mismatch and its reseting by serial_getchar.
// if it contains values over 127, we found buffer overruns.
typedef struct {
volatile uint8_t ring[UART_RECEIVE_BUFFER_SIZE];
volatile uint8_t head;
volatile uint8_t tail;
volatile uint8_t uart_error;
} t_rxbuffer;
uint8_t serial_getc(unsigned char *);
char readChar(void);
uint8_t readChars(unsigned char *, uint8_t);
uint8_t getBufferLength(void);
void clearReceptionBuffer(void);
#endif
// EOF
Gruß