added controller firmware

This commit is contained in:
Steffen Vogel 2012-11-15 22:04:14 +01:00
parent 6a2edcde49
commit 99eda35745
13 changed files with 2280 additions and 0 deletions

80
controller/Makefile Normal file
View file

@ -0,0 +1,80 @@
###############################################################################
# Makefile für das Viertsemester-Projekt am ACS
###############################################################################
## General Flags
TARGET = auto
MCU = atmega32
CC = avr-gcc
## Options common to compile, link and assembly rules
COMMON = -mmcu=$(MCU)
## Compile options common for all C compilation units.
CFLAGS = $(COMMON)
CFLAGS += -Wall -g -std=gnu99 -DF_CPU=16000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
CFLAGS += -MD -MP -MT $(*F).o -MF dep/$(@F).d
## Assembly specific flags
ASMFLAGS = $(COMMON)
ASMFLAGS += $(CFLAGS)
## Linker flags
LDFLAGS = $(COMMON)
LDFLAGS += -Wl,-Map=$(TARGET).map
## Intel Hex file production flags
HEX_FLASH_FLAGS = -R .eeprom -R .fuse -R .lock -R .signature
HEX_EEPROM_FLAGS = -j .eeprom
HEX_EEPROM_FLAGS += --set-section-flags=.eeprom="alloc,load"
HEX_EEPROM_FLAGS += --change-section-lma .eeprom=0 --no-change-warnings
## Include Directories
INCLUDES =
## Objects that must be built in order to link
OBJECTS = rotary.o lcd.o main.o pid.o adc.o uart.o
## Objects explicitly added by the user
LINKONLYOBJECTS =
## Build
all: $(TARGET).elf $(TARGET).hex $(TARGET).eep $(TARGET).lss size
## Link
$(TARGET).elf: $(OBJECTS)
$(CC) $(LDFLAGS) $(OBJECTS) $(LINKONLYOBJECTS) $(LIBDIRS) $(LIBS) -o $(TARGET).elf
%.hex: $(TARGET).elf
avr-objcopy -O ihex $(HEX_FLASH_FLAGS) $< $@
%.eep: $(TARGET).elf
avr-objcopy $(HEX_EEPROM_FLAGS) -O ihex $< $@ || exit 0
%.lss: $(TARGET).elf
avr-objdump -h -S $< > $@
size: $(TARGET).elf
@echo
@avr-size -C --mcu=${MCU} ${TARGET}.elf
flash: $(TARGET).hex
avrdude -p m32 -c avrisp2 -P usb \
-U flash:w:$(TARGET).hex
eeprom: $(TARGET).hex
avrdude -p m32 -c avrisp2 -P usb \
-U eeprom:w:$(TARGET).eep
## Clean target
.PHONY: clean
clean:
rm -rf $(OBJECTS) dep/*
for suf in elf hex eep lss map ; do \
rm -f $(TARGET).$$suf ; \
done
## Other dependencies
-include $(shell mkdir dep 2>/dev/null) $(wildcard dep/*)

57
controller/adc.c Normal file
View file

@ -0,0 +1,57 @@
/**
* Analog to Digital converter routines
*
* @copyright 2012 Institute Automation of Complex Power Systems (ACS), RWTH Aachen University
* @license http://www.gnu.org/licenses/gpl.txt GNU Public License
* @author Steffen Vogel <info@steffenvogel.de>
*/
#include <avr/io.h>
#include "adc.h"
/**
* Initialisierung Analog to digital Converter (ADC)
*/
void adc_init() {
uint16_t result;
ADC_DDR = 0x00; // Pins als Eingänge
ADC_PORT = 0x00; // Pullups deaktivieren
ADMUX = (1<<REFS1) | (1<<REFS0); // interne 2.56V als Referenz benutzen
ADCSRA = (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); // Frequenzvorteiler
ADCSRA |= (1<<ADEN); // ADC aktivieren
ADCSRA |= (1<<ADIE); // ADC Interrupts erlauben
ADCSRA |= (1<<ADSC); // starte eine ADC-Wandlung
while (ADCSRA & (1<<ADSC) ) { } // auf Abschluss der Konvertierung warten
result = ADC;
}
/**
* Auslesen des Kanals
*/
uint16_t adc_read(uint8_t channel) {
// Kanal waehlen, ohne andere Bits zu beeinflußen
ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
ADCSRA |= (1<<ADSC); // eine Wandlung "single conversion"
while (ADCSRA & (1<<ADSC) ) { } // auf Abschluss der Konvertierung warten
return ADC; // ADC auslesen und zurückgeben
}
/**
* Auslesen des Kanals mit Mittelwertrückgabe
*/
uint16_t adc_read_avg( uint8_t channel, uint8_t average) {
uint32_t result = 0;
for (uint8_t i = 0; i < average; ++i ) {
result += adc_read(channel);
}
return (uint16_t) (result / average);
}

25
controller/adc.h Normal file
View file

@ -0,0 +1,25 @@
/**
* Analog to Digital converter headers
*
* @copyright 2012 Institute Automation of Complex Power Systems (ACS), RWTH Aachen University
* @license http://www.gnu.org/licenses/gpl.txt GNU Public License
* @author Steffen Vogel <info@steffenvogel.de>
*/
#ifndef _ADC_H_
#define _ADC_H_
// Konfiguration
#define ADC_PORT PORTA
#define ADC_DDR DDRA
#define ADC_PIN_STERING_LEFT 0
#define ADC_PIN_STERING_RIGHT 1
#define ADC_PIN_BATT_LOGIC 2
#define ADC_PIN_BATT_DRIVE 3
#define ADMUX_MASK 0x1F
void adc_init();
uint16_t adc_read(uint8_t channel);
uint16_t adc_read_avg(uint8_t channel, uint8_t average);
#endif /* _ADC_H_ */

BIN
controller/init.o Normal file

Binary file not shown.

236
controller/lcd.c Normal file
View file

