RFM70 aan AVR (tinny2313)

Lucky Luke

Golden Member

Beste mede-CO-ers (co-CO-ers?),

Ik ben bezig met een RFM70 module aan een Attiny2313 te hangen. Ik gebruik 2 AVRDB2313 bordjes en daar zit de module op aangesloten. AVR en module draaien op 3.3V.

Doel is om als test een led te togglen, aangesloten op de ene AVRDB2313, als een schakelaar, aangesloten op de andere, wordt ingedrukt. Als test. Daarna wil ik er data over gaan sturen.

Maar het werkt niet, de led toggled niet, en ik heb packetloss hoewel de tranceivers nog geen 10cm uit elkaar op m'n bureau liggen.

Ik gebruik de RFM70 C lib van voti:
http://www.voti.nl/rfm70/

Mijn rfm70-config.h

c code:


// Er zou hier een template voor zijn, maar ik heb het nog niet kunnen vinden. Dan maar uit de losse pols

// definiëren van de gevraagde delayroutines:
#include <util/delay.h>
#define RFM70_WAIT_US _delay_us // delayrotine voor 1 us
#define RFM70_WAIT_MS _delay_ms // delayroutine voor 1 ms
// ja, dat is lomp, maar #define is search and replace dus dit moet goed gaan.

//definiëren van de pinnen. Ik zou eigenlijk liever de bestaande file aanpassen om de USI te gebruiken
// (Hardware SPI), maar goed.
#define RFM70_PIN_DIRECTION DDRB|=0b11011000 //portb.7=sck, portb.6=mosi, portb.5=miso, portb.4=CSN, protb.3=CE. De rest is "vrij te gebruiken"
//uitgangen:
#define RFM70_CE(A) {if((A)==0)        \
					 PORTB&=~(1<<3); \
					 else            \
					 PORTB|=(1<<3);} 
#define RFM70_CSN(B) {if((B)==0)       \
					 PORTB&=~(1<<4); \
					 else            \
					 PORTB|=(1<<4);} 
#define RFM70_MOSI(C) {if((C)==0)      \
					 PORTB&=~(1<<6); \
					 else            \
					 PORTB|=(1<<6);} 
#define RFM70_SCK(D) {if((D)==0)       \
					 PORTB&=~(1<<7); \
					 else            \
					 PORTB|=(1<<7);} 


//Ingang:
#define RFM70_MISO ((PINB&(1<<5))>>5) // pak bit 5 maar schuif het naar bit 1, anders gaat er wat fout in de software SPI.

Let wel: dit is voor het eerst dat ik zo'n config file maak. Maar SPI communicatie met de RFM70 werkt wel.

En m'n code:

c code:


#define F_CPU 4000000
#include <avr/io.h>
#include "rfm70-c/rfm70.h"
#include "rfm70-c/rfm70.c"

const unsigned char p1_address[]={ 0xDE, 0xAF, 0xD0, 0x0D, 0x01 } ; // deaf dude 1
const unsigned char p0_address[]={ 0xC0, 0xFF, 0xEE, 0xBA, 0xBE } ; // coffee babe


