Main Site Documentation

SPI help needed


#1

Guys,

I hope someone is able to help as I’m already trying to get this to work since 14 days. :’(

The SPI device in question is a HopeRF RFM12B transceiver.

The problem is that I only receive the first 2 bytes, which contains some flags, an ID and the length of the payload that should be coming in after these 2 bytes.

The datasheet mentions this regarding the SPI interface:

[quote]
Commands to the transmitter are sent serially. Data bits on pin SDI are shifted into the device upon the rising edge of the clock on
pin SCK whenever the chip select pin nSEL is low. When the nSEL signal is high, it initializes the serial interface. All commands
consist of a command code, followed by a varying number of parameter or data bits. All data are sent MSB first (e.g. bit 15 for a 16-bit command). Bits having no influence (dont care) are indicated with X. Special care must be taken when the microcontrollers built-in hardware serial port is used. If the port cannot be switched to 16-bit mode then a separate I/O line should be used to control the
nSEL pin to ensure the low level during the whole duration of the command or a software serial control interface should be
implemented.[/quote]

I’m a bit puzzled by the 16-bit mode. Is the SPI port on the USBizi in 16-bit mode by default or is there a setting I’m overlooking?

I’m initializing the SPI port as follows:


//In Main():
			rfm = new RFM12B_3(SPI.SPI_module.SPI1, (Cpu.Pin)FEZ_Pin.Digital.Di5, (Cpu.Pin)FEZ_Pin.Interrupt.Di6);
			rfm.OnDataReceived += new RFM12B_3.OnDataRecievedHandler(rfm_OnDataReceived);
			rfm.Init(31, Band.RF868MHZ, 4); //This nodeid=31 and group 4


//The Driver:
		public RFM12B_3(SPI.SPI_module spiModule, Cpu.Pin spiSelectPin, Cpu.Pin rfmInterruptPin)
		{
			spiConfig = new SPI.Configuration(spiSelectPin, false, 0, 0, false, true, 1000, spiModule);
			_spiPort = new SPI(spiConfig);
			_myinterruptPort = new InterruptPort(rfmInterruptPin, false, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeLow);
			_myinterruptPort.OnInterrupt += new NativeEventHandler(_interruptPort_OnInterruptRF12);
			_myinterruptPort.DisableInterrupt();
		}

		public void Init(uint nid, Band b, uint g)
		{
			group = g;
			nodeid = nid;
			usedband = b;
			InitModuleJee();
			reset();
		}

		private void reset()
		{
			RFXX_WRT_CMD(RF_STATUS_REG);
			RFXX_WRT_CMD(RF_RECEIVER_ON);//!er,!ebb,ET,ES,EX,!eb,!ew,DC 
			////Init FIFO   
			RFXX_WRT_CMD(RF_ENABLE_FIFO);
			RFXX_WRT_CMD(RF_CLEAR_FIFO);
		}

		private void InitModuleJee()
		{
			RFXX_WRT_CMD(RF_STATUS_REG);
			RFXX_WRT_CMD(RF_SLEEP_MODE);
			RFXX_WRT_CMD(RF_TXREG_WRITE);
			while (_myinterruptPort.Read() == false)
				RFXX_WRT_CMD(RF_STATUS_REG);
			ushort config = (ushort)(0x80C7 | ((int)usedband << 4)); 
			RFXX_WRT_CMD(config); // EL (ena TX), EF (ena RX FIFO), 12.0pF 
			RFXX_WRT_CMD(0xA640);
			RFXX_WRT_CMD(0xC606); // approx 49.2 Kbps, i.e. 10000/29/(1+6) Kbps
			RFXX_WRT_CMD(0x94A2); // VDI,FAST,134kHz,0dBm,-91dBm 
			RFXX_WRT_CMD(0xC2AC); // AL,!ml,DIG,DQD4
			if (group != 0)
			{
				RFXX_WRT_CMD(RF_CLEAR_FIFO); // FIFO8,2-SYNC,!ff,DR 
				RFXX_WRT_CMD((ushort)(0xCE00 | group)); // SYNC=2DXX; 
			}
			else
			{
				RFXX_WRT_CMD(0xCA8B); // FIFO8,1-SYNC,!ff,DR 
				RFXX_WRT_CMD(0xCE2D); // SYNC=2D; 
			}
			RFXX_WRT_CMD(0xC483); // @ PWR,NO RSTRIC,!st,!fi,OE,EN 0xC483
			RFXX_WRT_CMD(0x9850); // !mp,90kHz,MAX OUT 
			RFXX_WRT_CMD(0xCC77); // OB1,OB0, LPX,!ddy,DDIT,BW0 
			RFXX_WRT_CMD(0xE000); // NOT USE 
			RFXX_WRT_CMD(0xC800); // NOT USE 
			RFXX_WRT_CMD(0xC049); // 1.66MHz,3.1V 

			if ((nodeid & NODE_ID) != 0)
				_myinterruptPort.EnableInterrupt();
			else
				_myinterruptPort.DisableInterrupt();
		}

The Receiving part:


		void _interruptPort_OnInterruptRF12(uint data1, uint data2, DateTime time)
		{
			_myinterruptPort.DisableInterrupt();
			while (_myinterruptPort.Read() == false)  //We can only read the FIFO when the irq line is LOW
			{
				status = RFXX_WRT_CMD(RF_STATUS_REG);
				printstatus(status);
				HDR = (byte)(RFXX_WRT_CMD(RF_RX_FIFO_READ) & 0x00FF); //Contains CTL,DST,ACK and NodeID
				LEN = (byte)(RFXX_WRT_CMD(RF_RX_FIFO_READ) & 0x00FF);
				CTL = ((HDR >> 5) & 0x04) != 0;		//Control bit used with ACK
				DST = ((HDR >> 5) & 0x02) != 0;		//if DST is false, ID is sending Node ID, else target Node ID
				ACK = ((HDR >> 5) & 0x01) != 0;		//if sender wants an ACK
				NodeID = (HDR & RF12_HDR_MASK);					//Node ID
				payload = new byte[LEN];	//Buffer to recieve the data
				for (int x = 0; x < LEN; x++)
				{
					status = RFXX_WRT_CMD(RF_STATUS_REG);
					payload[x] = (byte)ReadBuffer2(); // (byte)(RFXX_WRT_CMD(RF_RX_FIFO_READ) & 0x00FF);
				}
				OnDataReceived(payload);
				reset();
			}
			_myinterruptPort.EnableInterrupt();
		}
		uint data_in;
		private ushort ReadBuffer2()
		{
				data_in = RFXX_WRT_CMD(0x0000);  //status register
				if ((data_in & 0x8000) != 0)        // if FIFO full, read a byte
				{
					data_in = RFXX_WRT_CMD(0xB000);
					return (ushort)(data_in & 0x00FF);
				}
				else
				{
					return 0;
				}
		}

		private void resetfifo()
		{
			RFXX_WRT_CMD(RF_ENABLE_FIFO);
			RFXX_WRT_CMD(RF_CLEAR_FIFO);
		}

		ushort[] _readBuf = new ushort[1];
		ushort[] _writeBuf = new ushort[1];
		private ushort RFXX_WRT_CMD(ushort cmd)
		{
		  _writeBuf[0] = cmd;
		  _spiPort.WriteRead(_writeBuf, _readBuf);
		  return (_readBuf[0]);
		}


The problem is that when i want to read the actual data (payload) it only returns 0’s.

I’m wondering if the SPI port is setup correctly? If it is, could i be looking here at a timing issue? Or is the 16-bit mode bugging me here? Or maybe something else I’m overlooking.

For completeness I’ll add a link to the transceiver’s datasheet: http://www.silabs.com/pages/DownloadDoc.aspx?FILEURL=Support%20Documents/TechnicalDocs/Si4421.pdf&src=DocumentationWebPart

Many thanks to those willing to take a look at this.

Greetings,
Eric


#2

If I corectly understand all comunication through SPI is done here:

ushort[] _readBuf = new ushort[1];
		ushort[] _writeBuf = new ushort[1];
		private ushort RFXX_WRT_CMD(ushort cmd)
		{
		  _writeBuf[0] = cmd;
		  _spiPort.WriteRead(_writeBuf, _readBuf);
		  return (_readBuf[0]);
		}

If it’s true, then You are in a “8bit” mode.

The problem here is that every WriteRead() trigers chipselect pin. If you want a continous read, than you need to define biger buffers:


		private ushort RFXX_WRT_CMD(ushort cmd, ushort bytesToRead)
		{
                                   ushort[] _readBuf = new ushort[ bytesToRead];
		ushort[] _writeBuf = new ushort[ bytesToRead];
                                    for(int i=0; i< bytesToRead;i++){
		  _writeBuf[i] = cmd;
                                    }
		  _spiPort.WriteRead(_writeBuf, _readBuf);
		  return (_readBuf[0]);
		}


It’s totaly untested, and can be with errors, but you should see the difference.


#3

wrong reply


#4

You have to double check the CS signal. some SPI devices requires that you toggle the CS on every byte/word and some of them require the toggling the CS per transaction.


#5

Joe,

[quote]If the port cannot be switched to 16-bit mode then a separate I/O line should be used to control the
nSEL pin to ensure the low level during the whole duration of the command[/quote]

I understand that it needs toggling per transaction, right?

If so, I tried Rimvis’s suggestion without any luck. All i get back are 0’s.

I also tried to use a separate nSel pin with the same result.

At this point, I’m clueless.


#6

Not really! It says you must not release SSEL within 16bits but it doesn’t say what you must or must not do outside the 16bit chunks.