@ -0,0 +1,236 @@
/**
* Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus
*
* Die Pinbelegung ist über defines in lcd.h einstellbar
*
* @copyright 2012 Institute Automation of Complex Power Systems (ACS), RWTH Aachen University
* @license http://www.gnu.org/licenses/gpl.txt GNU Public License
* @link http://www.mikrocontroller.net/articles/HD44780
* @link http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung
*/
#include <stdlib.h>
#include <string.h>
#include <avr/eeprom.h>
#include "lcd.h"
/**
* Erzeugt einen Enable-Puls
*/
static void lcd_enable() {
LCD_PORT |= (1<<LCD_EN); // Enable auf 1 setzen
_delay_us( LCD_ENABLE_US ); // kurze Pause
LCD_PORT &= ~(1<<LCD_EN); // Enable auf 0 setzen
}
/**
* Sendet eine 4-bit Ausgabeoperation an das LCD
*/
static void lcd_out(uint8_t data) {
data &= 0xF0; // obere 4 Bit maskieren
LCD_PORT &= ~(0xF0>>(4-LCD_DB)); // Maske löschen
LCD_PORT |= (data>>(4-LCD_DB)); // Bits setzen
lcd_enable();
}
/**
* Initialisierung: muss ganz am Anfang des Programms aufgerufen werden
*/
void lcd_init() {
// verwendete Pins auf Ausgang schalten
uint8_t pins = (0x0F << LCD_DB) | // 4 Datenleitungen
(1<<LCD_RS) | // R/S Leitung
(1<<LCD_EN); // Enable Leitung
LCD_DDR |= pins;
// initial alle Ausgänge auf Null
LCD_PORT &= ~pins;
// warten auf die Bereitschaft des LCD
_delay_ms( LCD_BOOTUP_MS );
// Soft-Reset muss 3mal hintereinander gesendet werden zur Initialisierung
lcd_out( LCD_SOFT_RESET );
_delay_ms( LCD_SOFT_RESET_MS1 );
lcd_enable();
_delay_ms( LCD_SOFT_RESET_MS2 );
lcd_enable();
_delay_ms( LCD_SOFT_RESET_MS3 );
// 4-bit Modus aktivieren
lcd_out( LCD_SET_FUNCTION |
LCD_FUNCTION_4BIT );
_delay_ms( LCD_SET_4BITMODE_MS );
// 4-bit Modus / 2 Zeilen / 5x7
lcd_command( LCD_SET_FUNCTION |
LCD_FUNCTION_4BIT |
LCD_FUNCTION_2LINE |
LCD_FUNCTION_5X7 );
// Display ein / Cursor aus / Blinken aus
lcd_command( LCD_SET_DISPLAY |
LCD_DISPLAY_ON |
LCD_CURSOR_OFF |
LCD_BLINKING_OFF);
// Cursor inkrement / kein Scrollen
lcd_command( LCD_SET_ENTRY |
LCD_ENTRY_INCREASE |
LCD_ENTRY_NOSHIFT );
lcd_clear();
}
/**
* Sendet ein Datenbyte an das LCD
*/
void lcd_data(uint8_t data) {
LCD_PORT |= (1<<LCD_RS); // RS auf 1 setzen
lcd_out(data); // zuerst die oberen,
lcd_out(data<<4); // dann die unteren 4 Bit senden
_delay_us( LCD_WRITEDATA_US );
}
/**
* Sendet einen Befehl an das LCD
*/
void lcd_command(uint8_t data) {
LCD_PORT &= ~(1<<LCD_RS); // RS auf 0 setzen
lcd_out(data); // zuerst die oberen,
lcd_out(data<<4); // dann die unteren 4 Bit senden
_delay_us(LCD_COMMAND_US);
}
/**
* Sendet den Befehl zur Löschung des Displays
*/
void lcd_clear() {
lcd_command(LCD_CLEAR_DISPLAY);
_delay_ms(LCD_CLEAR_DISPLAY_MS);
}
/**
* Sendet den Befehl: Cursor Home
*/
void lcd_home() {
lcd_command(LCD_CURSOR_HOME);
_delay_ms(LCD_CURSOR_HOME_MS);
}
/**
* Setzt den Cursor in Spalte x (0..15) Zeile y (1..4)
*/
void lcd_setcursor(uint8_t x, uint8_t y) {
uint8_t data;
switch (y) {
case 0: // 1. Zeile
data = LCD_SET_DDADR + LCD_DDADR_LINE1 + x;
break;
case 1: // 2. Zeile
data = LCD_SET_DDADR + LCD_DDADR_LINE2 + x;
break;
case 2: // 3. Zeile
data = LCD_SET_DDADR + LCD_DDADR_LINE3 + x;
break;
case 3: // 4. Zeile
data = LCD_SET_DDADR + LCD_DDADR_LINE4 + x;
break;
default:
return; // für den Fall einer falschen Zeile
}
lcd_command(data);
}
/**
* Schreibt einen String auf das LCD
*/
void lcd_string(const char *data) {
while (*data != '\0') {
lcd_data(*data++);
}
}
/**
* Schreibt ein Zeichen in den Character Generator RAM
*/
void lcd_generatechar(uint8_t code, const uint8_t *data) {
// Startposition des Zeichens einstellen
lcd_command(LCD_SET_CGADR | (code<<3));
// Bitmuster übertragen
for (uint8_t i=0; i<8; i++) {
lcd_data(data[i]);
}
}
/**
* Ausgabe Balkenanzeige
*/
void lcd_bar(uint8_t startx, uint8_t starty, uint8_t length, uint8_t percent) {
uint8_t i;
if (percent > 100) return;
lcd_setcursor(startx, starty);
lcd_data(0b00111100); // <
for (i=0; i < length-2; i++) {
lcd_setcursor( startx+1+i , starty );
if (percent*(length-2)/100 <= i ) {
lcd_data('-');
}
else {
lcd_data(0b11111111);
}
}
lcd_setcursor(startx+length-1, starty );
lcd_data(0b00111110); // >
}
/**
* Ausgabe einer Ganzzahl mit führenden Leerzeichen
*/
void lcd_int(int16_t number, uint8_t length) {
char buffer[length+1];
bool vz = true;
bool zf = true;
bool neg = (number < 0);
buffer[length] = '\0';
for (int8_t counter = length-1; counter >= 0; counter--) {
if (zf) {
buffer[counter] = '0' + (abs(number) % 10);
number /= 10;
zf = (bool) number;
}
else if (vz && neg) {
buffer[counter] = '-';
vz = false;
}
else {
buffer[counter] = ' ';
}
}
lcd_string(buffer);
}

154
controller/lcd.h Normal file
View file

@ -0,0 +1,154 @@
/**
* Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus
*
* Die Pinbelegung ist über #defines in lcd.h konfigurierbar
*
* @copyright 2012 Institute Automation of Complex Power Systems (ACS), RWTH Aachen University
* @license http://www.gnu.org/licenses/gpl.txt GNU Public License
* @link http://www.mikrocontroller.net/articles/HD44780
* @link http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung
*/
#ifndef _LCD_H_
#define _LCD_H_
#include <stdbool.h>
#include <stdint.h>
#include <avr/io.h>
#include <util/delay.h>
#include "rotary.h"
// Pinbelegung für das LCD, an verwendete Pins anpassen
// Alle LCD Pins müssen an einem Port angeschlossen sein und die 4
// Datenleitungen müssen auf aufeinanderfolgenden Pins liegen
// LCD DB4-DB7 <--> PORTD Bit PD0-PD3
#define LCD_PORT PORTC
#define LCD_DDR DDRC
#define LCD_DB PC4
// LCD RS <--> PORTD Bit PD4 (RS: 0=Data, 1=Command)
#define LCD_RS PC3
// LCD EN <--> PORTD Bit PD5 (EN: 1-Impuls für Daten)
#define LCD_EN PC2
// LCD Ausführungszeiten (MS=Millisekunden, US=Mikrosekunden)
#define LCD_BOOTUP_MS 15
#define LCD_ENABLE_US 1
#define LCD_WRITEDATA_US 46
#define LCD_COMMAND_US 42
#define LCD_SOFT_RESET_MS1 5
#define LCD_SOFT_RESET_MS2 1
#define LCD_SOFT_RESET_MS3 1
#define LCD_SET_4BITMODE_MS 5
#define LCD_CLEAR_DISPLAY_MS 2
#define LCD_CURSOR_HOME_MS 2
// Zeilendefinitionen des verwendeten LCD
// Die Einträge hier sollten für ein LCD mit einer Zeilenlänge von 16 Zeichen passen
// Bei anderen Zeilenlängen müssen diese Einträge angepasst werden
#define LCD_DDADR_LINE1 0x00
#define LCD_DDADR_LINE2 0x40
#define LCD_DDADR_LINE3 0x10
#define LCD_DDADR_LINE4 0x50
// Initialisierung: muss ganz am Anfang des Programms aufgerufen werden.
void lcd_init( void );
// LCD löschen
void lcd_clear( void );
// Cursor in die 1. Zeile, 0-te Spalte
void lcd_home( void );
// Cursor an eine beliebige Position
void lcd_setcursor( uint8_t spalte, uint8_t zeile );
// Ausgabe eines einzelnen Zeichens an der aktuellen Cursorposition
void lcd_data( uint8_t data );
// Ausgabe eines Strings an der aktuellen Cursorposition
void lcd_string( const char *data );
// Definition eines benutzerdefinierten Sonderzeichens.
// data muss auf ein Array[5] mit den Spaltencodes des zu definierenden Zeichens
// zeigen
void lcd_generatechar( uint8_t code, const uint8_t *data );
// Ausgabe eines Kommandos an das LCD.
void lcd_command( uint8_t data );
// Ausgabe Balkenanzeige
void lcd_bar(uint8_t startx, uint8_t starty, uint8_t length, uint8_t percent);
// Ausgabe einer ganzen Zahl
void lcd_int(int16_t number, uint8_t length);
// LCD Befehle und Argumente.
// Zur Verwendung in lcd_command
// Clear Display -------------- 0b00000001
#define LCD_CLEAR_DISPLAY 0x01
// Cursor Home ---------------- 0b0000001x
#define LCD_CURSOR_HOME 0x02
// Set Entry Mode ------------- 0b000001xx
#define LCD_SET_ENTRY 0x04
#define LCD_ENTRY_DECREASE 0x00
#define LCD_ENTRY_INCREASE 0x02
#define LCD_ENTRY_NOSHIFT 0x00
#define LCD_ENTRY_SHIFT 0x01
// Set Display ---------------- 0b00001xxx
#define LCD_SET_DISPLAY 0x08
#define LCD_DISPLAY_OFF 0x00
#define LCD_DISPLAY_ON 0x04
#define LCD_CURSOR_OFF 0x00
#define LCD_CURSOR_ON 0x02
#define LCD_BLINKING_OFF 0x00
#define LCD_BLINKING_ON 0x01
// Set Shift ------------------ 0b0001xxxx
#define LCD_SET_SHIFT 0x10
#define LCD_CURSOR_MOVE 0x00
#define LCD_DISPLAY_SHIFT 0x08
#define LCD_SHIFT_LEFT 0x00
#define LCD_SHIFT_RIGHT 0x04
// Set Function --------------- 0b001xxxxx
#define LCD_SET_FUNCTION 0x20
#define LCD_FUNCTION_4BIT 0x00
#define LCD_FUNCTION_8BIT 0x10
#define LCD_FUNCTION_1LINE 0x00
#define LCD_FUNCTION_2LINE 0x08
#define LCD_FUNCTION_5X7 0x00
#define LCD_FUNCTION_5X10 0x04
#define LCD_SOFT_RESET 0x30
// Set CG RAM Address --------- 0b01xxxxxx (Character Generator RAM)
#define LCD_SET_CGADR 0x40
#define LCD_GC_CHAR0 0
#define LCD_GC_CHAR1 1
#define LCD_GC_CHAR2 2
#define LCD_GC_CHAR3 3
#define LCD_GC_CHAR4 4
#define LCD_GC_CHAR5 5
#define LCD_GC_CHAR6 6
#define LCD_GC_CHAR7 7
// Set DD RAM Address --------- 0b1xxxxxxx (Display Data RAM)
#define LCD_SET_DDADR 0x80
#endif /* _LCD_H_ */