int main(){
uint8_t i, length, pipe;
rfm70_buffer rxbuf, txbuf;

DDRD|=0x0F;//  pd.0: indicatieled aanwezigheid rfm70. pd.1: indicatieled ontvangst (toggle), pd2 indicatieled retransmit count
// pd3 indicatieled packetloss.
PORTB=0xFF; // pull-ups op portb (voor geval bordje met switches niet is aangesloten)
 

rfm70_init();
rfm70_retransmit_delay_attempts(0,15); //korste retransmit delay (250 ms?), 15 pogingen. (init zou 'm op traagste zetten)
rfm70_receive_address_p1(p1_address); // init zet P0 en P1 op gelijk adres, mag dat? 
rfm70_receive_address_p0(p0_address); // 
rfm70_transmit_address(p0_address); // P0 en transmit moeten gelijk adress hebben anders werkt ack niet.

i = rfm70_is_present();
	if(i==0) PORTD|=(1<<0); else PORTD&=~(1<<0); //indicatieledje is active low en brand als rfm70 aanwezig is.

	while(1){
		if((PINB&(1<<0))==0) {
		txbuf[0]=1;
		rfm70_mode_transmit();
		rfm70_transmit_message(txbuf,RFM70_MAX_PACKET_LEN);
		// niet direct omschakelen naar receive mode: ding moet kans krijgen te zenden
			do{
			_delay_ms(500);
			}while((PINB&(1<<0))==0);
		rfm70_mode_receive();
		}
	i=rfm70_receive(&pipe,rxbuf,&length); // ontvangen van rfm70.
		if(i==0){ // kan in 1 if, maar zo uitgeschreven om in debugger te kunnen zien wat er gebeurt. 
		// i is niet leesbaar in wathcwindow, maar zo kan ik het toch zien.
		// PORTD^=(1<<2) ; // toggle rode led (even uitgecomment want gebruik die led nu voor wat anders)
		}else if(rxbuf[0]==1){ // bij gelukte ontvangst van een 1:
			PORTD^=(1<<1); // toggle LED
			_delay_ms(500); // ter vertraging zodat LED ook zichbaar aan/uit gaat.
		}
	i=rfm70_retransmit_count();
		if(i>10){
		PORTD&=~(1<<2);  // retransmit count over 10 LED, active LOW.
		_delay_ms(500);
		}else{
		PORTD|=(1<<2);
		}
	i=rfm70_lost_packets_count();
		if(i!=0){
		PORTD&=~(1<<3);  // lost packets LED, active LOW.
		_delay_ms(500);
		rfm70_lost_packets_reset();
		}else{
		PORTD|=(1<<3);
		}

	}
}
/*  Note: ontvangen lukt niet, i blijft 0 bij i=rfm70_receive(&pipe,rxbuf,&length);
Retransmit count gaat >10 en er zijn lost packets al liggen de tranceivers naast elkaar
Intermiterend werkt het WEL  (PD1 op ene bordje toggled dan na laag maken PB0 op andere bordje)
*/

Het stomme is dus dat het héél af en toe wél werkt (met dezelfde code), maar nooit lang.

Zou een van jullie hier met een frisse blik naar willen kijken?
Ik zit me blind te staren en zie de fout niet.

Eluke.nl | De mens onderscheid zich van (andere) dieren door o.a. complexe gereedschappen en bouwwerken te maken. Mens zijn is nerd zijn. Blijf Maken. (Of wordt, bijvoorbeeld, cultuurhistoricus)

Ik heb me eerst scheef gestaard op de fouten in de documentatie en voorbeeldcode van de fabrikant voordat ik helemaal overnieuw ben begonnen. Heb nu een library gemaakt. Werkt prima. Voorbeeldcode is 1598 bytes op de ATmega328p, misschien een beetje aan de grote kant voor de Attiny2313. http://critiacrof.nl/?page=rfm70

Lucky Luke

Golden Member

Thanks, ga er naar kijken. En die attiny zit bij mij nu ook 70-80% vol oid.

Zonder levelconversie leveren de modules stroom terug naar hun voeding. Hierdoor kan de voedingsspanning van de modules oplopen tot 4,2 Volt.

Dat had ik dus ook, daarom draait bij de hele boel nu op 3,3V. Want hoewel de I/O van die RFM70 5V tolerant zou zijn mag de voeding maar tot 3,9V stijgen.

Eluke.nl | De mens onderscheid zich van (andere) dieren door o.a. complexe gereedschappen en bouwwerken te maken. Mens zijn is nerd zijn. Blijf Maken. (Of wordt, bijvoorbeeld, cultuurhistoricus)
Lucky Luke

Golden Member

Goed, ik ben hier weer mee doorgegaan, met de library van critacrof. Die neemt minder ruimte in in m'n attiny dan de libs van voti :) Wel enigszinds moeten aanpassen omdat de attiny andere SPI hardware heeft (USI vs echte SPI hardware).

Maar ik loop nu tegen een stel hele weirde problemen aan. Zie commentaarregels:

c code:


