PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Funkmodul RFM12 + ATMEGA32



Hoebi1
07.12.2013, 18:38
Liebe Roboternetz Community,

ich arbeite seit Tagen an einer Funkübertragung mittels RFM12 zwischen zwei ATMEGA32. Die Anforderungen sind nicht so besonders...ich möchte nur alle paar Sekunden 8 byte senden (in den untenstehenden Programmen jeweils nur 1 Datenbyte pro Sekunde). Leider bringe ich nichts zustande ...habe mich bei der Programmierung sehr an die ausgezeichnete Doku zum RN-MikroFunk V1.7 gehalten (jedoch mit Hardware SPI).
Ich bin mir sehr bewusst, dass meine Arbeit eher unschön und amateurmäßig ist...habe sehr wenig Erfahrung mit AVR und generell µC. Ich würde euch aber trotzdem bitten, meine Programme anzusehen und mir Fehlerquellen zu verraten. Einen Hardwarefehler schließe ich aus, da ich schon mehrmals kontrolliert habe. Ich freue mich schon auf eure Antworten!

Hier zuerst mal der Sender:


#define F_CPU 16000000UL // Taktfrequenz des µC definieren
#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>


volatile int Data_H;
volatile int Data_L;
volatile int i = 0;

void SPI_MasterInit(void)
{
/* Set SS, MOSI and SCK output, all others input */
DDRB = (1<<DDB4)|(1<<DDB5)|(1<<DDB7);
/* Enable SPI, Master, set clock rate fck/16 */
SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
return;
}

void SPI_MasterTransmit(unsigned int Data)
{
Data_H = ((Data >>8) & 0x00FF);
Data_L = Data & 0x00FF;

PORTB &= ~(1<<PB4); //nSel auf Low ziehen
SPDR = Data_H; /* Start transmission */
while(!(SPSR & (1<<SPIF)));
/* Wait for transmission complete */
PORTB |= (1<<PB4); //nSEL auf High
_delay_ms(10);
PORTB &= ~(1<<PB4);
SPDR = Data_L; /* Start transmission */
while(!(SPSR & (1<<SPIF))); /* Wait for transmission complete */
PORTB |=(1<<PB4);
_delay_ms(10);
return;
}


int main(void)
{

//------------------------------------µC - Initialisierung ----------------------------------------------------------------------
// AD - Wandler:

PORTB &= ~(1<<PB0);

ADMUX |= 0x00; // Sind die Mux4...0 -Bits = 0, so wird der Port ADC0 ausgewählt
ADMUX |= (1<<REFS0) | (1<<ADLAR); // Referenzspannung ist AVCC

ADCSRA = 0x00;
ADCSRA |= (1<<ADPS0) | (1<<ADPS1) | (1<<ADPS2); // Prescaler für Samplerate auf 128
ADCSRA |= (1<<ADEN); // ADC einschalten



// TIMER 0:

TCCR0 = (1<<WGM01) | (1<<CS02) | (1<<CS00); // CTC Mode im Timer und Prescaler auf 16MHz/1024
OCR0 = 0x1F; // Timer - Intervall

TIMSK |= (1<<OCIE0); // Compare Match Interrupt aktivieren
sei(); // Interrupts generell aktivieren


// RFM12 Funkmodul:

SPI_MasterInit();

SPI_MasterTransmit(0x80D7); //Enable: 433 Mhz;XTAL cap=12pf; TX-Register; RX-Fifo //// D7!!!!
SPI_MasterTransmit(0x82D9); //Enable: Receiver; Crystal Osc; Base Band Block; Synthesizer
//Disable Low-bat Detector; Transmitter; Wake-Up-Timer; Clock output Pin
SPI_MasterTransmit(0xA67C); //Frequenz 434,15 Mhz
SPI_MasterTransmit(0xC623); //Baudrate 9600kBps
SPI_MasterTransmit(0x90C0); //LNA Gain=MAX; Pin=nInt; RX Bandwidth=67 khz; VDI=Fast; DRSSI=-103 dB
SPI_MasterTransmit(0xC2AC); //Fiter=Digital; Recover Mode=Auto; Quality Threshold=4; Recovery Speed=Slow
SPI_MasterTransmit(0xCA80); //FIFO INT Level=8; Sync on=2;Fifo Fill Start=Sync; Reset; Sensitivity=High
//Disable:FIFO Fill Enabled
SPI_MasterTransmit(0xC483); //Enable: AFC Mode; AFC; Frequency Offset Register
//Disable: High Accuracy; Strobe
SPI_MasterTransmit(0x9820); //Frequenz Shift=POS; Power Out=0 dB; Deviation=45 khz
SPI_MasterTransmit(0xE000); //WakeUp timer = 0ms
SPI_MasterTransmit(0xC800); //Duty Cycle = Infinity % OFF
SPI_MasterTransmit(0xC000); //Low Battery = 2,2V; Clock Pin = 1Mhz
SPI_MasterTransmit(0xCED4); //Synchron Pattern
SPI_MasterTransmit(0xCC57); //PLL Settings
SPI_MasterTransmit(0x0000); //Status lesen, nirqs zurückstellen
_delay_ms(200); //200ms warten




while(1)
{
if(i==61) // 61 für Sekundentakt (1s/(256*16Mhz/1024))
{
i = 0; // Zähler zurücksetzen
//ADCSRA |= (1<<ADSC); // Starten der Umwandlung
//while(ADCSRA & (1<<ADSC)); // Warten bis ADSC wieder 0 wird (µC ist fertig mit der AD - Wandlung)
//PORTB = ADCH; // Ausgabe des Higher-Bytes am Port B


// Daten übertragen

SPI_MasterTransmit(0x8238); // Sender, Synthesizer Quarzoszillator
SPI_MasterTransmit(0xB8AA);
SPI_MasterTransmit(0xB8AA);
SPI_MasterTransmit(0xB8AA); //1010 zum synchronisieren mit dem Empfänger
SPI_MasterTransmit(0xB82D); //Synchronbyte
SPI_MasterTransmit(0xB8D4); //Synchronbyte

SPI_MasterTransmit(0xB801); //DATEN (01 nur TEST)
SPI_MasterTransmit(0xB8AA);
SPI_MasterTransmit(0xB8AA); //1010 zum synchronisieren mit dem Empfänger
SPI_MasterTransmit(0x8208); //Sender abschalten


}
}
}