548
controller/main.c Normal file
View file

@ -0,0 +1,548 @@
/**
* Main loop
*
* @copyright 2012 Institute Automation of Complex Power Systems (ACS), RWTH Aachen University
* @license http://www.gnu.org/licenses/gpl.txt GNU Public License
* @author Steffen Vogel <info@steffenvogel.de>
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include <avr/pgmspace.h>
#include <avr/wdt.h>
#include "lcd.h"
#include "rotary.h"
#include "adc.h"
#include "pid.h"
#include "uart.h"
#define DISPLAY_MODI 11
#define BAUDRATE 57600
#define MAGIC_BYTE 0xca
#define MAGIC_LEN 16
const char *mode_str[] = {
"Stop ",
"Auto ",
"Manual",
"UART "
};
const char *display_str[] = {
NULL,
"PWM Motor",
"PWM Servo",
"PID Drive: P",
"PID Drive: I",
"PID Stering: P",
"PID Stering: I",
"ADC Inductor: L",
"ADC Inductor: R",
"ADC Batt: Logic",
"ADC Batt: Drive"
};
enum channel {
STERING_LEFT,
STERING_RIGHT,
BATT_LOGIC,
BATT_DRIVE
};
enum state {
HALT,
AUTO,
MANUAL
};
enum menu {
OVERVIEW,
PWM_DRIVE,
PWM_STERING,
PID_DRIVE_P,
PID_DRIVE_I,
PID_STERING_P,
PID_STERING_I,
ADC_STERING_LEFT,
ADC_STERING_RIGHT,
ADC_BATT_LOGIC,
ADC_BATT_DRIVE,
};
enum cmd {
SET_STATUS, /* Mode, Display */
SET_PWM, /* Drive & Stering */
SET_PID, /* Controller state */
GET_PID /* Controller state */
};
// EEPROM
int16_t pid_drive_p_ee EEMEM = 0;
int16_t pid_drive_i_ee EEMEM = 0;
int16_t pid_stering_p_ee EEMEM = 39;
int16_t pid_stering_i_ee EEMEM = 0;
int16_t pwm_drive_ee EEMEM = 0;
int16_t pwm_stering_ee EEMEM = 0;
// Globale Daten
int16_t pwm_stering;
int16_t pwm_drive;
int8_t out_stering;
uint8_t out_drive;
int16_t adc_stering_left;
int16_t adc_stering_right;
int16_t adc_batt_logic;
int16_t adc_batt_drive;
uint8_t speed;
uint16_t speed_cnt;
bool speed_ovf = true;
struct pid pid_drive;
struct pid pid_stering;
enum state mode;
enum menu display;
bool edit = false;
bool redraw = true;
/**
* Parameter editieren
*/
void parameter_edit(int16_t *parameter, int16_t *parameter_ee, enum taster input) {
switch (input) {
case DGB_SW: // Editier-Modus togglen
edit = !edit;
break;
case DGB_CW: // Wert ändern
if (edit) *parameter = *parameter + 1;
break;
case DGB_CCW: // Wert ändern
if (edit) *parameter = *parameter - 1;
break;
case SW_GRUEN: // Laden aus EEPROM
*parameter = eeprom_read_word((uint16_t *) parameter_ee);
break;
case SW_BLAU: // Speichern im EEPROM
eeprom_write_word((uint16_t *) parameter_ee, *parameter);
break;
}
}
/**
* Startsequenz
*/
void greeter() {
// UART
uart_puts("Donaudampfschiff\r\n");
// LCD
lcd_clear();
lcd_string("Donaudampfschiff");
lcd_setcursor(0, 1);
lcd_string(" 4.Sem.Proj. ");
_delay_ms(1500);
lcd_clear();
}
/**
* Initialize Timers and IO Ports
*/
void init() {
DDRB = 0xff;
DDRC = 0xff;
DDRD = 0b11111011;
// Geschwindigkeitssensor
// Falling Edge on INT0 triggers Interrupt
MCUCR = (1<<ISC01) | (0<<ISC00);
GICR = (1<<INT0);
// Timer Counter 0 init - 8 bit
// f = 7.812 kHz
// Verwendung fuer Auswertung der Taster und analog Signal Erzeugung
TCCR0 = (0<<FOC0) | (1<<WGM00) | (0<<COM01) | (0<<COM00) | (1<<WGM01) | (0<<CS02) | (1<<CS01) | (0<<CS00);
TIMSK |= (1<<TOIE0); // Timer 0 OVF Interrupt einschalten
// Timer Counter 1 - Servo - 16 bit Counter - UINT16_MAX = 65535
// Frequenz 50 Hz
// Timer 1: f_PWM=f_Clk/(Prescaler1*(1+ICR1))
TCCR1A = (1<<COM1A1) | (0<<COM1A0) | (1<<COM1B1) | (0<<COM1B0) | (1<<WGM11) | (0<<WGM10);
TCCR1B = (1<<WGM13) | (1<<WGM12) | (0<<CS12) | (1<<CS11) | (0<<CS10); // Prescaler1 = 8
TIMSK |= (1<<TOIE1);
ICR1 = 40000; // Für welchen Wert im Register ICR1 ergibt sich eine Grundfrequenz von 50 Hz
OCR1A = 0; // Register zum Einstellen des Servo Tastgrades
// Timer Counter 2 init - 8 bit
// Verwendung fuer Motor-PWM
// Timer 2: f = f_Clk / (256 * Prescaler2)
// f2 = 976 Hz
TCCR2 = (0<<FOC2) | (1<<WGM20) | (1<<COM21) | (0<<COM20) | (1<<WGM21) | (1<<CS22) | (0<<CS21) | (0<<CS20); // Prescaler2 = 64
OCR2 = 0; // Register zum Einstellen des Motor Tastgrades
TIMSK |= (1<<TOIE2); // Timer 2 OVF Interrupt für Eingabe Polling einschalten
}
/**
* Interupt Subroutine für Eingabe Polling
*/
ISR(TIMER0_OVF_vect) {
enum taster input = dgb_read(); /* Drehgeber wird periodisch ausgelesen */
if (edit == false && input) { /* Menu wechseln */
if (input == DGB_CW && display < DISPLAY_MODI-1) {
display++;
}
else if (input == DGB_CCW && display != OVERVIEW) {
display--;
}
redraw = true;
}
switch (display) {
case OVERVIEW:
switch (input) {
case SW_BLAU:
mode = HALT;
break;
case SW_GRUEN:
switch (mode) {
case HALT:
mode = MANUAL;
break;
case MANUAL:
mode = AUTO;
break;
case AUTO:
mode = MANUAL;
break;
default:
mode = HALT;
}
break;
case DGB_CW:
case DGB_CCW:
case DGB_SW:
;
}
break;
case PWM_DRIVE:
parameter_edit(&pwm_drive, &pwm_drive_ee, input);
break;
case PWM_STERING:
parameter_edit(&pwm_stering, &pwm_stering_ee, input);
break;
case PID_STERING_P:
parameter_edit(&pid_stering.pFactor, &pid_stering_p_ee, input);
break;
case PID_STERING_I:
parameter_edit(&pid_stering.iFactor, &pid_stering_i_ee, input);
break;
case PID_DRIVE_P:
parameter_edit(&pid_drive.pFactor, &pid_drive_p_ee, input);
break;
case PID_DRIVE_I:
parameter_edit(&pid_drive.iFactor, &pid_drive_i_ee, input);
break;
case ADC_STERING_LEFT:
case ADC_STERING_RIGHT:
case ADC_BATT_LOGIC:
case ADC_BATT_DRIVE:
/* ADC Werte können nicht geändert werden */;
}
}
/**
* Interupt Subroutine für UART Kommunikation
*/
ISR(TIMER1_OVF_vect) {
static uint8_t cnt, cnt2, magic, dat = 1;
char buffer[32];
/* Send Data */
/*if (cnt++ == 25) {
switch (cnt2++ & 0x01) {
case 0:
sprintf(buffer,
"spd=%i, adc_r=%i, adc_l=%i, ",
speed,
adc_stering_right,
adc_stering_left
);
break;
case 1:
sprintf(buffer,
"stering=%i, drive=%i\r\n",
out_stering,
out_drive
);
break;
}
uart_puts(buffer);
cnt = 0;
}*/
/*uint16_t byte = uart_getc();
if (byte != UART_NO_DATA) {
if (byte & 0x01) {
pwm_stering = *((int8_t *) &byte);
}
else {
pwm_drive = (uint8_t) byte;
}
}*/
}
/**
* Interupt Subroutine für Regelung
*/
ISR(TIMER2_OVF_vect) {
speed_cnt++;
speed_ovf |= (speed_cnt == INT16_MAX);
/**
* Berechnung der neuen Sollwerte
*/
int16_t diff = adc_stering_right - adc_stering_left;
uint16_t sum = adc_stering_right + adc_stering_left;
/**
* Schreiben der Compare-Register
* Motor Tastgrad
*/
switch (mode) {
case MANUAL:
out_drive = pwm_drive;
out_stering = pwm_stering;
break;
case AUTO:
if (sum < 30) { /* Von Strecke abgekommen */
mode = HALT;
display = OVERVIEW;
edit = false;
}
else {
out_stering = pid_controller(0 , diff , &pid_stering);
// output_drive = pid_controller(pwm_drive, set_drive, &pid_drive); // funktioniert noch nicht
// output_drive = pid_controller(pwm_drive, speed, &pid_drive); // funktioniert noch nicht
out_drive = (pwm_drive>>1) + (( pwm_drive * (128 - abs(out_stering))) >> 8);
}
break;
case HALT:
default:
out_drive = 0;
out_stering = 0;
}
/**
* Servo Tastgrad
* Links 2.1ms => 4200
* Mitte 1.5ms => 3000
* Rechts 0.9ms => 1800
*/
OCR1A = 3000 + (out_stering * 9);
OCR2 = out_drive;
}
/**
* Interupt Subroutine für ADC
*/
ISR(ADC_vect) {
enum channel ch = ADMUX & ADMUX_MASK;
switch (ch) {
case STERING_LEFT:
adc_stering_left = ADC;
break;
case STERING_RIGHT:
adc_stering_right = ADC;
break;
case BATT_LOGIC:
adc_batt_logic = ADC;
break;
case BATT_DRIVE:
adc_batt_drive = ADC;
break;
}
ADMUX = (ADMUX & ~ADMUX_MASK);
ADMUX |= (ch >= 1) ? 0 : ++ch;
ADCSRA |= (1<<ADSC); // starte nächste Messung
}
/**
* Interrupt Subroutine für Geschwindigkeitssensor
*/
ISR(INT0_vect) {
if (speed_ovf) {
speed = 0;
speed_ovf = false;
}
else {
speed = 2e4 / speed_cnt;
}
speed_cnt = 0;
}
int main() {
// Gespeicherte Werte einlesen
pwm_drive = eeprom_read_word((uint16_t *) &pwm_drive_ee);
pwm_stering = eeprom_read_word((uint16_t *) &pwm_stering_ee);
// Initialisierung
init();
adc_init();
dgb_init();
lcd_init();
uart_init(UART_BAUD_SELECT(BAUDRATE, F_CPU));
// PID mit EEPROM-Werten initialisieren
pid_init(
eeprom_read_word((uint16_t *) &pid_drive_p_ee),
eeprom_read_word((uint16_t *) &pid_drive_i_ee),
0, &pid_drive);
pid_init(
eeprom_read_word((uint16_t *) &pid_stering_p_ee),
eeprom_read_word((uint16_t *) &pid_stering_i_ee),
0, &pid_stering);
// Interrupts aktivieren
sei();
// Startsqeuenz
greeter();
// Watchdog Timer aktivieren
wdt_enable(WDTO_1S);
// Menü
while (1) {
wdt_reset();
lcd_home(); /* set cursor to top-leftmost postion */
if (redraw) {
lcd_clear();
if (display_str[display]) {
lcd_string(display_str[display]);
}
redraw = false;
}
switch (display) {
case OVERVIEW:
lcd_string(mode_str[mode]);
lcd_string(" S:");
lcd_int(out_stering, 6);
lcd_setcursor(0, 1);
lcd_string("V:");
lcd_int(speed, 5);
lcd_string(" D:");
lcd_int(out_drive, 6);
break;
case PWM_DRIVE:
lcd_setcursor(0, 1);
lcd_int(pwm_drive, 16);
break;
case PWM_STERING:
lcd_setcursor(0, 1);
lcd_int(pwm_stering, 16);
break;
case PID_STERING_P:
lcd_setcursor(0, 1);
lcd_int(pid_stering.pFactor, 16);
break;
case PID_STERING_I:
lcd_setcursor(0, 1);
lcd_int(pid_stering.iFactor, 16);
break;
case PID_DRIVE_P:
lcd_setcursor(0, 1);
lcd_int(pid_drive.pFactor, 16);
break;
case PID_DRIVE_I:
lcd_setcursor(0, 1);
lcd_int(pid_drive.iFactor, 16);
break;
case ADC_STERING_LEFT:
lcd_setcursor(0, 1);
lcd_int(adc_stering_left, 5);
lcd_bar(6, 1, 10, adc_stering_left / 10);
break;
case ADC_STERING_RIGHT:
lcd_setcursor(0, 1);
lcd_int(adc_stering_right, 5);
lcd_bar(6, 1, 10, adc_stering_right / 10);
break;
case ADC_BATT_LOGIC:
lcd_setcursor(10, 1);
lcd_int(adc_batt_logic, 6);
break;
case ADC_BATT_DRIVE:
lcd_setcursor(10, 1);
lcd_int(adc_batt_drive, 6);
break;
}
}
return 0;
}