#define F_CPU 4000000ULL//for util/delay.h
#include "RFM70.h"
#include <avr/io.h>         //for register addresses and bit names of AVR
#include <stdint.h>         //for uint8_t
#include <util/delay.h>     //for _delay_ms()

// const uint8_t p0_address[]={ 0xC0, 0xFF, 0xEE, 0xBA, 0xBE } ; // coffee babe

uint8_t debugvar=0, buff[32]; // staan deze global dan gaat memcpy fout (in rf70.C Send_Packet)
// maar staan ze local, dan geeft Packet_Received(); steeds 0 terug...
// Prog ik de "zender" met deze vars local en de "ontvanger" met 
// deze vars global dan werkt het. In die richting dan. Weird!?
// Het wordt nog leuker: als "cofee babe" local staat gaat het wel goed met buff global... 
// Alleen toggled portd^=(1<<2) dan ook portd.0 en 1 (En ook dat slechts soms), en soms loopt de attiny "vast".
int main(){
uint8_t p0_address[]={ 0xC0, 0xFF, 0xEE, 0xBA, 0xBE } ; // coffee babe
// uint8_t debugvar=0, buff[32];

DDRD = 0xff; // portD = outputs
PORTD = 0xff; // en allemaal hoog (LED's uit)
PORTB|=(1<<0); // pullup op portb.0
buff[0]=0; // Ging dat niet vanzelf? Anyway, zolang het maar geen 5 is.

	if(RFM70_Initialize(0,p0_address)){ // init RFM70. Kanaal0, adres p0_adress. indien gelukt lus uitvoeren
	debugvar = 1;
	PORTD&=~(1<<0); // LED 0 aan
	}

	while(1){
		if((PINB&(1<<0))==0){ // indien switch ingegdrukt
		buff[0]=5;
			if(Send_Packet(buff,32)) PORTD&=~(1<<1); // transmit, led1 aan wanneer succesvol
			while((PINB&(1<<0))==0) _delay_ms(200); // wacht tot switch losgelaten is
		PORTD|=(1<<1); // led weer uit
		Select_RX_Mode(); // en weer terug naar ontvangstmodus (send_packet schakelt vanzelf naar zendmodus)
		}
		if(Packet_Received()){ 
			buff[0]=0;
			Receive_Packet(&buff[0]);
			if(buff[0]==5){
			PORTD^=(1<<2); // toggle led2
			Select_RX_Mode(); // flush rx buffer
			buff[0]=0; // ook op de uc.
			}
		}
	}


}

Er is geen pijl op te trekken, soms als ik dezelfde code opnieuw programmeer gedraagt het zich anders...

Als p0_addres const en global staat, en buff[32] staat ook global, dan gaat memcpy fout: zowel buff als payload krijgen "gekke waardes" (alles 0xDB bijvoorbeeld).

Staat buff local, dan gaat memcpy goed, maar krijg ik geen pakketten binnen. Ook als ik naar de SPI communicatie kijk zegt de RFM70 letterlijk dat er geen data is binnengekomen. Terwijl het verzenden op de andere module wel succesvol is (Alleen niet de goede data omdat memcpy fout gaat).

Zet ik op 1 module de code met buff global en gebruik ik die als ontvanger, en op de andere module buff local en gebruik ik die als zender, dan kan ik vanaf de zender de led op de ontvanger togglen zonder probelemen.

Als ik nog meer andere dingen probeer worden mijn comments zelfs voor mijzelf onontcijferbaar, en het gedrag van de microcontroller al helemaal, maar het enige wat ik doe is die 3 vars (debugvar, p0_adress en buff[32]) verplaatsen van binnen naar buiten de mainloop of andersom, en al dan niet static (op vast adress) of volatile (altijd opnieuw uit ram lezen en niet optimizen).

Soms gaat opeens portd.0 en portd.1 meetogglen als ik portd.2 toggle (bij succesvolle ontvangst), en als ik daarna de schakelaar indruk en probeer te zenden lijkt de attiny vast te lopen. (Hij zal nog wel lopen, maar duidelijk niet meer mijn code).