ISR(TIMER0_COMP_vect) // Interrupt des Timer0
{
i++;
}


Und hier der Empfänger:

#define F_CPU 16000000UL // Taktfrequenz des µC definieren
#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>

volatile int Data_H;
volatile int Data_L;

void SPI_MasterInit(void)
{
// Set SS, MOSI and SCK output, all others input ----> MISO muss input sein!
DDRB = (1<<DDB4)|(1<<DDB5)|(1<<DDB7);
/* Enable SPI, Master, set clock rate fck/16 */
SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
return;
}

void SPI_MasterTransmit(unsigned int Data)
{
Data_H = ((Data >>8) & 0x00FF);
Data_L = Data & 0x00FF;

PORTB &= ~(1<<PB4); //nSel auf Low ziehen
SPDR = Data_H; /* Start transmission */
while(!(SPSR & (1<<SPIF)));
/* Wait for transmission complete */
PORTB |= (1<<PB4); //nSEL auf High
_delay_ms(5);
PORTB &= ~(1<<PB4);
SPDR = Data_L; /* Start transmission */
while(!(SPSR & (1<<SPIF))); /* Wait for transmission complete */
PORTB |=(1<<PB4);
_delay_ms(5);
return;
}

void RFM12_init(void)
{
// RFM12 Funkmodul:

SPI_MasterTransmit(0x80D7); //Enable: 433 Mhz;XTAL cap=12pf; TX-Register; RX-Fifo
SPI_MasterTransmit(0x82D9); //Enable: Receiver; Crystal Osc; Base Band Block; Synthesizer
//Disable Low-bat Detector; Transmitter; Wake-Up-Timer; Clock output Pin
SPI_MasterTransmit(0xA67C); //Frequenz 434,15 Mhz
SPI_MasterTransmit(0xC623); //Baudrate 9600kBps
SPI_MasterTransmit(0x90C0); //LNA Gain=MAX; Pin=nInt; RX Bandwidth=67 khz; VDI=Fast; DRSSI=-103 dB
SPI_MasterTransmit(0xC2AC); //Fiter=Digital; Recover Mode=Auto; Quality Threshold=4; Recovery Speed=Slow
SPI_MasterTransmit(0xCA80); //FIFO INT Level=8; Sync on=2;Fifo Fill Start=Sync; Reset; Sensitivity=High
//Disable:FIFO Fill Enabled
SPI_MasterTransmit(0xC483); //Enable: AFC Mode; AFC; Frequency Offset Register
//Disable: High Accuracy; Strobe
SPI_MasterTransmit(0x9820); //Frequenz Shift=POS; Power Out=0 dB; Deviation=45 khz
SPI_MasterTransmit(0xE000); //WakeUp timer = 0ms
SPI_MasterTransmit(0xC800); //Duty Cycle = Infinity % OFF
SPI_MasterTransmit(0xC000); //Low Battery = 2,2V; Clock Pin = 1Mhz
SPI_MasterTransmit(0xCED4); //Synchron Pattern
SPI_MasterTransmit(0xCC57); //PLL Settings


SPI_MasterTransmit(0x82C8); //Empfänger aktivieren
SPI_MasterTransmit(0xCA83); //FIFO einschalten
_delay_ms(200);
SPI_MasterTransmit(0x0000); //Status zurückstellen
return;
}