112
controller/pid.c Normal file
View file

@ -0,0 +1,112 @@
/**
* General PID implementation for AVR
*
* Discrete PID controller implementation. Set up by giving P/I/D terms
* to pid_init(), and uses a struct pid to store internal values.
*
* Based on ATMEL AppNote: AVR221 - Discrete PID controller
*
* @copyright 2012 Institute Automation of Complex Power Systems (ACS), RWTH Aachen University
* @license http://www.gnu.org/licenses/gpl.txt GNU Public License
* @author Atmel Corporation <avr@atmel.com>
* @link http://www.atmel.com
* @revision 456
* @date 2006-02-16 12:46:13
*/
#include <stdlib.h>
#include <stdint.h>
#include "pid.h"
/**
* Initialisation of PID controller parameters
*
* Initialise the variables used by the PID algorithm
*
* @param p_factor Proportional term
* @param i_factor Integral term
* @param d_factor Derivate term
* @param pid Struct with PID status
*/
void pid_init(int16_t pFactor, int16_t iFactor, int16_t dFactor, struct pid *pid) {
// Start values for PID controller
pid->sumError = 0;
pid->lastProcessValue = 0;
// Tuning constants for PID loop
pid->pFactor = pFactor;
pid->iFactor = iFactor;
pid->dFactor = dFactor;
// Limits to avoid overflow
pid->maxError = MAX_INT / (pid->pFactor + 1);
pid->maxSumError = MAX_I_TERM / (pid->iFactor + 1);
}
/**
* PID control algorithm
*
* Calculates output from setpoint, process value and PID status
*
* @param setPoint Desired value
* @param processValue Measured value
* @param pid PID status struct
*/
int16_t pid_controller(int16_t setPoint, int16_t processValue, struct pid *pid) {
int16_t error, pTerm, dTerm;
int32_t iTerm, ret, temp;
error = setPoint - processValue;
// Calculate pTerm and limit error overflow
if (error > pid->maxError){
pTerm = MAX_INT;
}
else if (error < -pid->maxError){
pTerm = -MAX_INT;
}
else {
pTerm = pid->pFactor * error;
}
// Calculate iTerm and limit integral runaway
temp = pid->sumError + error;
if (temp > pid->maxSumError){
iTerm = MAX_I_TERM;
pid->sumError = pid->maxSumError;
}
else if (temp < -pid->maxSumError){
iTerm = -MAX_I_TERM;
pid->sumError = -pid->maxSumError;
}
else {
pid->sumError = temp;
iTerm = pid->iFactor * pid->sumError;
}
// Calculate Dterm
dTerm = pid->dFactor * (pid->lastProcessValue - processValue);
pid->lastProcessValue = processValue;
ret = (pTerm + iTerm + dTerm) >> SCALING_SHIFT;
if (ret > MAX_INT){
ret = MAX_INT;
}
else if (ret < -MAX_INT){
ret = -MAX_INT;
}
return (int16_t) ret;
}
/**
* Resets the integrator
*
* Calling this function will reset the integrator in the PID regulator
*/
void pid_reset_integrator(struct pid *pid) {
pid->sumError = 0;
}