Doe ik soms iets fout met die pointers her en der? De compiler geeft ook de warning: passing argument 2 of 'RFM70_Initialize' discards qualifiers from pointer target type. (warning is weg bij 2e keer compilen of als ik p0_address als variabele zet, dus zonder const)Maar als ik meekijken met de SPI dan zie ik daar keurig "COFFEEBABE"... Dus het gaat wel goed.

Maak ik buff volatile dan krijg ik ook de volgende warnings:
warning: passing argument 1 of 'Send_Packet' discards qualifiers from pointer target type
warning: passing argument 1 of 'Receive_Packet' discards qualifiers from pointer target type

Zwaar frustrerend dit...

Eluke.nl | De mens onderscheid zich van (andere) dieren door o.a. complexe gereedschappen en bouwwerken te maken. Mens zijn is nerd zijn. Blijf Maken. (Of wordt, bijvoorbeeld, cultuurhistoricus)
EricP

mét CE

Vaag gedrag duidt meestal op stack problemen. Vooral in Send_Packet wordt er even een berg op de stack gemikt. 32 byes voor je payload. Je had al 32 bytes voor je buffer. Gaat best hard dan op een tiny2313...

Voor die pointers: kwestie van goed uitzoeken wat je nou eigenlijk doet. Is niet spannend, maar op de gok ergens wat neer mikken, kijken of het bouwt en dan kijken of het ook runt is niet de juiste weg.

Lucky Luke

Golden Member

Ik kan zelfs pd0 laten togglen ipv pd2.

Pointers heb ik nagezien en zou moeten kloppen, maar ik zou wat over het hoofd kunnen zien. (ik doe zelfs &buff[0] waar buff ook al het adres van het 0e element van de array op levert om duidelijker te maken dat ik een adres aan het doorgeven ben)

Stack... Dat is een goeie, want AVR's waarschuwen niet als stack botst met gewoon RAM. (Ram groeit van boven naar onder, stack omgekeerd, en als ze in het midden elkaar tegenkomen gaat dat gewoon fout zonder waarschuwing)

Als ik die lib nou 's aanpas dat 'ie niet gaat kopieren, maar gewoon de al bestaande array uitleest, dat scheelt 32 bytes ram (1 array minder) en 32 bytes stack. Uhm, Dat is de helft van het hele ram...