int main(void)
{
//-----------------------------------------RFM 12------------------------------------------------------

SPI_MasterInit(); //SPI als Master konfigurieren
RFM12_init(); //RFM 12 initialisieren

_delay_ms(200); //200ms Warten

// USART - Serielle Schnittstelle

UCSRB |= (1<<TXEN); // USART - Transmitter einschalten

// - Baudrate auf 2400bps einstellen
UBRRH = 0x01; // UBRRH mit Baudrate für 2400bps (Bits 8 - 11) -416
UBRRL = 0xA0; // UBRRL mit Baudrate für 2400bps (Bits 0 - 7)



PORTB &= ~(1<<PB4); //nSel auf 0 -> RFM12 wird selektiert
_delay_us(100); //100µs warten

while(1)
{
if(PB6 == 1)
{
SPI_MasterTransmit(0xb000);
while (!(UCSRA & (1<<UDRE))) // Warten bis die Daten des letzten Vorganges fertig verarbeitet sind
{

}
UDR = SPDR; // Daten übertragen
//HIER VERMUTE ICH EINEN FEHLER...WIE LESE ICH DEN FIFO RICHTIG AUS?

}
}
}


VIELEN DANK und Liebe Grüsse,
Stefan

Spectre
02.01.2014, 08:24
Hallo Stefan,

Ich bin mir ziemlich sicher, dass dies nicht dein Problem auslöst, aber mir ist in deinem Sender-Programm-Auszug folgendes aufgefallen. Du verwendest für die Variable i um festzustellen, ob eine gewisse Zeitspanne vergangen ist. In der Hauptschleife des Programms fragst du dann unter Verwendung des == Operators ab, ob diese Variable einen gewissen Wert (in dem gezeigten Codebeispiel den Wert 61) beträgt. Da du die Variable i in der Hauptschleife liest, aber im Interrupt beschreibst könnte es beim Auslesen zu einer sogenannten Race-Condition (http://de.wikipedia.org/wiki/Race_Condition) kommen. Dadurch kann ein falscher Wert ausgelesen und im worst case der Fall i = 61 überhaupt nicht oder erst verspätet eintreten. Um die Race-Condition zu verhinden gibt es zwei Möglichkeiten:

1) Temporäres Abschalten der Interrupts beim Auslesen der Variable i (Am Besten)


while(1)
{
cli(); // disable interrupts
int const tmp_i = i;
sei(); // enable interrupts

if(tmp_i==61)
{
// ... do stuff

Dadurch wird die Race-Condition verhindert.

2) Den >= Operator verwenden (Besser als nichts)


while(1) {

if(i>=61)
{
// ... do stuff

Damit werden die Effekte der Race-Condition vermindert. Dennoch sollte man Variante 1 bevorzugen.

Im Empfänger-Programm ist mir aufgefallen, dass du die nSEL-Leitung vor der Hauptschleife auf '0' setzt.


// - Baudrate auf 2400bps einstellen
UBRRH = 0x01; // UBRRH mit Baudrate für 2400bps (Bits 8 - 11) -416
UBRRL = 0xA0; // UBRRL mit Baudrate für 2400bps (Bits 0 - 7)



PORTB &= ~(1<<PB4); //nSel auf 0 -> RFM12 wird selektiert
_delay_us(100); //100µs warten

while(1)

Die nSEL Leitung ist beim RFM12B meines Wissens nach aber kein Eingang, mit dem der RFM12B an sich ein- bzw. ausgeschalten wird sondern dient zur Aus/Anwahl des RFM12B während der SPI-Kommunikation mit dem RFM12B. Notwendig wird dies z.B. in einem System, in dem mehrere SPI-Slaves mit dem Microcontroller verbunden sind. Die Auswahl, mit welchem SPI-Slave der Mikrocontroller-SPI-Master kommunizieren will, erfolgt dann über das Setzen der "nSEL"-Leitung des jeweiligen SPI-Slave.

Und beim weiteren Analysieren des Empfängers ist mir noch aufgefallen, dass du nur dann versuchst, etwas aus dem RFM12B auszulesen, wenn die Bedingung PB6 == 1 erfüllt ist. Abgesehen davon, dass ich den Sinn der Bedingung nicht verstehe (Denn PB6 = MISO = Eingang der SPI-Schnittstelle am SPI-Master und sollte daher mit SDO des RFM12B verbunden sein - Wie soll die Abfrage ob dieser Pin = 1 ist damit zusammenhängen, ob ein Byte empfangen wurde?) sehe ich noch ein ganz anderes Problem: PB6 ist in der Datei iom32.h so definiert:


#define PB6 6

Damit übersetzt sich


if(PB6 == 1)

zu


if(6 == 1)

Diese Bedingung kann niemals wahr sein, daher wird der zugehörige Code nie ausgeführt und keine Daten über die serielle Schnittstelle versendet. Wenn du wirklich abfragen willst, ob am Pin 6 von PORTB der Wert 1 anliegt so funktioniert das so:


if(PINB & (1<<PB6))

Hoffe, dir damit weitergeholfen zu haben. Viel Erfolg ;)