NRF24L01P通信
这几天一直在调试nrf24l01p的通信,终于调通了:
控制器使用的是上次活动时买的烈火四轴飞行器的遥控器:
由于采用了freeRTOS系统,采用任务调度方式,同时管脚多,一粗心设错了就调不通。在小车上就因为SPI的中断调用函数放错了位置,用了很多时间才找出来。今天把代码贴上,省得以后再重复调。下面是遥控器的代码,每秒发送一次hello消息。有些函数用不多,例如receivePacket等,写上以备后用。
/*
* nrf24l01.c: nRF24L01(-p) TX mode low level driver
*/
/* TODO:
* - Separate the SPI and GPIO driver from here.
* - Handle TX mode
*/
#include "stdio.h"
#include <stdbool.h>
#include <string.h>
#include "nrf24l01.h"
#include "stm32f10x_conf.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_spi.h"
#include "stm32f10x_exti.h"
/*FreeRtos includes*/
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "queue.h"
/* Defines for the SPI and GPIO pins used to drive the SPI Flash */
#define RADIO_GPIO_CS GPIO_Pin_13
#define RADIO_GPIO_CS_PORT GPIOC
#define RADIO_GPIO_CS_PERIF RCC_APB2Periph_GPIOC
#define RADIO_GPIO_CE GPIO_Pin_14
#define RADIO_GPIO_CE_PORT GPIOC
#define RADIO_GPIO_CE_PERIF RCC_APB2Periph_GPIOC
#define RADIO_GPIO_IRQ GPIO_Pin_15
#define RADIO_GPIO_IRQ_PORT GPIOC
#define RADIO_GPIO_IRQ_PERIF RCC_APB2Periph_GPIOC
#define RADIO_GPIO_IRQ_SRC_PORT GPIO_PortSourceGPIOC
#define RADIO_GPIO_IRQ_SRC GPIO_PinSource15
#define RADIO_GPIO_IRQ_LINE EXTI_Line15
#define RADIO_SPI SPI2
#define RADIO_SPI_CLK RCC_APB1Periph_SPI2
#define RADIO_GPIO_SPI_PORT GPIOB
#define RADIO_GPIO_SPI_SCK GPIO_Pin_13
#define RADIO_GPIO_SPI_MISO GPIO_Pin_14
#define RADIO_GPIO_SPI_MOSI GPIO_Pin_15
#define NVIC_RADIO_PRI 13
/* Registers address definition */
#define REG_CONFIG 0x00 // 'Config' register address
#define REG_EN_AA 0x01 // 'Enable Auto Acknowledgment' register address
#define REG_EN_RXADDR 0x02 // 'Enabled RX addresses' register address
#define REG_SETUP_AW 0x03 // 'Setup address width' register address
#define REG_SETUP_RETR 0x04 // 'Setup Auto. Retrans' register address
#define REG_RF_CH 0x05 // 'RF channel' register address
#define REG_RF_SETUP 0x06 // 'RF setup' register address
#define REG_STATUS 0x07 // 'Status' register address
#define REG_OBSERVE_TX 0x08 // 'Observe TX' register address
#define REG_RPD 0x09 // 'Carrier Detect' register address
#define REG_RX_ADDR_P0 0x0A // 'RX address pipe0' register address
#define REG_RX_ADDR_P1 0x0B // 'RX address pipe1' register address
#define REG_RX_ADDR_P2 0x0C // 'RX address pipe2' register address
#define REG_RX_ADDR_P3 0x0D // 'RX address pipe3' register address
#define REG_RX_ADDR_P4 0x0E // 'RX address pipe4' register address
#define REG_RX_ADDR_P5 0x0F // 'RX address pipe5' register address
#define REG_TX_ADDR 0x10 // 'TX address' register address
#define REG_RX_PW_P0 0x11 // 'RX payload width, pipe0' register address
#define REG_RX_PW_P1 0x12 // 'RX payload width, pipe1' register address
#define REG_RX_PW_P2 0x13 // 'RX payload width, pipe2' register address
#define REG_RX_PW_P3 0x14 // 'RX payload width, pipe3' register address
#define REG_RX_PW_P4 0x15 // 'RX payload width, pipe4' register address
#define REG_RX_PW_P5 0x16 // 'RX payload width, pipe5' register address
#define REG_FIFO_STATUS 0x17 // 'FIFO Status Register' register address
#define REG_DYNPD 0x1C
#define REG_FEATURE 0x1D
#define VAL_RF_SETUP_250K 0x26
#define VAL_RF_SETUP_1M 0x06
#define VAL_RF_SETUP_2M 0x0E
#define VAL_SETUP_AW_3B 1
#define VAL_SETUP_AW_4B 2
#define VAL_SETUP_AW_5B 3
/* nRF24L SPI commands */
#define CMD_R_REG 0x00
#define CMD_W_REG 0x20
#define CMD_R_RX_PAYLOAD 0x61
#define CMD_W_TX_PAYLOAD 0xA0
#define CMD_FLUSH_TX 0xE1
#define CMD_FLUSH_RX 0xE2
#define CMD_REUSE_TX_PL 0xE3
#define CMD_ACTIVATE 0x50
#define CMD_RX_PL_WID 0x60
#define CMD_W_ACK_PAYLOAD(P) (0xA8|(P&0x0F))
#define CMD_W_PAYLOAD_NO_ACK 0xD0
#define CMD_NOP 0xFF
#define ACTIVATE_DATA 0x73
#define DUMMY_BYTE 0xA5
#define RADIO_PACKET_SIZE 32
#define RADIO_CONNECTED_TIMEOUT (2000)
/*** Defines ***/
#define RADIO_RATE_250K 0
#define RADIO_RATE_1M 1
#define RADIO_RATE_2M 2
/* Usefull macro */
#define RADIO_EN_CS() GPIO_ResetBits(RADIO_GPIO_CS_PORT, RADIO_GPIO_CS)
#define RADIO_DIS_CS() GPIO_SetBits(RADIO_GPIO_CS_PORT, RADIO_GPIO_CS)
#define RADIO_DIS_CE() GPIO_ResetBits(RADIO_GPIO_CE_PORT, RADIO_GPIO_CE)
#define RADIO_EN_CE() GPIO_SetBits(RADIO_GPIO_CE_PORT, RADIO_GPIO_CE)
/* Synchronisation */
xSemaphoreHandle dataRdy;
xQueueHandle txQueue;
xQueueHandle rxQueue;
typedef struct _radioPacket
{
char raw[RADIO_PACKET_SIZE];
} radioPacket;
static uint32_t lastPacketTick;
static bool radioIsInit;
/***********************
* SPI private methods *
***********************/
static char spiSendByte(char byte)
{
/* Loop while DR register in not emplty */
while (SPI_I2S_GetFlagStatus(RADIO_SPI, SPI_I2S_FLAG_TXE) == RESET);
/* Send byte through the SPI1 peripheral */
SPI_I2S_SendData(RADIO_SPI, byte);
/* Wait to receive a byte */
while (SPI_I2S_GetFlagStatus(RADIO_SPI, SPI_I2S_FLAG_RXNE) == RESET);
/* Return the byte read from the SPI bus */
return SPI_I2S_ReceiveData(RADIO_SPI);
}
static char spiReceiveByte()
{
return spiSendByte(DUMMY_BYTE);
}
/****************************************************************
* nRF SPI commands, Every commands return the status byte *
****************************************************************/
/* Read len bytes from a nRF24L register. 5 Bytes max */
unsigned char nrfReadReg(unsigned char address, char *buffer, int len)
{
unsigned char status;
int i;
RADIO_EN_CS();
/* Send the read command with the address */
status = spiSendByte( CMD_R_REG | (address&0x1F) );
/* Read LEN bytes */
for(i=0; i<len; i++)
buffer[i]=spiReceiveByte();
RADIO_DIS_CS();
return status;
}
/* Write len bytes a nRF24L register. 5 Bytes max */
unsigned char nrfWriteReg(unsigned char address, char *buffer, int len)
{
unsigned char status;
int i;
RADIO_EN_CS();
/* Send the write command with the address */
status = spiSendByte( CMD_W_REG | (address&0x1F) );
/* Write LEN bytes */
for(i=0; i<len; i++)
spiSendByte(buffer[i]);
RADIO_DIS_CS();
return status;
}
/* Write only one byte (useful for most of the reg.) */
unsigned char nrfWrite1Reg(unsigned char address, char byte)
{
return nrfWriteReg(address, &byte, 1);
}
/* Read only one byte (useful for most of the reg.) */
unsigned char nrfRead1Reg(unsigned char address) {
char byte;
nrfReadReg(address, &byte, 1);
return byte;
}
/* Sent the NOP command. Used to get the status byte */
unsigned char nrfNop()
{
unsigned char status;
RADIO_EN_CS();
status = spiSendByte(CMD_NOP);
RADIO_DIS_CS();
return status;
}
unsigned char nrfFlushRx()
{
unsigned char status;
RADIO_EN_CS();
status = spiSendByte(CMD_FLUSH_RX);
RADIO_DIS_CS();
return status;
}
unsigned char nrfFlushTx()
{
unsigned char status;
RADIO_EN_CS();
status = spiSendByte(CMD_FLUSH_TX);
RADIO_DIS_CS();
return status;
}
// Return the payload length
unsigned char nrfRxLength(unsigned int pipe)
{
unsigned char length;
RADIO_EN_CS();
spiSendByte(CMD_RX_PL_WID);
length = spiReceiveByte();
RADIO_DIS_CS();
return length;
}
unsigned char nrfActivate()
{
unsigned char status;
RADIO_EN_CS();
status = spiSendByte(CMD_ACTIVATE);
spiSendByte(ACTIVATE_DATA);
RADIO_DIS_CS();
return status;
}
// Write the ack payload of the pipe 0
unsigned char nrfWriteTx(char *buffer, int len)
{
unsigned char status;
int i;
RADIO_EN_CS();
/* Send the read command with the address */
status = spiSendByte(CMD_W_TX_PAYLOAD);
/* Read LEN bytes */
for(i=0; i<len; i++)
spiSendByte(buffer[i]);
RADIO_DIS_CS();
return status;
}
// Write the ack payload of the pipe 0
unsigned char nrfWriteAck(unsigned int pipe, char *buffer, int len)
{
unsigned char status;
int i;
printf("send: %s",buffer);
RADIO_EN_CS();
/* Send the read command with the address */
status = spiSendByte(CMD_W_ACK_PAYLOAD(pipe));
// status = spiSendByte(0xA8);
/* Read LEN bytes */
for(i=0; i<len; i++)
spiSendByte(buffer[i]);
RADIO_DIS_CS();
return status;
}
// Read the RX payload
unsigned char nrfReadRX(char *buffer, int len)
{
unsigned char status;
int i;
RADIO_EN_CS();
/* Send the read command with the address */
status = spiSendByte(CMD_R_RX_PAYLOAD);
/* Read LEN bytes */
for(i=0; i<len; i++)
buffer[i]=spiReceiveByte();
RADIO_DIS_CS();
return status;
}
void nrfSetChannel(unsigned int channel)
{
if (channel<126)
nrfWrite1Reg(REG_RF_CH, channel);
}
void nrfSetDatarate(int datarate)
{
switch(datarate)
{
case RADIO_RATE_250K:
nrfWrite1Reg(REG_RF_SETUP, VAL_RF_SETUP_250K);
break;
case RADIO_RATE_1M:
nrfWrite1Reg(REG_RF_SETUP, VAL_RF_SETUP_1M);
break;
case RADIO_RATE_2M:
nrfWrite1Reg(REG_RF_SETUP, VAL_RF_SETUP_2M);
break;
}
}
void nrfSetAddress(unsigned int pipe, char* address)
{
int len = 5;
// ASSERT(pipe<6);
if (pipe > 1)
len = 1;
nrfWriteReg(REG_RX_ADDR_P0 + pipe, address, len);
}
void nrfSetEnable(bool enable)
{
if (enable)
{
RADIO_EN_CE();
}
else
{
RADIO_DIS_CE();
}
}
unsigned char nrfGetStatus()
{
return nrfNop();
}
bool nrfInterruptActive(void)
{
return (GPIO_ReadInputDataBit(RADIO_GPIO_IRQ_PORT, RADIO_GPIO_IRQ) == Bit_RESET);
}
/* Initialisation */
void nrfInit(void)
{
SPI_InitTypeDef SPI_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable SPI and GPIO clocks */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO |RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC,ENABLE);
/* Enable SPI and GPIO clocks */
RCC_APB1PeriphClockCmd(RADIO_SPI_CLK, ENABLE);
/* Configure SPI pins: SCK, MOSI, MISO */
GPIO_InitStructure.GPIO_Pin = RADIO_GPIO_SPI_SCK
|RADIO_GPIO_SPI_MOSI| RADIO_GPIO_SPI_MISO;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(RADIO_GPIO_SPI_PORT, &GPIO_InitStructure);
/* Configure I/O for the Chip select */
GPIO_InitStructure.GPIO_Pin = RADIO_GPIO_CS;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(RADIO_GPIO_CS_PORT, &GPIO_InitStructure);
/* Configure I/O for the Chip Enable */
GPIO_InitStructure.GPIO_Pin = RADIO_GPIO_CE;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(RADIO_GPIO_CE_PORT, &GPIO_InitStructure);
/* Enable SYSCFG clock */
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
GPIO_InitStructure.GPIO_Pin = RADIO_GPIO_IRQ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(RADIO_GPIO_IRQ_PORT, &GPIO_InitStructure);
/* SPI configuration */
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(RADIO_SPI, &SPI_InitStructure);
GPIO_EXTILineConfig(RADIO_GPIO_IRQ_SRC_PORT, RADIO_GPIO_IRQ_SRC);
EXTI_InitStructure.EXTI_Line = RADIO_GPIO_IRQ_LINE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
/* disable the chip select */
RADIO_DIS_CS();
/* disable the chip enable */
RADIO_DIS_CE();
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = NVIC_RADIO_PRI;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Enable the SPI */
SPI_Cmd(RADIO_SPI, ENABLE);
}
u8 Nrf24l01_Check(void)
{
char buf1[5]={0};
char radioAddress[5] = {0xE1, 0xE2, 0xE3, 0xE4, 0xE5};
u8 i;
nrfWriteReg(0x30,radioAddress,5);
nrfReadReg(REG_TX_ADDR,buf1,5);
for(i=0;i<5;i++)
{
if(buf1[i]!=radioAddress[i])
break;
}
if(i==5)
return SUCCESS ;
else
return ERROR ;
}
//not used in TX mode
void nrfInterruptHandler(void)
{
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
if(EXTI_GetITStatus(RADIO_GPIO_IRQ_LINE) != RESET)
{
//To unlock RadioTask
xSemaphoreGiveFromISR(dataRdy, &xHigherPriorityTaskWoken);
if(xHigherPriorityTaskWoken == pdTRUE)
portYIELD();
EXTI_ClearITPendingBit(RADIO_GPIO_IRQ_LINE);
}
}
/* Crazyflie */
void nrfConfigure(void)
{
int i;
char radioAddress[5] = {0xE1, 0xE2, 0xE3, 0xE4, 0xE5};
//Set radio address
nrfSetAddress(0, radioAddress);
//Set the radio channel
nrfSetChannel(40);
//Set the radio data rate 2M
nrfSetDatarate(2);
nrfWrite1Reg(REG_EN_RXADDR,0x01);//1
nrfWrite1Reg(REG_EN_AA,0x01);
//address length
nrfWrite1Reg(REG_SETUP_AW,3);
nrfWriteReg(REG_TX_ADDR,radioAddress,5);
nrfWrite1Reg(REG_SETUP_RETR,0x0);
nrfWrite1Reg(REG_STATUS,0xfe);
//Power the radio, Enable the DS interruption, set the radio in PRX mode
nrfWrite1Reg(REG_CONFIG, 0x0e);
// Enable the dynamic payload size and the ack payload for the pipe 0
nrfWrite1Reg(REG_FEATURE, 0x06);
nrfWrite1Reg(REG_DYNPD, 0x01);
//Flush TX
for(i=0;i<3;i++)
nrfFlushTx();
printf("radiolink initNRF24L01 over\r\n");
}
void radiolinkTask(void *param)
{
unsigned char dataLen;
static radioPacket p;
while (1)
{
// xSemaphoreTake(dataRdy, portMAX_DELAY);
lastPacketTick = xTaskGetTickCount();
nrfSetEnable(false);
//Fetch all the data (Loop until the RX Fifo is NOT empty)
while( !(nrfRead1Reg(REG_FIFO_STATUS)&0x01) )
{
dataLen = nrfRxLength(0);
if (dataLen>RADIO_PACKET_SIZE) //If a packet has a wrong size it is dropped
nrfFlushRx();
else //Else, it is processed
{
nrfReadRX(p.raw, dataLen);
xQueueSend( rxQueue, &p, 0);
}
}
//Push the data to send (Loop until the TX Fifo is full or there is no more data to send)
while( (uxQueueMessagesWaiting((xQueueHandle)txQueue) > 0) && !(nrfRead1Reg(REG_FIFO_STATUS)&0x20) )
{
xQueueReceive(txQueue, &p, 0);
nrfWriteAck(0,p.raw, RADIO_PACKET_SIZE);
printf("nrfWriteAck over\r\n");
}
//clear the interruptions flags
nrfWrite1Reg(REG_STATUS, 0x70);
//Re-enable the radio
nrfSetEnable(true);
}
}
void radiolinkTest(void)
{
if( Nrf24l01_Check() == SUCCESS){
printf("Nrf24l01 Check returns SUCCESS\r\n");
}else{
printf("Nrf24l01 Check returns ERROR\r\n");
}
return;
}
/*
* Public functions,
* finish all the nrf24l01 iniaialization.
*/
void radiolinkInit()
{
if(radioIsInit)
return;
/* Initialise the semaphores */
vSemaphoreCreateBinary(dataRdy);
/* Queue init */
txQueue = xQueueCreate(3, sizeof(radioPacket));
rxQueue = xQueueCreate(3, sizeof(radioPacket));
nrfInit();
radiolinkTest();
nrfConfigure();
/* Start Tx tasks */
xTaskCreate(radiolinkTask, (const signed char *)"radiolinkTask",
configMINIMAL_STACK_SIZE, NULL, /*priority*/2, NULL);
vTaskSetApplicationTaskTag(0, (pdTASK_HOOK_CODE)TASK_RADIO_ID_NBR);
radioIsInit = true;
}
void radiolinkReInit(void)
{
if (!radioIsInit)
return;
nrfConfigure();
}
/*public functions*/
int sendPacket(unsigned char *p)
{
xQueueSend(txQueue, p, 0);
return 0;
}
int receivePacket(unsigned char * pk)
{
xQueueReceive( rxQueue, pk, portMAX_DELAY);
return 0;
}
void radioTestTask(void *param)
{
unsigned char buff[RADIO_PACKET_SIZE] = "Hello,this is controller\r\n";
while(1)
{
vTaskDelay(1000);//send a hello message one time in a second
sendPacket(buff);
}
}