[Bericht gewijzigd door Lucky Luke op zondag 26 februari 2012 13:58:50 (12%)

Eluke.nl | De mens onderscheid zich van (andere) dieren door o.a. complexe gereedschappen en bouwwerken te maken. Mens zijn is nerd zijn. Blijf Maken. (Of wordt, bijvoorbeeld, cultuurhistoricus)
EricP

mét CE

Stack... Dat is een goeie, want AVR's waarschuwen niet als stack botst met gewoon RAM.

Stack is niks anders dan gewoon RAM...

Overigens is het een beetje inherent aan C. Je *zou* linktime nog wat kunnen doen (compiletime gaat dat natuurlijk niet werken). Sommige compilers (met linker) doen dat ook. Normaal heb je natuurlijk wel in de gaten of het kritisch is of niet. Maar met dergelijk variabelen gebruik en onvoorspelbaar gedrag, is het mijn eerste insteek, zonder de code goed bekeken te hebben.

Verder ben ik van mening dat code warning-vrij moet compileren. Hoe dan ook. Als er warnings in zitten doe je vrijwel altijd wat verkeerd.
Het kan zijn dat je warnings maar 1x ziet als je meerdere keren compileert. Da's logisch, want alleen nog niet bestaande (of outdated) object files worden opnieuw gebouwd. Een 'full build' of hoe het ook heten mag (ik zou zeggen: make clean) bouwt ook die files opnieuw... Met die warnings.

Verder ben ik ook niet zo'n fan van void pointers. Je ontneemt de compiler de mogelijkheid om typechecks te doen. Als je de code op een 8051 zet, wordt het opeens erg 'duur' omdat hetgeen die pointer aanwijst in ROM, IDATA of XDATA kan zitten. Dat wordt in een runtime lib opgelost (omdat je er andere assembly voor nodig hebt).

Lucky Luke

Golden Member

Ja, wat ik bedoel te zeggen: Stack en variabelen zitten in hetzelfde stuk RAMgeheugen, waarbij de variabelen vanaf het laagste naar het hoogste adres worden geplaatst en de stack vanaf het hoogste adres naar het laagste adres groeit. (variabelen kunnen ook in een van de 32 general purpose working registers zitten, maar ik bedoelde natuurlijk variabelen in RAM.)

Anyway, ik heb nu iets dat werkt. (Door de library aan te passen en ipv de buffer te kopieren een pointer naar de bestaande buffer mee te geven. Kost gek genoeg meer ram (.data 108 bytes/84.4%), maar m'n stackpointer blijft boven de 200, dus geen botsingen meer) Dan wel met buff[32] global, zet ik 'm local dan werkt het weer niet. Moet ik nog 's naar kijken... (Maar voor nu ben ik blij te weten dat iig die RFM70 moduletjes nog heel zijn ook al hebben ze 4,2V gehad... 5V tolerant I/O, me neus, ze clampen gewoon naar voeding. Misschien met een paar kΩ in serie...)

En warning vrij compilen doet het alleen als ik een constant adres (Wat ik ook niet verander) als variabele declareer. Dat lijkt me nu ook weer niet de bedoeling, ik hèb al RAM tekort. (Kan natuurlijk wel de library aanpassen dat 'ie ook constantes als adres lust)

Eluke.nl | De mens onderscheid zich van (andere) dieren door o.a. complexe gereedschappen en bouwwerken te maken. Mens zijn is nerd zijn. Blijf Maken. (Of wordt, bijvoorbeeld, cultuurhistoricus)
EricP

mét CE

En warning vrij compilen doet het alleen als ik een constant adres (Wat ik ook niet verander) als variabele declareer. Dat lijkt me nu ook weer niet de bedoeling, ik hèb al RAM tekort. (Kan natuurlijk wel de library aanpassen dat 'ie ook constantes als adres lust)

Geen zin om zelf in die code te duiken... Maar warnings zijn vrijwel altijd voorboden van andere ellende. Je moet je compiler (en de onderliggende assembly) verdomd goed kennen om te kunnen zeggen 'oh, laat maar, daar maakt-ie dit van en dat gaat goed'. Vrijwel nooit een optie dus...

Anyway... Succes. Wellicht toch een ATMega overwegen voor wat meer RAM? :)

Op 22 januari 2012 11:43:33 schreef Lucky Luke:
Maar het werkt niet, de led toggled niet, en ik heb packetloss hoewel de tranceivers nog geen 10cm uit elkaar op m'n bureau liggen.

Een opmerking die ik doorkreeg van een andere RFM70 gebruiker: voor zeer korte afstanden is het beter om de zender wat zwakker te zetten, danwel in de ontvanger de verzwakker aan te zetten. Anders wordt de ontvanger overstuurd en dat veroorzaakt slechte ontvangst (en dus 'pakket loss').

Wouter van Ooijen: VOTI webwinkel, docent HvU (Technische Informatica); C++ on mictrocontrollers blog

warning is weg bij 2e keer compilen

Mijn eerste ingeving is dan; niet goed geschoond. Ik kan me voorstellen dat je dan andere vage problemen krijgt, zeker als je met pointers en static memory locaties zit te "klooien".

Lucky Luke

Golden Member

Functie die het adres las/instelde aangepast: accepteert nu ook const.

Nu:
bool RFM70_Initialize(const uint8_t Channel,const uint8_t* Address)
Was:
bool RFM70_Initialize(uint8_t Channel, uint8_t* Address)

Op 26 februari 2012 14:56:34 schreef EricP:
Anyway... Succes. Wellicht toch een ATMega overwegen voor wat meer RAM? :)

[Evil mode on >:)]
Nee, dit is leerzaam.

Meer RAM is niet altijd de oplossing, ik vind dat je als programmeur ook op beperkte hardware goede software moet kunnen laten draaien. Anders krijgen we straks 64 bits quadcore broodroosters, lopend op Windows 2020... En brand de toast nog steeds aan.
[/evil mode off :)]