54
controller/pid.h Normal file
View file

@ -0,0 +1,54 @@
/**
* General PID implementation for AVR
*
* Discrete PID controller implementation. Set up by giving P/I/D terms
* to pid_init(), and uses a struct pid to store internal values.
*
* Based on ATMEL AppNote: AVR221 - Discrete PID controller
*
* @copyright 2012 Institute Automation of Complex Power Systems (ACS), RWTH Aachen University
* @license http://www.gnu.org/licenses/gpl.txt GNU Public License
* @author Atmel Corporation <avr@atmel.com>
* @link http://www.atmel.com
* @revision 456
* @date 2006-02-16 12:46:13
*/
#ifndef _PID_H_
#define _PID_H_
#include <stdlib.h>
#include <stdint.h>
#define SCALING_FACTOR 128
#define SCALING_SHIFT 7
/**
* PID Status
*
* Setpoints and data used by the PID control algorithm
*/
struct pid {
int16_t lastProcessValue; // Last process value, used to find derivative of process value.
int32_t sumError; // Summation of errors, used for integrate calculations
int16_t pFactor; // The Proportional tuning constant, multiplied with SCALING_FACTOR
int16_t iFactor; // The Integral tuning constant, multiplied with SCALING_FACTOR
int16_t dFactor; // The Derivative tuning constant, multiplied with SCALING_FACTOR
int16_t maxError; // Maximum allowed error, avoid overflow
int32_t maxSumError; // Maximum allowed sumerror, avoid overflow
};
/**
* Maximum values
*
* Needed to avoid sign/overflow problems
*/
#define MAX_INT INT16_MAX
#define MAX_LONG INT32_MAX
#define MAX_I_TERM (MAX_LONG / 2)
void pid_init(int16_t pFactor, int16_t iFactor, int16_t dFactor, struct pid *pid);
int16_t pid_controller(int16_t setPoint, int16_t processValue, struct pid *pid);
void pid_reset_integrator(struct pid *pid);
#endif /* _PID_H_ */

125
controller/rotary.c Normal file
View file

@ -0,0 +1,125 @@
/**
* Rotary encoder routines
*
* @copyright 2012 Institute Automation of Complex Power Systems (ACS), RWTH Aachen University
* @license http://www.gnu.org/licenses/gpl.txt GNU Public License
* @author Steffen Vogel <info@steffenvogel.de>
*/
#include "rotary.h"
/**
* Drehgeber initialisieren
*/
void dgb_init() {
// DDR auf input setzen
SW_DDR &= ~(1<<SW_PIN_GRUEN);
SW_DDR &= ~(1<<SW_PIN_BLAU);
DGB_DDR &= ~(1<<DGB_PIN_A);
DGB_DDR &= ~(1<<DGB_PIN_B);
DGB_DDR &= ~(1<<DGB_PIN_SW);
// PullUps einschalten
SW_PORT |= (1<<SW_PIN_GRUEN);
SW_PORT |= (1<<SW_PIN_BLAU);
DGB_PORT |= (1<<DGB_PIN_A);
DGB_PORT |= (1<<DGB_PIN_B);
DGB_PORT |= (1<<DGB_PIN_SW);
}
/**
* Drehgeber auslesen
*
* Diese Funktion sollte periodisch aufgerufen werden (Polling)
*/
uint8_t dgb_read() {
// Debouncing
volatile static uint8_t lastA;
volatile static uint8_t lastSW;
volatile static uint8_t lastSW_Gruen;
volatile static uint8_t lastSW_Blau;
volatile static bool trigger;
volatile static bool ready;
volatile static bool ignore;
enum taster eingabe = 0;
// Drehgeber Trigger finden
if ((!(DGB_PIN & (1<<DGB_PIN_A))) && !trigger) {
if(lastA < 3) {
lastA++;
}
else {
trigger = true;
lastA = 0;
}
}
// Richtung auswerten
if (trigger && !ready) {
if (!(DGB_PIN & (1<<DGB_PIN_B))) {
eingabe = DGB_CCW;
ready = true;
}
else {
eingabe = DGB_CW;
ready = true;
}
}
if(ready && (DGB_PIN & (1<<DGB_PIN_A))) {
ready = false;
trigger = false;
}
// Schalter des Drehgebers auswerten
if (!(DGB_PIN & (1<<DGB_PIN_SW))) {
if (lastSW < 255) lastSW++;
}
else if (lastSW == 255) {
lastSW = 0;
if (ignore) {
ignore = false;
}
else {
eingabe = DGB_SW;
}
}
// zusätzliche Schalter auswerten
if (!(SW_PIN & (1<<SW_PIN_GRUEN))) {
if(lastSW_Gruen < 255) {
lastSW_Gruen++;
}
}
else if (lastSW_Gruen == 255) {
lastSW_Gruen = 0;
if (ignore) {
ignore = false;
}
else {
eingabe = SW_GRUEN;
}
}
if (!(SW_PIN & (1<<SW_PIN_BLAU))) {
if (lastSW_Blau < 255) {
lastSW_Blau++;
}
}
else if (lastSW_Blau == 255) {
lastSW_Blau = 0;
if (ignore) {
ignore = false;
}
else {
eingabe = SW_BLAU;
}
}
return eingabe;
}

44
controller/rotary.h Normal file
View file

@ -0,0 +1,44 @@
/**
* Rotary encoder headers
*
* @copyright 2012 Institute Automation of Complex Power Systems (ACS), RWTH Aachen University
* @license http://www.gnu.org/licenses/gpl.txt GNU Public License
* @author Steffen Vogel <info@steffenvogel.de>
*/
#ifndef _ROTARY_H_
#define _ROTARY_H_
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#define DGB_PORT PORTB
#define DGB_DDR DDRB
#define DGB_PIN_A PB0
#define DGB_PIN_B PB1
#define DGB_PIN_SW PB2
#define DGB_PIN PINB
// Definitionen für zusätzliche Schalter
#define SW_PORT PORTB
#define SW_DDR DDRB
#define SW_PIN PINB
#define SW_PIN_BLAU PB3
#define SW_PIN_GRUEN PB4
enum taster {
DGB_CCW = 1,
DGB_CW,
DGB_SW,
SW_GRUEN,
SW_BLAU
};
void dgb_init();
enum taster dgb_read();
#endif /* _ROTARY_H_ */

651
controller/uart.c Normal file
View file

