added controller firmware
This commit is contained in:
parent
6a2edcde49
commit
99eda35745
13 changed files with 2280 additions and 0 deletions
80
controller/Makefile
Normal file
80
controller/Makefile
Normal 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
57
controller/adc.c
Normal 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
25
controller/adc.h
Normal 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
BIN
controller/init.o
Normal file
Binary file not shown.
236
controller/lcd.c
Normal file
236
controller/lcd.c
Normal 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
154
controller/lcd.h
Normal 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
548
controller/main.c
Normal 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
112
controller/pid.c
Normal 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
54
controller/pid.h
Normal 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
125
controller/rotary.c
Normal 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
44
controller/rotary.h
Normal 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
651
controller/uart.c
Normal 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
194
controller/uart.h
Normal 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
|
||||
|
Loading…
Add table
Reference in a new issue