Op 26 februari 2012 14:02:40 schreef EricP:
Verder ben ik ook niet zo'n fan van void pointers. Je ontneemt de compiler de mogelijkheid om typechecks te doen. Als je de code op een 8051 zet, wordt het opeens erg 'duur' omdat hetgeen die pointer aanwijst in ROM, IDATA of XDATA kan zitten. Dat wordt in een runtime lib opgelost (omdat je er andere assembly voor nodig hebt).

Zo zat dat in die lib. Maargoed, niks houd me tegen dat aan te passen en dat heb ik nu ook gedaan.

De boel compiled nu warningvrij, werkt, geeft geen botsingen met stack (Heb in debugger stack pointer gevolgd), en ik heb er even een zipje van gemaakt voor wie nog meer een rfm70 aan een attiny2313 wil hangen.

www.uploadarchief.net/files/download/rfm70_critacroflibs.zip

Buffer global/local is ook opgelost: static local. Ik geef immers een pointer door, dan moet die bufferarray niet opeens opgeruimd worden... Dan krijg ik een dangling pointer probleem. Dus daarom moet 'ie ofwel global staan (zodat 'ie niet opgeruimd wordt), ofwel local maar met een briefje "niet opruimen".

Next up:
- Die RFM70 nuttig voor toepassen (Maar dat gaat wel lukken, want ik had daar al een plan voor voor ik hieraan begon ;))
- Eens zien of ik ook auto acknoledge aan de praat krijg, want critacrof gebruikt het niet. DONE!

(Nummer 2 daarvan is misschien niet eens nodig, ik krijg nog steeds prima data door over de afstand die ik nodig heb, met een vloer ertussen)

En @Wouter van Ooien: Korte afstand werkt ook prima :)

Eluke.nl | De mens onderscheid zich van (andere) dieren door o.a. complexe gereedschappen en bouwwerken te maken. Mens zijn is nerd zijn. Blijf Maken. (Of wordt, bijvoorbeeld, cultuurhistoricus)
Lucky Luke

Golden Member

Zo, auto-acknoledge werkt nu ook. Was best simpel: je moet het gewoon niet enablen op p1 als je alleen p0 gebruikt... (i.t.t. tot de standaard libs). Achteraf erg logisch ook.

RFM70 zijn mooie dingen, als het eenmaal werkt...

Link in vorige post is aangepast.

Eluke.nl | De mens onderscheid zich van (andere) dieren door o.a. complexe gereedschappen en bouwwerken te maken. Mens zijn is nerd zijn. Blijf Maken. (Of wordt, bijvoorbeeld, cultuurhistoricus)

Mooi gedaan hoor! Veel efficienter zo. Auto-acknowledge gebruikte ik niet, omdat ik toch zelf een protocol had dat informatie heen en weer zond, vond het dus makkelijker om het weg te laten, maar daardoor is de verbinding minder betrouwbaar, dus mooi dat je het er in hebt gedaan.

Lucky Luke

Golden Member

Dank je :)

Autoretransmit levert trouwens ook wel problemen op: als max_rt geset is (Dus als het maximumaantal retransmissions heeft plaatsgevonden) moet die eerst gecleared worden voor er weer gecommuniceerd kan worden. (staat ook in datasheet). Niet dat dat moeilijk is:

c code:


// voeg dit toe aan rfm70.C:
void Clear_Max_RT(){
	SPI_Write_Register(STATUS,(1<<4));  //clear MAX_RT interrupt flag
}
// (En een prototype aan rfm70.h)

Anyway, ik heb nu werkende draadloze communicatie.

Wie de libs in de door mij aangepaste vorm gebruikt kan me in dit topic altijd vragen stellen. waarschijnlijk geef ik zelfs antwoord.

Eluke.nl | De mens onderscheid zich van (andere) dieren door o.a. complexe gereedschappen en bouwwerken te maken. Mens zijn is nerd zijn. Blijf Maken. (Of wordt, bijvoorbeeld, cultuurhistoricus)

Zou je één van jullie mischien kunnen helpen met het opstellen van de config file voor een pic 16f690, zoals in dit topic door mij uitgelegd kom ik daar namelijk niet uit.