SparkFun Forums 

Where electronics enthusiasts find answers.

Your source for all things Atmel.
By Lajon
#3288
Here are some functions I use for the RF-24G (shockburst mode) connected to an AVR with SPI. The SPI connection is described in:
http://www.nordicsemi.no/files/Product/ ... RF2401.pdf

The code is for avr-gcc. I use it with mega32 and mega48 parts (both at 8Mhz and with the SPI clock at 1Mhz).

The 'trf24g_config.h' file is where the wanted configuration and pins to use must be specified (the file below is what I use on a mega32).

/Lars


trf24g.h:
Code: Select all
/*
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 42):
 * Lars Jonsson (User name Lajon at www.avrfreaks.net) wrote this file.  
 * As long as you retain this notice you can do whatever you want with this
 * stuff. If we meet some day, and you think this stuff is worth it, you can
 * buy me a beer in return.   Lars Jonsson
 * ----------------------------------------------------------------------------
 *
 */
#ifndef TRF24G_H
#define TRF24G_H

#include <inttypes.h>
#include <stdbool.h>

extern void trf24gInit(void);
extern bool trf24gReceive(void *buf);
extern void trf24gSend(void *buf);
extern void trf24gReconfigure(uint8_t channel, bool rx);
#define TRF24G_TIME_ON_AIR ((1 + 3 * (1-TRF24G_1MBPS)) * 8 * \
	(1 + TRF24G_ADDR_SIZE + TRF24G_PAYLOAD_SIZE + TRF24G_CRC_SIZE))
#endif 
trf24g_config.h:
Code: Select all
/*
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 42):
 * Lars Jonsson (User name Lajon at www.avrfreaks.net) wrote this file.  
 * As long as you retain this notice you can do whatever you want with this
 * stuff. If we meet some day, and you think this stuff is worth it, you can
 * buy me a beer in return.   Lars Jonsson
 * ----------------------------------------------------------------------------
 *
 */
#ifndef TRF24G_CONFIG_H
#define TRF24G_CONFIG_H

#define TRF24G_CHANNEL 2
#define TRF24G_RX 0 // 0 or 1
#define TRF24G_ADDR_SIZE 3 // 1-5
#define TRF24G_ADDR1 0, 0,'L',0x55,0xAF // 5 bytes
#define TRF24G_RX2_EN 0 // 0 or 1
#define TRF24G_ADDR2  0,0,0,0,0 // 5 bytes
#define TRF24G_CRC_SIZE 2 // 0,1,2
#define TRF24G_PAYLOAD_SIZE 12
#define TRF24G_1MBPS 0 // 1== 1MBs 0==250Kbs

#define TRF24G_CONTROL_PORT  PORTD
#define TRF24G_CONTROL_DDR   DDRD
#define TRF24G_CS_MASK	  (1<<5)
#define TRF24G_CE_MASK	  (1<<4)

#define TRF24G_DR1_PIN	  PIND
#define TRF24G_DR1_MASK 	  (1<<2) 

#define TRF24G_SPI_DDR	  DDRB
#define TRF24G_SPI_SCK_MASK  (1<<7)
#define TRF24G_SPI_MOSI_MASK (1<<5)

#endif
trf24g.c:
Code: Select all
/*
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 42):
 * Lars Jonsson (User name Lajon at www.avrfreaks.net) wrote this file.  
 * As long as you retain this notice you can do whatever you want with this
 * stuff. If we meet some day, and you think this stuff is worth it, you can
 * buy me a beer in return.   Lars Jonsson
 * ----------------------------------------------------------------------------
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include "config.h"
#include <avr/delay.h>
#include "trf24g_config.h"


#define TRF24G_START_CONFIG() TRF24G_CONTROL_PORT |= TRF24G_CS_MASK; _delay_us(5)
#define TRF24G_END_CONFIG()   TRF24G_CONTROL_PORT &= (uint8_t)~TRF24G_CS_MASK;

#define TRF24G_ACTIVE()   TRF24G_CONTROL_PORT |= TRF24G_CE_MASK; _delay_us(5)
#define TRF24G_INACTIVE() TRF24G_CONTROL_PORT &= ~TRF24G_CE_MASK;

static uint8_t trf24gConfig[15] = {
	/*  0 */ TRF24G_PAYLOAD_SIZE * 8,	// DATA2_W
	/*  1 */ TRF24G_PAYLOAD_SIZE * 8,	// DATA1_W
	/*  2 */ 0,0,0,0,0,					// ADDR2 not used
	/*  7 */ TRF24G_ADDR1,				// ADDR1
	/* 12 */ ((TRF24G_ADDR_SIZE * 8) << 2) | 
	         ((TRF24G_CRC_SIZE == 0) ? 0b00 : ((TRF24G_CRC_SIZE == 1) ? 0b01 : 0b11)), 
	/* 13 */ (0 << 7) |					// RX2 active or not (not for now)
			 (1 << 6) | 				// Shockburst (always)
			 (TRF24G_1MBPS << 5) |		// Speed
			 (0b011 << 2) |         	// 16MHz always
			 0b11,                    	// Full power always
	/* 14 */ (TRF24G_CHANNEL << 1) | TRF24G_RX
 };
 #define ADDR1 7
 
