Bei meiner 10 bit Variante hört man die Motoren manchmal leise piepsen
7Khz hört man halt - aber als Nachteil finde ich das nicht. Ich fänd's cool, wenn er proportional zur Geschwindigkeit brummen tät. Irgendwas mit Prescaler 64 und Fast PWM oder so..
Code:
/*********************************************************************
N I B O B E E Feste Zykluszeit für Mainloop und
zyklische (Sensor) Ein- und (LED) Ausgabe
Version 1.07 für alternative Nibobee C Bib von s.frings
20100411 Birger Töpelmann
*********************************************************************/
#ifndef _ZYKLUS_H_
#define _ZYKLUS_H_
// Folgende Zeile auskommentieren für "normalen" NiboBee
#define _LED6_MUX_ // 6 LEDs an PB0..PB2; PB3 ist frei
/*--------------------------------------------------------------------
- PB2 -|100R|-->(x) es leuchtet pro Takt immer nur eine LED;
- PB1 -|100R|-->(y) ein PIN ist Plus, ein PIN Minus, ein Pin offen
- PB0 -|100R|-->(z) LED (Vor-)Widerstand 2 x 100R
LED_Pattern
Bit Anode - Kathode DDRB PORTB
0 DUO links grün (y) - (z) ..110 ..010
1 DUO links rot (x) - (z) ..101 ..001
2 DUO rechts grün (z) - (y) ..110 ..100
3 DUO rechts rot (x) - (y) ..011 ..001
4 ROTAUGE links (z) - (x) ..011 ..010
5 ROTAUGE rechts (y) - (x) ..101 ..100
Zuordnung LED_Pattern Bit# und LED anpassbar in led_ausgabe():
case 0:{ ddr = 0x06; pin = 0x02;} break; // DUO links grün
case 1:{ ddr = 0x05; pin = 0x01;} break; // DUO links rot
case 2:{ ddr = 0x06; pin = 0x04;} break; // DUO rechts grün
case 3:{ ddr = 0x03; pin = 0x01;} break; // DUO rechts rot
case 4:{ ddr = 0x03; pin = 0x02;} break; // ROTAUGE links
case 5:{ ddr = 0x05; pin = 0x04;} break; // ROTAUGE rechts
--------------------------------------------------------------------*/
//#include "nibobee.h" // s.frings Bibliothek
#include "zyklus.h" // also includes "nibobee.h"
#include <stdio.h>
#include <avr/interrupt.h>
#include <util/atomic.h>
#include <util/delay.h>
/******************************************* ZYKLUS & CLOCK ***********/
// zählt bis 1000 (1s) in 1ms Schritten
volatile uint16_t takt_ms;
// zählt die Sekunden seit dem Systemstart
volatile uint32_t takt_sec;
// Zyklustimer wird in ISR inkrementiert und in Zyklus()
volatile uint8_t zyklus_ms; // überwacht und genullt
// bremst die Hauptschleife auf feste Zykluszeit (Zzeit in ms)
// organisert LED Ausgabe und Sensoren Eingabe mit Flankenerkennung
uint8_t Zyklus(uint8_t Zzeit);
/****************************************** Sensoren ******************/
volatile uint8_t Sensors; // Aktueller Eingabestatus
volatile uint8_t SensSFL; // Steigende Flanke erkannt
volatile uint8_t SensFFL; // Fallende Flanke erkannt
/*---------------------------------------------------------------------
Flankenerkennung (Trigger):
0101 Bitmuster vorher
0011 Bitmuster aktuell
0010 Bitmuster steigende Flanke
0100 Bitmuster fallende Flanke
----------------------------------------------------------------------*/
void sens_eingabe(); // SensorPins lesen/Flankenerkennung
#define SENS_SW1_1 readBit(Sensors,4)
#define SENS_SW2_1 readBit(Sensors,5)
#define SENS_SW3_1 readBit(Sensors,7)
#define SENS_SW4_1 readBit(Sensors,6)
#define SENS_SW1_S readBit(SensSFL,4)
#define SENS_SW2_S readBit(SensSFL,5)
#define SENS_SW3_S readBit(SensSFL,7)
#define SENS_SW4_S readBit(SensSFL,6)
#define SENS_SW1_F readBit(SensFFL,4)
#define SENS_SW2_F readBit(SensFFL,5)
#define SENS_SW3_F readBit(SensFFL,7)
#define SENS_SW4_F readBit(SensFFL,6)
/***************************************** LED MULTIPLEX **************/
volatile uint8_t LED_Pattern; // LED Ausgabemuster
volatile uint8_t LED_PattSFL;
volatile uint8_t LED_PattFFL;
volatile uint8_t LED_Blinker; // gesetztes Bit lässt LED blinken
#ifdef _LED6_MUX_ // umgebauter NiboBee
// Kompatibilität zu s.frings: entsprechendes Bit in Pattern setzen
#define set_LED0(value) writeBit(LED_Pattern,1,value)
#define set_LED1(value) writeBit(LED_Pattern,4,value)
#define set_LED2(value) writeBit(LED_Pattern,5,value)
#define set_LED3(value) writeBit(LED_Pattern,3,value)
// Returns 1, if the LED is on
#define LED0 readBit(LED_Pattern,1)
#define LED1 readBit(LED_Pattern,4)
#define LED2 readBit(LED_Pattern,5)
#define LED3 readBit(LED_Pattern,3)
#else // normaler NiboBee
// Kompatibilität zu s.frings: entsprechendes Bit in Pattern setzen
#define set_LED0(value) writeBit(LED_Pattern,0,value)
#define set_LED1(value) writeBit(LED_Pattern,1,value)
#define set_LED2(value) writeBit(LED_Pattern,2,value)
#define set_LED3(value) writeBit(LED_Pattern,3,value)
// Returns 1, if the LED is on
#define LED0 readBit(LED_Pattern,0)
#define LED1 readBit(LED_Pattern,1)
#define LED2 readBit(LED_Pattern,2)
#define LED3 readBit(LED_Pattern,3)
#endif //_LED6_MUX_
// meine LED Makros basieren auf LED Nummer
#define LED_EIN(NR) LED_Pattern |= (1 << NR);
#define LED_AUS(NR) LED_Pattern &= ~(1 << NR); LED_Blinker &= ~(1 << NR);
#define LED_BLINK(NR) LED_Pattern |= (1 << NR); LED_Blinker |= (1 << NR);
#define LED_TOGGLE(NR) LED_Pattern ^= (1 << NR);
void led_ausgabe(); // LED Multiplex Ausgabe
// Debughilfen: vordere LED links/rechts bei ea==0 aus-,
// sonst einschalten, !! schaltet jew. die andere LED aus
void augeL(uint8_t ea);
void augeR(uint8_t ea);
/***************************************** LED MULTIPLEX **************/
#endif // _ZYKLUS_H_
Code:
/*********************************************************************
N I B O B E E Feste Zykluszeit für Mainloop und
zyklische (Sensor) Ein- und (LED) Ausgabe
Version 1.07 für alternative Nibobee C Bib von s.frings
20100411 Birger Töpelmann
*********************************************************************/
#include "zyklus.h"
#include <stdio.h>
#include <avr/interrupt.h>
#include <util/atomic.h>
#include <util/delay.h>
/***************************************** CLOCK & MOTPID ***********/
volatile uint16_t takt_ms;
volatile uint32_t takt_sec;
//#define Zykluszeit 1 // Zykluszeit der Hauptschleife in ms
volatile uint8_t zyklus_ms; // Zyklustimer wird in ISR inkrementiert
// und in Zyklus() überwacht und genullt
/************************************************* SENS *************/
volatile uint8_t Sensors = 0; // Aktueller Eingabestatus
volatile uint8_t SensSFL = 0; // Steigende Flanke erkannt
volatile uint8_t SensFFL = 0; // Fallende Flanke erkannt
void sens_eingabe()
{
SensSFL = ~Sensors; // vorherigen Zustand merken für Flankenerkennung
SensFFL = Sensors;
Sensors ^= Sensors; // bits nullen für neuen Zustand
Sensors |= ~(PINC & 0xf0); // 1 == Sensor aktiv
// Hier Eingänge von Ext-Ports Pins verOdern
SensSFL &= Sensors; // Steigende Flanke erkennen
SensFFL &= ~Sensors; // Fallende Flanke erkennen
}
/************************************************* LED **************/
/*
#define LED_EIN(NR) LED_Pattern |= (1 << NR);
#define LED_AUS(NR) LED_Pattern &= ~(1 << NR); LED_Blinker &= ~(1 << NR);
#define LED_BLINK(NR) LED_Pattern |= (1 << NR); LED_Blinker |= (1 << NR);
#define LED_TOGGLE(NR) LED_Pattern ^= (1 << NR);
*/
volatile uint8_t LED_Pattern = 0;
volatile uint8_t LED_Blinker = 0;
void led_ausgabe()
{
static uint16_t tmux = 0;
if (0 == tmux--) tmux = 640; // !Wert = vielfaches von 8
uint8_t leds = LED_Pattern; // Pattern für Ausgabe
if (tmux < 400) leds &= ~LED_Blinker; // Blinkbits
#ifdef _LED6_MUX_ // umgebauter NiboBee
uint8_t ddr = 0;
uint8_t pin = 0;
if ((1 << (tmux & 0x07)) & leds) // welche LED soll leuchten?
{
switch((tmux & 0x07))
{
case 0:{ ddr = 0x06; pin = 0x02;} break; // DUO links grün
case 1:{ ddr = 0x05; pin = 0x01;} break; // DUO links rot
case 2:{ ddr = 0x06; pin = 0x04;} break; // DUO rechts grün
case 3:{ ddr = 0x03; pin = 0x01;} break; // DUO rechts rot
case 4:{ ddr = 0x03; pin = 0x02;} break; // ROTAUGE links
case 5:{ ddr = 0x05; pin = 0x04;} break; // ROTAUGE rechts
// case 6:{ ddr = 0x00; pin = 0x00;} break;
// case 7:{ ddr = 0x00; pin = 0x00;} break;
}
}
DDRB = (DDRB & ~0x07) | ddr; // zwei PINS als Ausgang
PORTB = (PORTB & ~0x07) | pin; // ein Pin = 1, Rest = 0
#else // nicht umgebauter NiboBee
// Auch hier wird immer nur eine LED eingeschaltet
// 1 * Zykluszeit Ein; 3 * Zykluszeit Aus
PORTB = (PORTB & ~0x0f) | (leds & (1 << (tmux & 3)));
#endif //_LED6_MUX_
}
/*
// Debughilfen
void augeL(uint8_t ea)
{
uint8_t ddr = 0;
uint8_t pin = 0;
if(ea){ ddr = 0x03; pin = 0x02;} // ROTAUGE links
DDRB = (DDRB & ~0x07) | ddr; // zwei PINS als Ausgang
PORTB = (PORTB & ~0x07) | pin; // ein Pin = 1, Rest = 0
}
void augeR(uint8_t ea)
{
uint8_t ddr = 0;
uint8_t pin = 0;
if(ea){ ddr = 0x05; pin = 0x04;} // ROTAUGE rechts
DDRB = (DDRB & ~0x07) | ddr; // zwei PINS als Ausgang
PORTB = (PORTB & ~0x07) | pin; // ein Pin = 1, Rest = 0
}
*/
/******************************* TIMER 1 CONFIG PWM & TAKT **********/
// s.frings setup:
// Initialize the motor PWM
// Mode 2 = phase correct 10 bit PWM,
// channel A+B outputs are set on compare match when upcounting
// and cleared on compare match when downcounting.
// TCCR1A = PWM invertiert + + 10bit PWM Betrieb
// TCCR1A = (1<<COM1A1) | (1<<COM1A0) | (1<<COM1B1) | (1<<COM1B0) | (1<<WGM11); | (1<<WGM10);
// Original nibobee (springob) setup
// TCCR1A = PWM invertiert + + 9bit PWM Betrieb
// TCCR1A = (1<<COM1A1) | (1<<COM1A0) | (1<<COM1B1) | (1<<COM1B0) | (1<<WGM11);
// Use I/O clock without prescaling
// TCCR1B = (1<<CS10);
// Overflow Interrupt enable
// TIMSK |= (1<<TOIE1);
// F_CPU = 15 Mhz
// Prescale = 1
// Frequenz bei 10 bit 15000000/(2*1023)= 7.331 kHz ; OVF alle 136 uS
// Frequenz bei 9 bit 15000000/(2* 511)=14.677 kHz ; OVF alle 68 uS
#define T1PERIODE 136 // Periodendauer zwischen Overflows in uS
/************************************* TIMER 1 OVERFLOW ISR **********/
ISR(TIMER1_OVF_vect)
{
static uint16_t takt_us=0;
takt_us += T1PERIODE;
if (takt_us >= 1000)
{
takt_us -= 1000;
takt_ms++;
zyklus_ms++;
}
if (takt_ms >= 1000)
{
takt_ms -= 1000;
takt_sec++;
}
}
/**************************************** ZYKLUS & WATCHDOG **********/
uint8_t Zyklus(uint8_t Zzeit)
{
if(Zzeit == 0) // Zyklustimer Reset / Init
{
// Timer 1 Overflow Interrupt freigeben, damit die Uhr tickt
#ifdef TIMSK1
// ATmega644
TIMSK1 |= (1<<TOIE1);
#else
// ATmega16
TIMSK |= (1<<TOIE1);
#endif
zyklus_ms = 0;
return(0);
}
sei(); // Interrupts freigeben
led_ausgabe(); // Ausgänge aktualisieren
uint8_t zms = zyklus_ms; // aktuelle Zykluszeit merken
while(zyklus_ms < Zzeit){}; // Zykluszeit abwarten
sens_eingabe(); // Eingänge lesen
// -------------------- WATCHDOG ---------------------------
// es wird kein Reset/Neustart ausgelöst -> Endlosschleife
// LED blinken im Binär Muster der erfassten Zykluszeit
if(zyklus_ms > Zzeit) // Zykluszeit überschritten ?
{
uint8_t zyt = 0; // alternativ Zyklustimer (Blinken)
if (zms > 15) zms = 15; // Zykluszeit >= 15 -> alle LED ein
while(1==1) // Fehlerfalle Zykluszeit überschritten
{
_delay_ms(10); // Klassische Verzögerung
if(zyt-- == 0) zyt = 70;// Blinktakt 700ms
if(zyt > 50) {
LED_Pattern = zms; // Ist-Zykluszeit Binär ausgeben
} else {
LED_Pattern = 0; // LED aus für Blinklicht
}
led_ausgabe(); // LED Muster am Port ausgeben
}
}//--------------------------------------------------------
else zyklus_ms = 0; // Zykluszeit Timer Reset
return(zms); // Interessant bei Zykluszeit > 1:
// Wie lang war der Zyklus tatsächlich
}
Und soweit bin ich bisher mit der Main() von s.frings gekommen: hier sind noch etliche Anpassungen nötig; _delay_ms(), while{} oder blockierende Funktionen sind eigentlich No Gos. Aber ich verspreche mir davon, dass mehrere "prozesse" quasi gleichzeitig bearbeitet werden können.
Code:
/*********************************************************************
N I B O B E E Projekt Alternative s.frings und Birger.T
zum Selberbasteln und Ändern Ver.20100411
*********************************************************************/
#define Zykluszeit 1 // Hauptschleife Zykluszeit in ms
#define USE_SERIAL 0
#include "nibobee.h"
#include "zyklus.h"
uint16_t previousSpeed;
// Display battery status on the 4 led's
// LED0 = >4.1V
// LED1 = >4.3V
// LED2 = >4.5V
// LED3 = >4.7V
void battery_check() {
set_AREF(REF_256);
for (uint8_t i=0; i<10; i++) {
// _delay_ms(50);
for (uint8_t t=0; t<50; t++) {
_delay_ms(1);
led_ausgabe();
}
set_LED0(analog(VBAT)>(410*2));
set_LED1(analog(VBAT)>(430*2));
set_LED2(analog(VBAT)>(450*2));
set_LED3(analog(VBAT)>(470*2));
// _delay_ms(50);
for (uint8_t t=0; t<50; t++) {
_delay_ms(1);
led_ausgabe();
}
set_LED0(0);
set_LED1(0);
set_LED2(0);
set_LED3(0);
}
set_AREF(REF_VCC);
_delay_ms(50);
}
// Wait for start signal (touch any sensor)
// While waiting, display debug information from sensors:
// LED0: Left odometer sensor
// LED3: Right odometer sensor
// LED1: System timer
// LED2: Center line sensor
void wait_for_start() {
while (!(SENS_SW1 || SENS_SW2 || SENS_SW3 || SENS_SW4)) {
// Display status of odometry sensors while waiting
set_LED0(ODO_L);
set_LED3(ODO_R);
// Display system timer (flashes every second)
set_LED1((system_time() % 1000) < 20);
// Display line sensor
set_LED2(analog(LINE_C)>600);
_delay_ms(1);
led_ausgabe();
}
set_LED0(0);
set_LED3(0);
_delay_ms(10);
while ((SENS_SW1 || SENS_SW2 || SENS_SW3 || SENS_SW4)) {}
_delay_ms(400);
}
#define FORWARD 1
#define BACKWARD 0
volatile uint8_t running = 0;
// Drive forward or backward.
// Accellerate or decellerate softly to the maxSpeed.
// The distance is measured in 1/20 wheel rotations.
// LED0 or LED3 light if a which motor runs too fast.
uint8_t drive(uint8_t direction, uint16_t distance, uint16_t maxSpeed) {
static uint16_t speed= 0;
static uint32_t lastTime=0;
if(!running){
if (direction) {
set_DIR_L(1);
set_DIR_R(0);
}
else {
set_DIR_L(0);
set_DIR_R(1);
}
// Start with the previous speed
// uint16_t speed=previousSpeed;
// uint32_t lastTime=system_time();
speed=previousSpeed;
lastTime=system_time();
reset_odometer();
running = 1;
}
// while (odometer_left()<distance || odometer_right()<distance) {
if(!(odometer_left()<distance || odometer_right()<distance)) running = 0; // Ziel erreicht
else{
int16_t diff=odometer_left()-odometer_right();
// If left motor is too fast
if (diff>0) {
set_LED0(1);
PWM_L=speed/2;
PWM_R=speed;
}
// If right motor is too fast
else if (diff<0) {
set_LED3(1);
PWM_R=speed/2;
PWM_L=speed;
}
// Speed of both motors is equal
else {
set_LED0(0);
set_LED3(0);
PWM_L=speed;
PWM_R=speed;
}
// If a millisecond has elapsed, increase or decrease the speed
// a little until the maxSpeed has been reached.
uint32_t currentTime=system_time();
if (currentTime>lastTime) {
if (speed<maxSpeed)
speed++;
else if (speed>maxSpeed)
speed--;
lastTime=currentTime;
}
} // vom while
previousSpeed=speed;
return(running);
}
// Stop driving.
void stop() {
PWM_L=0;
PWM_R=0;
previousSpeed=0;
running = 0;
}
// Main program
int main()
{
uint8_t tour = 0; // Merker für den Fahrtabschnitt
uint16_t warten = 0;
// nibobee_init();
// Display battery status
battery_check();
// Wait for a start signal before start driving
wait_for_start();
// Drive forward and backward repeatedly, to demonstrate
// that the drive() function keeps straight on properly.
Zyklus(0); //Zyklustimer Reset / Init
while(1)
{
// deactivate_output_bit(IO_LINE_EN); // Front IR Leds aus
writeBit(PORTB,PB5,0);
Zyklus(Zykluszeit); // Zykluszeit abwarten
// activate_output_bit(IO_LINE_EN); // Front IR LEDs blitzen im Zyklustakt für
// Zeitmessung per IR Transistor und Oszi
writeBit(PORTB,PB5,1);
// Kollision (oder an die Fühler getippt) = Stop
if(SENS_SW2_S || SENS_SW3_S) {
tour = 100;
stop();
}
// Neustart bei Sensor nach vorne tippen und loslassen
if((tour == 100) && (SENS_SW1_F || SENS_SW4_F)) tour = 0;
// Drive forward 2 meters, accellerating up to nearly maximum speed
if((tour == 0) && !drive(FORWARD,2000/6,900)) tour = 10;
// Drive forward 30cm, decellerating down to the minimum speed
if((tour == 10) && !drive(FORWARD,300/6,200)){
stop();
tour = 20;
warten = 500;
}
// _delay_ms(500);
if((tour == 20) && !warten--) tour = 30;
// Drive backward 2 meters, accellerating up to nearly maximum speed
if((tour == 30) && !drive(BACKWARD,2000/6,900)) tour = 40;
// Drive backward 30cm, decellerating down to the minimum speed
if((tour == 40) && !drive(BACKWARD,300/6,200)) {
stop();
tour = 50;
warten = 500;
}
// _delay_ms(500);
if((tour == 50) && !warten--) tour = 0;
}
return 0;
}
Oder ich weiß nicht - liege ich mit meiner Vorstellung einer Controllerprogrammierung total daneben? Dann geb' ich's auf und bastel mir 'ne Logo oder zwei auf Räder...
Eigentlich wollte ich noch ein Foto machen: Meine Erfahrung LiPos halten so lange, bis man sie dann mal braucht.. bis zum WE.
Lesezeichen