有了前面的SPI例程,我们再来看一个实际应用SPI的例子。NRF24L01是工作在2.4GHz公共频段的射频芯片,其接口是SPI的,虽然kinetis的SPI有自动片选功能,但每发完一串比特流(本例中是1字节)片选信号要变成一次无效电平,NRF24L01要求同一命令和数据中间片选信号要一直有效,故本例中用GPIO来做片选。NRF24L01+模块只要芯片是NRF24L01程序上就兼容。笔者用的是这种:
//main.c
/*
* main implementation: use this 'C' sample to create your own application
*
*/
#include <stdio.h>
#include "derivative.h" /* include peripheral declarations */
#include "NRF24L01.h"
/*-----------------------------------------------------------------------------
Global Defines
------------------------------------------------------------------------------*/
unsigned char send_dat;
unsigned char send_flag=0;
void init_CPU (void);
void delayms(unsigned short dly);//当dly=1时,延时的时间是1ms 4MHz晶震
void INIT_io(void);
void RX_Mode(void);
void TX_Mode(void);
//------------------------------------------------------
unsigned char t20ms;
void GPIO_Init()
{
SIM_SCGC5 |= SIM_SCGC5_PORTA_MASK;
PORTA_PCR26=(PORT_PCR_MUX(1));
PORTA_PCR27=(PORT_PCR_MUX(1));
PORTA_PCR28=(PORT_PCR_MUX(1));
PORTA_PCR29=(PORT_PCR_MUX(1));
GPIOA_PDDR = (1<<26) | (1<<27) | (1<<28) | (1<<29);
}
void PIT0_init(void)
{
SIM_SCGC6 |= 0x00800000;
PIT_MCR = 0x00;
PIT_TCTRL0 = 0x00;
PIT_TFLG0 = 0x01;
PIT_LDVAL0 = 10000000;
NVICISER2 |= 0x10;//使能中断NVICISERn=m,其中n=84/32,m=84%32
PIT_TCTRL0 = 0x03;
}
void delay(void)//
{
unsigned int i;
for(i=0 ; i<200 ; i++);
}
void delayms(unsigned short time)//
{
SIM_SCGC6 |= 0x00800000;
PIT_MCR = 0x00;
PIT_TCTRL1 = 0x00;
PIT_TFLG1 = 0x01;//清中断标识
PIT_LDVAL1 = 20000 *time;
PIT_TCTRL1 = 0x01;//开始计数
while(PIT_TFLG1 == 0);//未溢出时一直等待
PIT_TFLG1 = 0x01;//清中断标识
PIT_TCTRL1 = 0;
}
int main(void)
{
unsigned char tmp;
GPIO_Init();
NRF24L01_init();
delayms(200);
PIT0_init();
RX_Mode();
send_flag=0;
printf("Hello (Kinetis) World in 'C' from MK60DX256Z derivative! \n\r");
for(;;)
{
if(send_flag==1)
{
Buffer[0]=80;
Buffer[1]=02;
Buffer[2]=send_dat;
TX_Mode(); // set TX Mode and transmitting
send_flag=0;
}
}
return 0;
}
void PIT_CH0_ISR(void)
{
static unsigned int val=1;
PIT_TFLG0 = 1;
GPIOA_PDOR = val << 26;
send_dat = (unsigned char)val;
val = val << 1;
if(val == 0x10)
{
val = 1;
}
send_flag = 1;
}
//nrf24l01.c
#include "derivative.h" /* include peripheral declarations */
#include "NRF24L01.h"
unsigned char TX_ADDRESS[TX_ADR_WIDTH] = {0x34,0x43,0x10,0x10,0x01}; // Define a static TX address
unsigned char Buffer[]={
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
};
//------------------------------------------------------
/************************SPI初始化函数*******************************/
void SPI_Init(void) {
SIM_SCGC5 |= SIM_SCGC5_PORTB_MASK;
SIM_SCGC6 |= SIM_SCGC6_SPI1_MASK;
PORTB_PCR17 = PORT_PCR_MUX(2);
PORTB_PCR16 = PORT_PCR_MUX(2);
PORTB_PCR11 = PORT_PCR_MUX(2);
//PORTB_PCR9 = PORT_PCR_MUX(2);
SPI1_MCR = SPI_MCR_ROOE_MASK | SPI_MCR_PCSIS(2) | SPI_MCR_HALT_MASK;//0x01020001
// SPI1_MCR: MSTR=1,CONT_SCKE=0,DCONF=0,FRZ=0,MTFE=0,PCSSE=0,ROOE=1,??=0,??=0,PCSIS=2,DOZE=0,MDIS=0,DIS_TXF=1,DIS_RXF=1,CLR_TXF=1,CLR_RXF=1,SMPL_PT=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,HALT=1
SPI1_MCR = SPI_MCR_MSTR_MASK | SPI_MCR_ROOE_MASK | SPI_MCR_PCSIS(2) | SPI_MCR_DIS_TXF_MASK | SPI_MCR_DIS_RXF_MASK | SPI_MCR_CLR_TXF_MASK | SPI_MCR_CLR_RXF_MASK | SPI_MCR_HALT_MASK; //Set Configuration register
// SPI1_CTAR0: DBR=1,FMSZ=7,CPOL=0,CPHA=0,LSBFE=0,PCSSCK=1,PASC=1,PDT=1,PBR=1,CSSCK=0,ASC=0,DT=0,BR=0x0A
SPI1_CTAR0 = SPI_CTAR_DBR_MASK | SPI_CTAR_FMSZ(7) | SPI_CTAR_PCSSCK(1) | SPI_CTAR_PASC(1) | SPI_CTAR_PDT(1) | SPI_CTAR_PBR(1) | SPI_CTAR_CSSCK(0x00) | SPI_CTAR_ASC(0x00) | SPI_CTAR_DT(0x0) | SPI_CTAR_BR(0x0A); //Set Clock and Transfer Attributes register
// SPI1_SR: TCF=1,TXRXS=0,??=0,EOQF=1,TFUF=1,??=0,TFFF=1,??=0,??=0,??=0,??=1,??=0,RFOF=1,??=0,RFDF=1,??=0,TXCTR=0,TXNXTPTR=0,RXCTR=0,POPNXTPTR=0
SPI1_SR = SPI_SR_TCF_MASK | SPI_SR_EOQF_MASK | SPI_SR_TFUF_MASK | SPI_SR_TFFF_MASK | SPI_SR_RFOF_MASK | SPI_SR_RFDF_MASK | SPI_SR_TXCTR(0) | SPI_SR_TXNXTPTR(0) | SPI_SR_RXCTR(0) | SPI_SR_POPNXTPTR(0); //Clear flags
// SPI1_RSER: TCF_RE=0,??=0,??=0,EOQF_RE=0,TFUF_RE=0,??=0,TFFF_RE=0,TFFF_DIRS=0,??=0,??=0,??=0,??=0,RFOF_RE=0,??=0,RFDF_RE=0,RFDF_DIRS=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0
SPI1_RSER = 0; //Set DMA Interrupt Request Select and Enable register
// SPI1_MCR: HALT=0
SPI1_MCR &= ~SPI_MCR_HALT_MASK;
}
/**************************************************
Function: SPI_RW();
Description:
Writes one byte to nRF24L01, and return the byte read
from nRF24L01 during write, according to SPI protocol */
/**************************************************/
unsigned char SPI_RW(unsigned char dat)
{
unsigned char tmp;
{
SPI1_PUSHR = SPI_PUSHR_PCS(2) | dat;
while((SPI1_SR&SPI_SR_TFFF_MASK) == 0);
while((SPI1_SR&SPI_SR_RFDF_MASK) == 0);
tmp =SPI1_POPR;
SPI1_SR = SPI_SR_TFFF_MASK | SPI1_SR&SPI_SR_RFDF_MASK;
}
return(tmp); // return read byte
}
/**************************************************
Function: SPI_RW_Reg();
Description:
Writes value 'value' to register 'reg' */
/**************************************************/
unsigned char SPI_RW_Reg(unsigned char reg, unsigned char value)
{
unsigned char status;
unsigned char tmp;
nRF24L01_CSNL;
delay();
status = SPI_RW(reg); // select register
tmp = SPI_RW(value); // ..and write value to it..
nRF24L01_CSNH;
delay();
return(status); // return nRF24L01 status byte
}
/**************************************************
Function: SPI_Read();
Description:
Read one byte from nRF24L01 register, 'reg' */
/**************************************************/
unsigned char SPI_Read(unsigned char reg)
{
unsigned char reg_val;
unsigned char tmp;
nRF24L01_CSNL;
delay();
tmp = SPI_RW(reg); // Select register to read from..
reg_val = SPI_RW(0); // ..then read registervalue
nRF24L01_CSNH;
delay();
return(reg_val); // return register value
}
/**************************************************
Function: SPI_Read_Buf();
Description:
Reads 'bytes' #of bytes from register 'reg'
Typically used to read RX payload, Rx/Tx address */
/**************************************************/
unsigned char SPI_Read_Buf(unsigned char reg, unsigned char *pBuf, unsigned char bytes)
{
unsigned char status,byte_ctr;
nRF24L01_CSNL;
delay();
status = SPI_RW(reg); // Select register to write to and read status byte
for(byte_ctr=0;byte_ctr<bytes;byte_ctr++)
pBuf[byte_ctr] = SPI_RW(0); // Perform SPI_RW to read byte from nRF24L01
nRF24L01_CSNH;
delay();
return(status); // return nRF24L01 status byte
}
/**************************************************
Function: SPI_Write_Buf();
Description:
Writes contents of buffer '*pBuf' to nRF24L01
Typically used to write TX payload, Rx/Tx address */
/**************************************************/
unsigned char SPI_Write_Buf(unsigned char reg, unsigned char *pBuf, unsigned char bytes)
{
unsigned char status,byte_ctr;
nRF24L01_CSNL; // Set nRF24L01_CSN low, init SPI tranaction
delay();
status = SPI_RW(reg); // Select register to write to and read status byte
for(byte_ctr=0; byte_ctr<bytes; byte_ctr++) // then write all byte in buffer(*pBuf)
status = SPI_RW(*pBuf++);
nRF24L01_CSNH; // Set nRF24L01_CSN high again
delay();
return(status); // return nRF24L01 status byte
}
/**************************************************
Function: RX_Mode();
Description:
This function initializes one nRF24L01 device to
RX Mode, set RX address, writes RX payload width,
select RF channel, datarate & LNA HCURR.
After init, CE is toggled high, which means that
this device is now ready to receive a datapacket. */
/**************************************************/
void RX_Mode(void)
{
unsigned char tmp;
nRF24L01_CEL;
delay();
tmp = SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH); // Use the same address on the RX device as the TX device
tmp = SPI_RW_Reg(WRITE_REG + EN_AA, 0x01); // Enable Auto.Ack:Pipe0
tmp = SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01); // Enable Pipe0
tmp = SPI_RW_Reg(WRITE_REG + RF_CH, 40); // Select RF channel 40
tmp = SPI_RW_Reg(WRITE_REG + RX_PW_P0, TX_PLOAD_WIDTH); // Select same RX payload width as TX Payload width
tmp = SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07); // TX_PWR:0dBm, Datarate:2Mbps, LNA:HCURR
tmp = SPI_RW_Reg(WRITE_REG + CONFIG, 0x0f); // Set PWR_UP bit, enable CRC(2 bytes) & Prim:RX. RX_DR enabled..
nRF24L01_CEH;
delay();
// This device is now ready to receive one packet of 16 bytes payload from a TX device sending to address
// '3443101001', with auto acknowledgment, retransmit count of 10, RF channel 40 and datarate = 2Mbps.
}
/**************************************************/
/**************************************************
Function: TX_Mode();
Description:
This function initializes one nRF24L01 device to
TX mode, set TX address, set RX address for auto.ack,
fill TX payload, select RF channel, datarate & TX pwr.
PWR_UP is set, CRC(2 bytes) is enabled, & PRIM:TX.
One high pulse(>10us) on CE will now send this
packet and expext an acknowledgment from the RX device. */
/**************************************************/
void TX_Mode(void)
{
unsigned char tmp;
nRF24L01_CEL;
delay();
tmp = SPI_Write_Buf(WRITE_REG + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH); // Writes TX_Address to nRF24L01
tmp = SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH); // RX_Addr0 same as TX_Adr for Auto.Ack
tmp = SPI_Write_Buf(WR_TX_PLOAD, Buffer, TX_PLOAD_WIDTH); // Writes data to TX payload
tmp = SPI_RW_Reg(WRITE_REG + EN_AA, 0x01); // Enable Auto.Ack:Pipe0
tmp = SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01); // Enable Pipe0
tmp = SPI_RW_Reg(WRITE_REG + SETUP_RETR, 0x1a); // 500us + 86us, 10 retrans...
tmp = SPI_RW_Reg(WRITE_REG + RF_CH, 40); // Select RF channel 40
tmp = SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07); // TX_PWR:0dBm, Datarate:2Mbps, LNA:HCURR
tmp = SPI_RW_Reg(WRITE_REG + CONFIG, 0x0e); // Set PWR_UP bit, enable CRC(2 bytes) & Prim:TX. MAX_RT & TX_DS enabled..
nRF24L01_CEH;
delay();
}
void NRF24L01_init()
{
//初始化CE、IRQ对应的GPIO
SIM_SCGC5 |= SIM_SCGC5_PORTB_MASK;
PORTB_PCR9=PORT_PCR_MUX(1);
PORTB_PCR18=PORT_PCR_IRQC(0x0A) | PORT_PCR_MUX(1);
PORTB_PCR19=PORT_PCR_MUX(1);
GPIOB_PDDR |= (1<<9) | (1<<19);
SPI_Init();
nRF24L01_CEL;
delay();
NVICISER2 |= 1<<24;//使能中断NVICISERn=1<<m,其中n=88/32,m=88%32
}
void gpiob_isr(void)
{
unsigned char tmp;
tmp=SPI_Read(STATUS); // read register STATUS's value
if(tmp&RX_DR)//接收完成中断
SPI_Read_Buf(RD_RX_PLOAD,Buffer,TX_PLOAD_WIDTH);// read renRF24L01_CEive payload from RX_FIFO buffer
if(tmp&MAX_RT) //达到最多次重发中断
SPI_RW_Reg(FLUSH_TX,0);
tmp = SPI_RW_Reg(WRITE_REG+STATUS,0xff);// clear RX_DR or TX_DS or MAX_RT interrupt flag
if( (tmp&TX_DS)!=0 )//发送完成中断
{
RX_Mode();
}
PORTB_ISFR = 1<<18;//清中断
}
//nrf24l01.h
#define nRF24L01_CSNH (GPIOB_PSOR=1<<9)
#define nRF24L01_CSNL (GPIOB_PCOR=1<<9)
#define nRF24L01_CEH (GPIOB_PSOR=1<<19)
#define nRF24L01_CEL (GPIOB_PCOR=1<<19)
//****************************************************************//
// SPI(nRF24L01) commands
#define READ_REG 0x00 // Define read command to register
#define WRITE_REG 0x20 // Define write command to register
#define RD_RX_PLOAD 0x61 // Define RX payload register address
#define WR_TX_PLOAD 0xA0 // Define TX payload register address
#define FLUSH_TX 0xE1 // Define flush TX register command
#define FLUSH_RX 0xE2 // Define flush RX register command
#define REUSE_TX_PL 0xE3 // Define reuse TX payload register command
//#define NOP 0xFF // Define No Operation, might be used to read status register
//***************************************************//
// SPI(nRF24L01) registers(addresses)
#define CONFIG 0x00 // 'Config' register address
#define EN_AA 0x01 // 'Enable Auto Acknowledgment' register address
#define EN_RXADDR 0x02 // 'Enabled RX addresses' register address
#define SETUP_AW 0x03 // 'Setup address width' register address
#define SETUP_RETR 0x04 // 'Setup Auto. Retrans' register address
#define RF_CH 0x05 // 'RF channel' register address
#define RF_SETUP 0x06 // 'RF setup' register address
#define STATUS 0x07 // 'Status' register address
#define OBSERVE_TX 0x08 // 'Observe TX' register address
#define CD 0x09 // 'Carrier Detect' register address
#define RX_ADDR_P0 0x0A // 'RX address pipe0' register address
#define RX_ADDR_P1 0x0B // 'RX address pipe1' register address
#define RX_ADDR_P2 0x0C // 'RX address pipe2' register address
#define RX_ADDR_P3 0x0D // 'RX address pipe3' register address
#define RX_ADDR_P4 0x0E // 'RX address pipe4' register address
#define RX_ADDR_P5 0x0F // 'RX address pipe5' register address
#define TX_ADDR 0x10 // 'TX address' register address
#define RX_PW_P0 0x11 // 'RX payload width, pipe0' register address
#define RX_PW_P1 0x12 // 'RX payload width, pipe1' register address
#define RX_PW_P2 0x13 // 'RX payload width, pipe2' register address
#define RX_PW_P3 0x14 // 'RX payload width, pipe3' register address
#define RX_PW_P4 0x15 // 'RX payload width, pipe4' register address
#define RX_PW_P5 0x16 // 'RX payload width, pipe5' register address
#define FIFO_STATUS 0x17 // 'FIFO Status Register' register address
#define MAX_RT 0x10 // Max #of TX retrans interrupt
#define TX_DS 0x20 // TX data sent interrupt
#define RX_DR 0x40 // RX data received
//-----------------------------------------------------------------------------
#define TX_ADR_WIDTH 5 // 5 bytes TX(RX) address width
#define TX_PLOAD_WIDTH 20 // 16 bytes TX payload
extern unsigned char TX_ADDRESS[TX_ADR_WIDTH]; // Define a static TX address
extern unsigned char Buffer[64];
void SPI_Init(void);
void NRF24L01_init(void);
unsigned char SPI_Write_Buf(unsigned char reg, unsigned char *pBuf, unsigned char bytes);
unsigned char SPI_Read_Buf(unsigned char reg, unsigned char *pBuf, unsigned char bytes);
unsigned char SPI_RW_Reg(unsigned char reg, unsigned char value);
unsigned char SPI_Read(unsigned char reg);
void clear_buf(unsigned char *ptr,unsigned char number);
void delay(void);