@ -0,0 +1,651 @@
/*************************************************************************
Title: Interrupt UART library with receive/transmit circular buffers
Author: Peter Fleury <pfleury@gmx.ch> http://jump.to/fleury
File: $Id: uart.c,v 1.6.2.2 2009/11/29 08:56:12 Peter Exp $
Software: AVR-GCC 4.1, AVR Libc 1.4.6 or higher
Hardware: any AVR with built-in UART,
License: GNU General Public License
DESCRIPTION:
An interrupt is generated when the UART has finished transmitting or
receiving a byte. The interrupt handling routines use circular buffers
for buffering received and transmitted data.
The UART_RX_BUFFER_SIZE and UART_TX_BUFFER_SIZE variables define
the buffer size in bytes. Note that these variables must be a
power of 2.
USAGE:
Refere to the header file uart.h for a description of the routines.
See also example test_uart.c.
NOTES:
Based on Atmel Application Note AVR306
LICENSE:
Copyright (C) 2006 Peter Fleury
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 2 of the License, or
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.
*************************************************************************/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "uart.h"
/*
* constants and macros
*/
/* size of RX/TX buffers */
#define UART_RX_BUFFER_MASK ( UART_RX_BUFFER_SIZE - 1)
#define UART_TX_BUFFER_MASK ( UART_TX_BUFFER_SIZE - 1)
#if ( UART_RX_BUFFER_SIZE & UART_RX_BUFFER_MASK )
#error RX buffer size is not a power of 2
#endif
#if ( UART_TX_BUFFER_SIZE & UART_TX_BUFFER_MASK )
#error TX buffer size is not a power of 2
#endif
#if defined(__AVR_AT90S2313__) \
|| defined(__AVR_AT90S4414__) || defined(__AVR_AT90S4434__) \
|| defined(__AVR_AT90S8515__) || defined(__AVR_AT90S8535__) \
|| defined(__AVR_ATmega103__)
/* old AVR classic or ATmega103 with one UART */
#define AT90_UART
#define UART0_RECEIVE_INTERRUPT SIG_UART_RECV
#define UART0_TRANSMIT_INTERRUPT SIG_UART_DATA
#define UART0_STATUS USR
#define UART0_CONTROL UCR
#define UART0_DATA UDR
#define UART0_UDRIE UDRIE
#elif defined(__AVR_AT90S2333__) || defined(__AVR_AT90S4433__)
/* old AVR classic with one UART */
#define AT90_UART
#define UART0_RECEIVE_INTERRUPT SIG_UART_RECV
#define UART0_TRANSMIT_INTERRUPT SIG_UART_DATA
#define UART0_STATUS UCSRA
#define UART0_CONTROL UCSRB
#define UART0_DATA UDR
#define UART0_UDRIE UDRIE
#elif defined(__AVR_ATmega8__) || defined(__AVR_ATmega16__) || defined(__AVR_ATmega32__) \
|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega8535__) \
|| defined(__AVR_ATmega323__)
/* ATmega with one USART */
#define ATMEGA_USART
#define UART0_RECEIVE_INTERRUPT SIG_UART_RECV
#define UART0_TRANSMIT_INTERRUPT SIG_UART_DATA
#define UART0_STATUS UCSRA
#define UART0_CONTROL UCSRB
#define UART0_DATA UDR
#define UART0_UDRIE UDRIE
#elif defined(__AVR_ATmega163__)
/* ATmega163 with one UART */
#define ATMEGA_UART
#define UART0_RECEIVE_INTERRUPT SIG_UART_RECV
#define UART0_TRANSMIT_INTERRUPT SIG_UART_DATA
#define UART0_STATUS UCSRA
#define UART0_CONTROL UCSRB
#define UART0_DATA UDR
#define UART0_UDRIE UDRIE
#elif defined(__AVR_ATmega162__)
/* ATmega with two USART */
#define ATMEGA_USART0
#define ATMEGA_USART1
#define UART0_RECEIVE_INTERRUPT SIG_USART0_RECV
#define UART1_RECEIVE_INTERRUPT SIG_USART1_RECV
#define UART0_TRANSMIT_INTERRUPT SIG_USART0_DATA
#define UART1_TRANSMIT_INTERRUPT SIG_USART1_DATA
#define UART0_STATUS UCSR0A
#define UART0_CONTROL UCSR0B
#define UART0_DATA UDR0
#define UART0_UDRIE UDRIE0
#define UART1_STATUS UCSR1A
#define UART1_CONTROL UCSR1B
#define UART1_DATA UDR1
#define UART1_UDRIE UDRIE1
#elif defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__)
/* ATmega with two USART */
#define ATMEGA_USART0
#define ATMEGA_USART1
#define UART0_RECEIVE_INTERRUPT SIG_UART0_RECV
#define UART1_RECEIVE_INTERRUPT SIG_UART1_RECV
#define UART0_TRANSMIT_INTERRUPT SIG_UART0_DATA
#define UART1_TRANSMIT_INTERRUPT SIG_UART1_DATA
#define UART0_STATUS UCSR0A
#define UART0_CONTROL UCSR0B
#define UART0_DATA UDR0
#define UART0_UDRIE UDRIE0
#define UART1_STATUS UCSR1A
#define UART1_CONTROL UCSR1B
#define UART1_DATA UDR1
#define UART1_UDRIE UDRIE1
#elif defined(__AVR_ATmega161__)
/* ATmega with UART */
#error "AVR ATmega161 currently not supported by this libaray !"
#elif defined(__AVR_ATmega169__)
/* ATmega with one USART */
#define ATMEGA_USART
#define UART0_RECEIVE_INTERRUPT SIG_USART_RECV
#define UART0_TRANSMIT_INTERRUPT SIG_USART_DATA
#define UART0_STATUS UCSRA
#define UART0_CONTROL UCSRB
#define UART0_DATA UDR
#define UART0_UDRIE UDRIE
#elif defined(__AVR_ATmega48__) ||defined(__AVR_ATmega88__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega48P__) || defined(__AVR_ATmega88P__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega328P__)
/* ATmega with one USART */
#define ATMEGA_USART0
#define UART0_RECEIVE_INTERRUPT SIG_USART_RECV
#define UART0_TRANSMIT_INTERRUPT SIG_USART_DATA
#define UART0_STATUS UCSR0A
#define UART0_CONTROL UCSR0B
#define UART0_DATA UDR0
#define UART0_UDRIE UDRIE0
#elif defined(__AVR_ATtiny2313__)
#define ATMEGA_USART
#define UART0_RECEIVE_INTERRUPT SIG_USART0_RX
#define UART0_TRANSMIT_INTERRUPT SIG_USART0_UDRE
#define UART0_STATUS UCSRA
#define UART0_CONTROL UCSRB
#define UART0_DATA UDR
#define UART0_UDRIE UDRIE
#elif defined(__AVR_ATmega329__) ||defined(__AVR_ATmega3290__) ||\
defined(__AVR_ATmega649__) ||defined(__AVR_ATmega6490__) ||\
defined(__AVR_ATmega325__) ||defined(__AVR_ATmega3250__) ||\
defined(__AVR_ATmega645__) ||defined(__AVR_ATmega6450__)
/* ATmega with one USART */
#define ATMEGA_USART0
#define UART0_RECEIVE_INTERRUPT SIG_UART_RECV
#define UART0_TRANSMIT_INTERRUPT SIG_UART_DATA
#define UART0_STATUS UCSR0A
#define UART0_CONTROL UCSR0B
#define UART0_DATA UDR0
#define UART0_UDRIE UDRIE0
#elif defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__) || defined(__AVR_ATmega640__)
/* ATmega with two USART */
#define ATMEGA_USART0
#define ATMEGA_USART1
#define UART0_RECEIVE_INTERRUPT SIG_USART0_RECV
#define UART1_RECEIVE_INTERRUPT SIG_USART1_RECV
#define UART0_TRANSMIT_INTERRUPT SIG_USART0_DATA
#define UART1_TRANSMIT_INTERRUPT SIG_USART1_DATA
#define UART0_STATUS UCSR0A
#define UART0_CONTROL UCSR0B
#define UART0_DATA UDR0
#define UART0_UDRIE UDRIE0
#define UART1_STATUS UCSR1A
#define UART1_CONTROL UCSR1B
#define UART1_DATA UDR1
#define UART1_UDRIE UDRIE1
#elif defined(__AVR_ATmega644__)
/* ATmega with one USART */
#define ATMEGA_USART0
#define UART0_RECEIVE_INTERRUPT SIG_USART_RECV
#define UART0_TRANSMIT_INTERRUPT SIG_USART_DATA
#define UART0_STATUS UCSR0A
#define UART0_CONTROL UCSR0B
#define UART0_DATA UDR0
#define UART0_UDRIE UDRIE0
#elif defined(__AVR_ATmega164P__) || defined(__AVR_ATmega324P__) || defined(__AVR_ATmega644P__)
/* ATmega with two USART */
#define ATMEGA_USART0
#define ATMEGA_USART1
#define UART0_RECEIVE_INTERRUPT SIG_USART_RECV
#define UART1_RECEIVE_INTERRUPT SIG_USART1_RECV
#define UART0_TRANSMIT_INTERRUPT SIG_USART_DATA
#define UART1_TRANSMIT_INTERRUPT SIG_USART1_DATA
#define UART0_STATUS UCSR0A
#define UART0_CONTROL UCSR0B
#define UART0_DATA UDR0
#define UART0_UDRIE UDRIE0
#define UART1_STATUS UCSR1A
#define UART1_CONTROL UCSR1B
#define UART1_DATA UDR1
#define UART1_UDRIE UDRIE1
#else
#error "no UART definition for MCU available"
#endif
/*
* module global variables
*/
static volatile unsigned char UART_TxBuf[UART_TX_BUFFER_SIZE];
static volatile unsigned char UART_RxBuf[UART_RX_BUFFER_SIZE];
static volatile unsigned char UART_TxHead;
static volatile unsigned char UART_TxTail;
static volatile unsigned char UART_RxHead;
static volatile unsigned char UART_RxTail;
static volatile unsigned char UART_LastRxError;
#if defined( ATMEGA_USART1 )
static volatile unsigned char UART1_TxBuf[UART_TX_BUFFER_SIZE];
static volatile unsigned char UART1_RxBuf[UART_RX_BUFFER_SIZE];
static volatile unsigned char UART1_TxHead;
static volatile unsigned char UART1_TxTail;
static volatile unsigned char UART1_RxHead;
static volatile unsigned char UART1_RxTail;
static volatile unsigned char UART1_LastRxError;
#endif
SIGNAL(UART0_RECEIVE_INTERRUPT)
/*************************************************************************
Function: UART Receive Complete interrupt
Purpose: called when the UART has received a character
**************************************************************************/
{
unsigned char tmphead;
unsigned char data;
unsigned char usr;
unsigned char lastRxError;
/* read UART status register and UART data register */
usr = UART0_STATUS;
data = UART0_DATA;
/* */
#if defined( AT90_UART )
lastRxError = (usr & (_BV(FE)|_BV(DOR)) );
#elif defined( ATMEGA_USART )
lastRxError = (usr & (_BV(FE)|_BV(DOR)) );
#elif defined( ATMEGA_USART0 )
lastRxError = (usr & (_BV(FE0)|_BV(DOR0)) );
#elif defined ( ATMEGA_UART )
lastRxError = (usr & (_BV(FE)|_BV(DOR)) );
#endif
/* calculate buffer index */
tmphead = ( UART_RxHead + 1) & UART_RX_BUFFER_MASK;
if ( tmphead == UART_RxTail ) {
/* error: receive buffer overflow */
lastRxError = UART_BUFFER_OVERFLOW >> 8;
}else{
/* store new index */
UART_RxHead = tmphead;
/* store received data in buffer */
UART_RxBuf[tmphead] = data;
}
UART_LastRxError = lastRxError;
}
SIGNAL(UART0_TRANSMIT_INTERRUPT)
/*************************************************************************
Function: UART Data Register Empty interrupt
Purpose: called when the UART is ready to transmit the next byte
**************************************************************************/
{
unsigned char tmptail;
if ( UART_TxHead != UART_TxTail) {
/* calculate and store new buffer index */
tmptail = (UART_TxTail + 1) & UART_TX_BUFFER_MASK;
UART_TxTail = tmptail;
/* get one byte from buffer and write it to UART */
UART0_DATA = UART_TxBuf[tmptail]; /* start transmission */
}else{
/* tx buffer empty, disable UDRE interrupt */
UART0_CONTROL &= ~_BV(UART0_UDRIE);
}
}
/*************************************************************************
Function: uart_init()
Purpose: initialize UART and set baudrate
Input: baudrate using macro UART_BAUD_SELECT()
Returns: none
**************************************************************************/
void uart_init(unsigned int baudrate)
{
UART_TxHead = 0;
UART_TxTail = 0;
UART_RxHead = 0;
UART_RxTail = 0;
#if defined( AT90_UART )
/* set baud rate */
UBRR = (unsigned char)baudrate;
/* enable UART receiver and transmmitter and receive complete interrupt */
UART0_CONTROL = _BV(RXCIE)|_BV(RXEN)|_BV(TXEN);
#elif defined (ATMEGA_USART)
/* Set baud rate */
if ( baudrate & 0x8000 )
{
UART0_STATUS = (1<<U2X); //Enable 2x speed
baudrate &= ~0x8000;
}
UBRRH = (unsigned char)(baudrate>>8);
UBRRL = (unsigned char) baudrate;
/* Enable USART receiver and transmitter and receive complete interrupt */
UART0_CONTROL = _BV(RXCIE)|(1<<RXEN)|(1<<TXEN);
/* Set frame format: asynchronous, 8data, no parity, 1stop bit */
#ifdef URSEL
UCSRC = (1<<URSEL)|(3<<UCSZ0);
#else
UCSRC = (3<<UCSZ0);
#endif
#elif defined (ATMEGA_USART0 )
/* Set baud rate */
if ( baudrate & 0x8000 )
{
UART0_STATUS = (1<<U2X0); //Enable 2x speed
baudrate &= ~0x8000;
}
UBRR0H = (unsigned char)(baudrate>>8);
UBRR0L = (unsigned char) baudrate;
/* Enable USART receiver and transmitter and receive complete interrupt */
UART0_CONTROL = _BV(RXCIE0)|(1<<RXEN0)|(1<<TXEN0);
/* Set frame format: asynchronous, 8data, no parity, 1stop bit */
#ifdef URSEL0
UCSR0C = (1<<URSEL0)|(3<<UCSZ00);
#else
UCSR0C = (3<<UCSZ00);
#endif
#elif defined ( ATMEGA_UART )
/* set baud rate */
if ( baudrate & 0x8000 )
{
UART0_STATUS = (1<<U2X); //Enable 2x speed
baudrate &= ~0x8000;
}
UBRRHI = (unsigned char)(baudrate>>8);
UBRR = (unsigned char) baudrate;
/* Enable UART receiver and transmitter and receive complete interrupt */
UART0_CONTROL = _BV(RXCIE)|(1<<RXEN)|(1<<TXEN);
#endif
}/* uart_init */
/*************************************************************************
Function: uart_getc()
Purpose: return byte from ringbuffer
Returns: lower byte: received byte from ringbuffer
higher byte: last receive error
**************************************************************************/
unsigned int uart_getc(void)
{
unsigned char tmptail;
unsigned char data;
if ( UART_RxHead == UART_RxTail ) {
return UART_NO_DATA; /* no data available */
}
/* calculate /store buffer index */
tmptail = (UART_RxTail + 1) & UART_RX_BUFFER_MASK;
UART_RxTail = tmptail;
/* get data from receive buffer */
data = UART_RxBuf[tmptail];
return (UART_LastRxError << 8) + data;
}/* uart_getc */
/*************************************************************************
Function: uart_putc()
Purpose: write byte to ringbuffer for transmitting via UART
Input: byte to be transmitted
Returns: none
**************************************************************************/
void uart_putc(unsigned char data)
{
unsigned char tmphead;
tmphead = (UART_TxHead + 1) & UART_TX_BUFFER_MASK;
while ( tmphead == UART_TxTail ){
;/* wait for free space in buffer */
}
UART_TxBuf[tmphead] = data;
UART_TxHead = tmphead;
/* enable UDRE interrupt */
UART0_CONTROL |= _BV(UART0_UDRIE);
}/* uart_putc */
/*************************************************************************
Function: uart_puts()
Purpose: transmit string to UART
Input: string to be transmitted
Returns: none
**************************************************************************/
void uart_puts(const char *s )
{
while (*s)
uart_putc(*s++);
}/* uart_puts */
/*************************************************************************
Function: uart_puts_p()
Purpose: transmit string from program memory to UART
Input: program memory string to be transmitted
Returns: none
**************************************************************************/
void uart_puts_p(const char *progmem_s )
{
register char c;
while ( (c = pgm_read_byte(progmem_s++)) )
uart_putc(c);
}/* uart_puts_p */
/*
* these functions are only for ATmegas with two USART
*/
#if defined( ATMEGA_USART1 )
SIGNAL(UART1_RECEIVE_INTERRUPT)
/*************************************************************************
Function: UART1 Receive Complete interrupt
Purpose: called when the UART1 has received a character
**************************************************************************/
{
unsigned char tmphead;
unsigned char data;
unsigned char usr;
unsigned char lastRxError;
/* read UART status register and UART data register */
usr = UART1_STATUS;
data = UART1_DATA;
/* */
lastRxError = (usr & (_BV(FE1)|_BV(DOR1)) );
/* calculate buffer index */
tmphead = ( UART1_RxHead + 1) & UART_RX_BUFFER_MASK;
if ( tmphead == UART1_RxTail ) {
/* error: receive buffer overflow */
lastRxError = UART_BUFFER_OVERFLOW >> 8;
}else{
/* store new index */
UART1_RxHead = tmphead;
/* store received data in buffer */
UART1_RxBuf[tmphead] = data;
}
UART1_LastRxError = lastRxError;
}
SIGNAL(UART1_TRANSMIT_INTERRUPT)
/*************************************************************************
Function: UART1 Data Register Empty interrupt
Purpose: called when the UART1 is ready to transmit the next byte
**************************************************************************/
{
unsigned char tmptail;
if ( UART1_TxHead != UART1_TxTail) {
/* calculate and store new buffer index */
tmptail = (UART1_TxTail + 1) & UART_TX_BUFFER_MASK;
UART1_TxTail = tmptail;
/* get one byte from buffer and write it to UART */
UART1_DATA = UART1_TxBuf[tmptail]; /* start transmission */
}else{
/* tx buffer empty, disable UDRE interrupt */
UART1_CONTROL &= ~_BV(UART1_UDRIE);
}
}
/*************************************************************************
Function: uart1_init()
Purpose: initialize UART1 and set baudrate
Input: baudrate using macro UART_BAUD_SELECT()
Returns: none
**************************************************************************/
void uart1_init(unsigned int baudrate)
{
UART1_TxHead = 0;
UART1_TxTail = 0;
UART1_RxHead = 0;
UART1_RxTail = 0;
/* Set baud rate */
if ( baudrate & 0x8000 )
{
UART1_STATUS = (1<<U2X1); //Enable 2x speed
baudrate &= ~0x8000;
}
UBRR1H = (unsigned char)(baudrate>>8);
UBRR1L = (unsigned char) baudrate;
/* Enable USART receiver and transmitter and receive complete interrupt */
UART1_CONTROL = _BV(RXCIE1)|(1<<RXEN1)|(1<<TXEN1);
/* Set frame format: asynchronous, 8data, no parity, 1stop bit */
#ifdef URSEL1
UCSR1C = (1<<URSEL1)|(3<<UCSZ10);
#else
UCSR1C = (3<<UCSZ10);
#endif
}/* uart_init */
/*************************************************************************
Function: uart1_getc()
Purpose: return byte from ringbuffer
Returns: lower byte: received byte from ringbuffer
higher byte: last receive error
**************************************************************************/
unsigned int uart1_getc(void)
{
unsigned char tmptail;
unsigned char data;
if ( UART1_RxHead == UART1_RxTail ) {
return UART_NO_DATA; /* no data available */
}
/* calculate /store buffer index */
tmptail = (UART1_RxTail + 1) & UART_RX_BUFFER_MASK;
UART1_RxTail = tmptail;
/* get data from receive buffer */
data = UART1_RxBuf[tmptail];
return (UART1_LastRxError << 8) + data;
}/* uart1_getc */
/*************************************************************************
Function: uart1_putc()
Purpose: write byte to ringbuffer for transmitting via UART
Input: byte to be transmitted
Returns: none
**************************************************************************/
void uart1_putc(unsigned char data)
{
unsigned char tmphead;
tmphead = (UART1_TxHead + 1) & UART_TX_BUFFER_MASK;
while ( tmphead == UART1_TxTail ){
;/* wait for free space in buffer */
}
UART1_TxBuf[tmphead] = data;
UART1_TxHead = tmphead;
/* enable UDRE interrupt */
UART1_CONTROL |= _BV(UART1_UDRIE);
}/* uart1_putc */
/*************************************************************************
Function: uart1_puts()
Purpose: transmit string to UART1
Input: string to be transmitted
Returns: none
**************************************************************************/
void uart1_puts(const char *s )
{
while (*s)
uart1_putc(*s++);
}/* uart1_puts */
/*************************************************************************
Function: uart1_puts_p()
Purpose: transmit string from program memory to UART1
Input: program memory string to be transmitted
Returns: none
**************************************************************************/
void uart1_puts_p(const char *progmem_s )
{
register char c;
while ( (c = pgm_read_byte(progmem_s++)) )
uart1_putc(c);
}/* uart1_puts_p */
#endif