static inline uint8_t spiSend(uint8_t d)
{
	SPDR = d;
	while(!(SPSR & (1<<SPIF)))
		;
	return SPDR;
}

void trf24gInit(void)
{
	uint8_t i;
	
	// DDR for trf24g control, CS and CE are outputs
	TRF24G_CONTROL_DDR |= TRF24G_CS_MASK|TRF24G_CE_MASK;
    // DDR for SPI, MOSI and SCK are outputs
	TRF24G_SPI_DDR |= TRF24G_SPI_MOSI_MASK|TRF24G_SPI_SCK_MASK;
	
	// SPI master, trailing edge setup 
	SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0); // /8 clock (8Mhz/8 = 1Mhz)
    SPSR |= (1<<SPI2X);
	
	//SPCR = (1<<SPE)|(1<<MSTR)|(0<<SPR0); // /4 clock (works also)
	
	// >3ms startup
	_delay_ms(3);
	TRF24G_START_CONFIG();
	for(i = 0; i < sizeof(trf24gConfig); i++) {
		spiSend(trf24gConfig[i]);
	}
	TRF24G_END_CONFIG();
#if TRF24G_RX
	TRF24G_ACTIVE();
#endif
}

void trf24gReconfigure(uint8_t channel, bool rx)
{
	uint8_t cfg = channel << 1;
	cfg |= rx;
	
	TRF24G_INACTIVE();
	TRF24G_START_CONFIG();
	spiSend(cfg);
	TRF24G_END_CONFIG();
	if (rx) {
		TRF24G_ACTIVE();
	}
}

bool trf24gReceive(void *p)
{	
	uint8_t dr = TRF24G_DR1_PIN & TRF24G_DR1_MASK;
	if (dr) {
		uint8_t n = TRF24G_PAYLOAD_SIZE;
		uint8_t *buf = (uint8_t*)p;
		do {
			*buf++ = spiSend(0);
		} while(--n);
	}
	return dr;
}

void trf24gSend(void *p)
{
	uint8_t n = TRF24G_ADDR_SIZE;
	uint8_t *buf = (uint8_t*)p;
	uint8_t *a = trf24gConfig + ADDR1 + 5 - TRF24G_ADDR_SIZE;
	
	TRF24G_ACTIVE();
	do {
		spiSend(*a++);
	} while(--n);
	n = TRF24G_PAYLOAD_SIZE;
	do {
		spiSend(*buf++);
	} while(--n); 
	TRF24G_INACTIVE();
}
By Lajon
#3600
Important note from davids@oz.net about SPI configuration:
On the Mega8 at least you have to set the !SS pin as an output or put an external pullup on it. If you don't when you set the SPI master mode if !SS ever goes low the chip will drop out of master mode (a bus collision mechanisum). By setting it an output you avoid this problem.
/Lars
By k_cumhur
#5319
I couldn't get this project to compile with Win AVR. Where is config.h?
By Lajon
#5441
Yes, I forgot to make a comment about config.h. It is where I place a define like:
Code: Select all
#define F_CPU 8E6
which is needed by the avr-libc delay functions. You can provide that define in the makefile also if you prefer (i.e., a -DF_CPU=<your cpu freq here> added to CFLAGS).

/Lars
User avatar
By SebaS
#14144
I've got a doubt about (:wink:) the code. I'm not very familiar with C programming.
What I don't now is the meaning of:
Code: Select all
cfg=channel<<1
The problem is not with that instruction, but with the operand (<<). What does it mean?It's a shift?
THX
User avatar
By SebaS
#14145
Hi, It's me again!!

Lajon: colud you explian me what you do in "trf24gSend". I can't understand it... above all because of the variables with the *

And then you transmit *a++ . What's that??:?: (you increase *a and transmit it?? why do you increase *a??)

Hope I'm not bothering.

Thanks again
User avatar
By leon_heller
#14147
SebaS wrote:I've got a doubt about (:wink:) the code. I'm not very familiar with C programming.
What I don't now is the meaning of:
Code: Select all
cfg=channel<<1
The problem is not with that instruction, but with the operand (<<). What does it mean?It's a shift?
THX
Yes, it's standard C for a left shift.

Leon
User avatar
By leon_heller
#14148
SebaS wrote:Hi, It's me again!!

Lajon: colud you explian me what you do in "trf24gSend". I can't understand it... above all because of the variables with the *

And then you transmit *a++ . What's that??:?: (you increase *a and transmit it?? why do you increase *a??)

Hope I'm not bothering.

Thanks again
You need to learn C, try reading K&R.

Leon
By jimlake
#15536
Lars,

It will not compile for me. I get the following:

vr-gcc -g -Wall -Os -mmcu=atmega32 -Wl -o trf24g.elf trf24g.o
C:/WinAVR/bin/../lib/gcc/avr/3.4.3/../../../../avr/lib/avr5/crtm32.o(.init9+0x0): undefined reference to `main'
make.exe: *** [trf24g.elf] Error 1

> Process Exit Code: 2

I can not find the undefined reference to 'main'. This is just beyond my understanding of gcc and its libraries. Do you know?
User avatar
By leon_heller
#15543
You haven't provided a main function.

Leon
By Lajon
#15546
Indeed, the code above does not provide a main program. It can be very simple if you just plan to have a one way transmisson. So a sender main() can look like this:
Code: Select all
   trf24gInit(); 
   while(1) { 
       char buffer[TRF24G_PAYLOAD_SIZE]; 
       /* put something in the message buffer here */ 
       trf24gSend(buffer); 
       _delay_ms((TRF24G_TIME_ON_AIR + 999) / 1000); /* round up to ms */ 
   } 

The sender trf24g_config.h should have:
#define TRF24G_RX 0

A receiver main() would be:
Code: Select all
    trf24gInit(); 
    while(1) { 
        char buffer[TRF24G_PAYLOAD_SIZE]; 
        if (trf24gReceive(buffer)) { 
            /* Do something with the message here */ 
        } 
    } 

The receiver trf24g_config.h should have:
#define TRF24G_RX 1
and the rest of the configuration defines must match the sender (except the PORT, PIN, DDR and mask #defines which must match the hardware).

/Lars
By jimlake
#15554
I see. Now, it makes sense.

JEL
By wiml
#15563
Sebas: *a++ increments a and transmits *a. It's a post-increment so it trasmits *a and then increments a to point to the next thing. *++a would increment a and then transmit the thing a points to after the increment.

This only works because ++ has a higher precedence than *.
By jimlake
#16464
Lars,

First, let me thank you for writing and distributing this excellent code for the 2401. Studying it has been an education in C programming. Once I understood it, I have set out to adapt it to my application. In so doing I have run into a perplexing problem that I hoped you might be willing to hazard an opinion on.

My application is a remote controller. It repeatedly sends two 8 bit bytes that represent the position of seven switches on the controller. The transmitter uses a tiny2313 running code that bit bangs the data to the MiRF. It works fine. The receiever uses an M32. I wrote code for the receiver using a bit banging method that also works. However, I wanted to switch to using your SPI driven software because I want two way capability.

The problem is this. When I put your software into the M32 in receive mode only (my code is driving the transmitter) I see my data appear on MISO. However, the voltage of the individual bits is 100mv instead of 3.3v. All circuit components have a stable 3.3v vcc. When I look at the clock, it is 3.3v, same for CS, CE, DR1 and the configuration word when it goes from the MOSI to the MiRF via the SPI. MISO is also 3.3 volts between successive bytes. However, when I look at the individual data bits on my DSO, they are 100mv. If I put my original code for the receiver back into the very same circuit, the data looks normal. This must have something to do with the SPI, but I cannot figure out what is causing it.

The circuit consists of the power supply, the M32 and the MiRF and a MAX3232. I have driven SS high externally. MISO and MOSI are both connected to the DATA pin without resistors. CS and CE are connected to PD4 and PD5. CLK is connected to PB7, DR1 to PD2. I am running WINAVR GCC with AVRStudio 4.12 and JTAGICE

I have a DSO screen shot that shows what I have described, but I can't post it.

Any ideas of what is happening would be greatly appreciated.

Jim Lake
By jimlake
#16483
I have determined that the problem is caused by connecting MOSI and MISO to the DATA pin. By disconnecting MOSI after the config is done, I get normal data. Clearly, they need to be isolated. I added the pair of resistors suggested by Nordic, and I get some strangely shaped data, but I think that is caused by capacitance on the breadboard.