194
controller/uart.h Normal file
View file

@ -0,0 +1,194 @@
#ifndef UART_H
#define UART_H
/************************************************************************
Title: Interrupt UART library with receive/transmit circular buffers
Author: Peter Fleury <pfleury@gmx.ch> http://jump.to/fleury
File: $Id: uart.h,v 1.8.2.1 2007/07/01 11:14:38 peter Exp $
Software: AVR-GCC 4.1, AVR Libc 1.4
Hardware: any AVR with built-in UART, tested on AT90S8515 & ATmega8 at 4 Mhz
License: GNU General Public License
Usage: see Doxygen manual
LICENSE:
Copyright (C) 2006 Peter Fleury
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 2 of the License, or
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.
************************************************************************/
/**
* @defgroup pfleury_uart UART Library
* @code #include <uart.h> @endcode
*
* @brief Interrupt UART library using the built-in UART with transmit and receive circular buffers.
*
* This library can be used to transmit and receive data through the built in UART.
*
* An interrupt is generated when the UART has finished transmitting or
* receiving a byte. The interrupt handling routines use circular buffers
* for buffering received and transmitted data.
*
* The UART_RX_BUFFER_SIZE and UART_TX_BUFFER_SIZE constants define
* the size of the circular buffers in bytes. Note that these constants must be a power of 2.
* You may need to adapt this constants to your target and your application by adding
* CDEFS += -DUART_RX_BUFFER_SIZE=nn -DUART_RX_BUFFER_SIZE=nn to your Makefile.
*
* @note Based on Atmel Application Note AVR306
* @author Peter Fleury pfleury@gmx.ch http://jump.to/fleury
*/
/**@{*/
#if (__GNUC__ * 100 + __GNUC_MINOR__) < 304
#error "This library requires AVR-GCC 3.4 or later, update to newer AVR-GCC compiler !"
#endif
/*
** constants and macros
*/
/** @brief UART Baudrate Expression
* @param xtalcpu system clock in Mhz, e.g. 4000000L for 4Mhz
* @param baudrate baudrate in bps, e.g. 1200, 2400, 9600
*/
#define UART_BAUD_SELECT(baudRate,xtalCpu) ((xtalCpu)/((baudRate)*16l)-1)
/** @brief UART Baudrate Expression for ATmega double speed mode
* @param xtalcpu system clock in Mhz, e.g. 4000000L for 4Mhz
* @param baudrate baudrate in bps, e.g. 1200, 2400, 9600
*/
#define UART_BAUD_SELECT_DOUBLE_SPEED(baudRate,xtalCpu) (((xtalCpu)/((baudRate)*8l)-1)|0x8000)
/** Size of the circular receive buffer, must be power of 2 */
#ifndef UART_RX_BUFFER_SIZE
#define UART_RX_BUFFER_SIZE 64
#endif
/** Size of the circular transmit buffer, must be power of 2 */
#ifndef UART_TX_BUFFER_SIZE
#define UART_TX_BUFFER_SIZE 32
#endif
/* test if the size of the circular buffers fits into SRAM */
#if ( (UART_RX_BUFFER_SIZE+UART_TX_BUFFER_SIZE) >= (RAMEND-0x60 ) )
#error "size of UART_RX_BUFFER_SIZE + UART_TX_BUFFER_SIZE larger than size of SRAM"
#endif
/*
** high byte error return code of uart_getc()
*/
#define UART_FRAME_ERROR 0x0800 /* Framing Error by UART */
#define UART_OVERRUN_ERROR 0x0400 /* Overrun condition by UART */
#define UART_BUFFER_OVERFLOW 0x0200 /* receive ringbuffer overflow */
#define UART_NO_DATA 0x0100 /* no receive data available */
/*
** function prototypes
*/
/**
@brief Initialize UART and set baudrate
@param baudrate Specify baudrate using macro UART_BAUD_SELECT()
@return none
*/
extern void uart_init(unsigned int baudrate);
/**
* @brief Get received byte from ringbuffer
*
* Returns in the lower byte the received character and in the
* higher byte the last receive error.
* UART_NO_DATA is returned when no data is available.
*
* @param void
* @return lower byte: received byte from ringbuffer
* @return higher byte: last receive status
* - \b 0 successfully received data from UART
* - \b UART_NO_DATA
* <br>no receive data available
* - \b UART_BUFFER_OVERFLOW
* <br>Receive ringbuffer overflow.
* We are not reading the receive buffer fast enough,
* one or more received character have been dropped
* - \b UART_OVERRUN_ERROR
* <br>Overrun condition by UART.
* A character already present in the UART UDR register was
* not read by the interrupt handler before the next character arrived,
* one or more received characters have been dropped.
* - \b UART_FRAME_ERROR
* <br>Framing Error by UART
*/
extern unsigned int uart_getc(void);
/**
* @brief Put byte to ringbuffer for transmitting via UART
* @param data byte to be transmitted
* @return none
*/
extern void uart_putc(unsigned char data);
/**
* @brief Put string to ringbuffer for transmitting via UART
*
* The string is buffered by the uart library in a circular buffer
* and one character at a time is transmitted to the UART using interrupts.
* Blocks if it can not write the whole string into the circular buffer.
*
* @param s string to be transmitted
* @return none
*/
extern void uart_puts(const char *s );
/**
* @brief Put string from program memory to ringbuffer for transmitting via UART.
*
* The string is buffered by the uart library in a circular buffer
* and one character at a time is transmitted to the UART using interrupts.
* Blocks if it can not write the whole string into the circular buffer.
*
* @param s program memory string to be transmitted
* @return none
* @see uart_puts_P
*/
extern void uart_puts_p(const char *s );
/**
* @brief Macro to automatically put a string constant into program memory
*/
#define uart_puts_P(__s) uart_puts_p(PSTR(__s))
/** @brief Initialize USART1 (only available on selected ATmegas) @see uart_init */
extern void uart1_init(unsigned int baudrate);
/** @brief Get received byte of USART1 from ringbuffer. (only available on selected ATmega) @see uart_getc */
extern unsigned int uart1_getc(void);
/** @brief Put byte to ringbuffer for transmitting via USART1 (only available on selected ATmega) @see uart_putc */
extern void uart1_putc(unsigned char data);
/** @brief Put string to ringbuffer for transmitting via USART1 (only available on selected ATmega) @see uart_puts */
extern void uart1_puts(const char *s );
/** @brief Put string from program memory to ringbuffer for transmitting via USART1 (only available on selected ATmega) @see uart_puts_p */
extern void uart1_puts_p(const char *s );
/** @brief Macro to automatically put a string constant into program memory */
#define uart1_puts_P(__s) uart1_puts_p(PSTR(__s))
/**@}*/
#endif // UART_H