小小的一个nRF24L01,折腾了好几天
真是捉急
还好,到今天简单的通信功能已经实现了,但是还不是很满意
目前实现了单向的通信,双向通信还需要进一步修改程序流程
其实nRF24L01挺耐玩的,有空再好好研究一下一对多的通信方式
不说了,上测试的代码
接收端配置
/************************************************************* **函数名: SPI_NRF_MOD_RX() **描述 : 配置NRF进入 接收模式 **参数 : **返回值: **注意 : 仅用于测试 无参无返 可改成带配置参数的模块函数 RX通道0 地址TX_ADDR=0xB3B4B5B605 ************************************************************/ void SPI_NRF_MOD_RX(void) { u8 TX_Array[5]; u8 _TX_RX_ADDR_[5]={0xB3,0xB4,0xB5,0xB6,0x05}; SPI2_CE_LOW();//CE=0 待机模式 TX_Array[0]=0x01;//允许接收通道0 SPI_NRF_Write(SPI2,W_REGISTER+EN_RXADDR,TX_Array,1); TX_Array[0]=0x03;//设置地址宽度 11--5字节 10--4字节 01-3字节 00--不合法 SPI_NRF_Write(SPI2,W_REGISTER+SETUP_AW,TX_Array,1); TX_Array[0]=0x02;//射频通道 X000 0010 SPI_NRF_Write(SPI2,W_REGISTER+RF_CH,TX_Array,1); TX_Array[0]=0x0f;//射频参数寄存器 SPI_NRF_Write(SPI2,W_REGISTER+RF_SETUP,TX_Array,1); TX_Array[0]=0x01;//xx11 1111 0通道允许自动应答 SPI_NRF_Write(SPI2,W_REGISTER+EN_AA,TX_Array,1); TX_Array[0]=0x20;//xx11 1111 数据通道0 32字节通信有效数据宽度 SPI_NRF_Write(SPI2,W_REGISTER+RX_PW_P0,TX_Array,1); TX_Array[0]=0xfe;//1111 xxxx STATUS寄存器 写‘1’清除所有标志 SPI_NRF_Write(SPI2,W_REGISTER+STATUS,TX_Array,1); SPI_NRF_Write(SPI2,W_REGISTER+TX_ADDR,_TX_RX_ADDR_,5);//写入接收发送数据的地址,这个地址是接收端收件的凭证 SPI_NRF_Write(SPI2,W_REGISTER+RX_ADDR_P0,_TX_RX_ADDR_,5);//写入接收发送数据的地址,这个地址是接收端收件的凭证 TX_Array[0]=0x0f;//接收模式 PRIM_RX=1 PWR_UP=1 允许接收终端 SPI_NRF_Write(SPI2,W_REGISTER+CONFIG,TX_Array,1); SPI2_CE_HIGH();//CE=1 使能接收模式 delay_us(200);//CE拉高
发射端配置
/************************************************************* **函数名: SPI_NRF_MOD_TX() **描述 : 配置NRF进入 发送模式 **参数 : **返回值: **注意 : 仅用于测试 无参无返 可改成带配置参数的模块函数 ************************************************************/ void SPI_NRF_MOD_TX(void) { u8 TX_Array[5]; u8 _TX_RX_ADDR_[5]={0xB3,0xB4,0xB5,0xB6,0x05}; SPI1_CE_LOW();//CE=0 待机模式 TX_Array[0]=0x03;//设置地址宽度 11--5字节 10--4字节 01-3字节 00--不合法 SPI_NRF_Write(SPI1,W_REGISTER+SETUP_AW,TX_Array,1); TX_Array[0]=0x1a;//建立自动重发 SPI_NRF_Write(SPI1,W_REGISTER+SETUP_RETR,TX_Array,1); TX_Array[0]=0x02;//射频通道 X000 0010 SPI_NRF_Write(SPI1,W_REGISTER+RF_CH,TX_Array,1); TX_Array[0]=0x0f;//射频参数寄存器 SPI_NRF_Write(SPI1,W_REGISTER+RF_SETUP,TX_Array,1); TX_Array[0]=0x01;//xx11 1111 接收通道0允许 SPI_NRF_Write(SPI1,W_REGISTER+EN_RXADDR,TX_Array,1); TX_Array[0]=0x01;//xx11 1111 通道0允许自动应答 SPI_NRF_Write(SPI1,W_REGISTER+EN_AA,TX_Array,1); TX_Array[0]=0x20;//xx11 1111 32字节数据长度 SPI_NRF_Write(SPI1,W_REGISTER+RX_PW_P0,TX_Array,1); SPI_NRF_Write(SPI1,W_REGISTER+TX_ADDR,_TX_RX_ADDR_,5);//写入接收发送数据的地址,这个地址是接收端收件的凭证 SPI_NRF_Write(SPI1,W_REGISTER+RX_ADDR_P0,_TX_RX_ADDR_,5);//写入接收发送数据的地址,这个地址是接收端收件的凭证 TX_Array[0]=0x0e;//中断全开 发送模式 PRIM_RX=0 PWR_UP=1 SPI_NRF_Write(SPI1,W_REGISTER+CONFIG,TX_Array,1); TX_Array[0]=0xfe;//1111 xxxx STATUS寄存器 写‘1’清除所有标志 SPI_NRF_Write(SPI1,W_REGISTER+STATUS,TX_Array,1); SPI1_CE_HIGH();//CE=1 使能发射模式 delay_us(200);//CE拉高需要一定的延时才能进行发送 延时之后 即可通过SPI接口发送TX_PLD }
我这里没有专门的初始化程序
其实可以将两端相同的配置整合一下,成为一个通用初始化的子函数
看看接收数据包的函数
/************************************************************* **函数名: SPI_NRF_RX_DATAS() **描述 : 配置NRF发送 数据 **参数 : RBuff[ByteNUM] ByteNUM **返回值: ErrorStatus **注意 : ************************************************************/ ErrorStatus SPI_NRF_RX_DATAS(u8* RBuff) { ErrorStatus RX_Status=SUCCESS,flag; u8 Status[1],fifosta[1],sta[1]; SPI2_CE_HIGH(); delay_ms(1); /* SPI_NRF_Read(SPI2,R_REGISTER+CD,sta,1); printf("CD is 0x%02X\r\n",sta[0]); SPI_NRF_Read(SPI2,R_REGISTER+STATUS,Status,1);//??Status SPI_NRF_Read(SPI2,R_REGISTER+FIFO_STATUS,fifosta,1); printf("STATUS is 0x%02X\r\n",Status[0]); printf("FIFO_STATUS is 0x%02X\r\n",fifosta[0]);*/ //while(NRF_Read_IRQ()!=0);//中断产生时,IRQ引脚低电平 /* SPI_NRF_Read(SPI2,R_REGISTER+STATUS,Status,1);//??Status SPI_NRF_Read(SPI2,R_REGISTER+FIFO_STATUS,fifosta,1); printf("STATUS is 0x%02x\r\n",Status[0]); printf("FIFO_STATUS is 0x%02x\r\n",fifosta[0]); LED_On; printf("RX_OK..............................................\r\n"); SPI2_CE_LOW();//拉低待机,才能操作寄存器 SPI_NRF_Read(SPI2,R_REGISTER+STATUS,Status,1);*///读取Status switch(Status[0]&0x0e) { case 0x0e: RX_Status=ERROR; break; //RX_FIFO 空 default : printf("Rx Success!\r\n");//RX_FIFO非空 break; /* case 0x00: break; //通道0 case 0x02: break; //通道1 case 0x04: break; //通道2 case 0x06: break; //通道3 case 0x08: break; //通道4 case 0x0A: break; //通道5*/ } SPI_NRF_Read(SPI2,R_RX_PAYLOAD,RBuff,32);//读RX_FIFO SPI_NRF_Write(SPI2,W_REGISTER+STATUS,Status,1);//处理状态寄存器标志 return RX_Status; }
里面有不少debug时用到的语句,可忽略
接收主函数
if(NRF_Read_IRQ()==0) { SPI_NRF_RX_DATAS(Rx_buf); printf("RX_Data is "); for (i = 0;i<32;i++) { USART_SendData(USART1,Rx_buf[i]); delay_ms(1); } delay_ms(1); printf("\r\n"); SPI_NRF_MOD_RX(); }
必须每次接收结束后配置一次RX模式,要不然就不能接收到下一个数据包
按数据手册理解,设置成完成接收后,不更改配置寄存器的前提下,只有CE的电平才会影响模式状态
一直到现在都不解
看看发送端的发送数据包函数
/************************************************************* **函数名: SPI_NRF_TX_DATAS() **描述 : 配置NRF发送 数据 **参数 : TBuff[ByteNUM] ByteNUM **返回值: ErrorStatus **注意 : ************************************************************/ ErrorStatus SPI_NRF_TX_DATAS(u8* TBuff,u8 ByteNUM) { u8 Status[1],fifosta[1]; do{ SPI1_CE_LOW();//拉低待机 SPI_NRF_Write(SPI1,W_TX_PAYLOAD,TBuff,ByteNUM);//发送TBuff数组 SPI1_CE_HIGH();//拉低待机 delay_us(700); } while(NRF_Read_IRQ()!=0);//中断产生时,IRQ引脚低电平 printf("Tx data is %s\r\n",TBuff); /* SPI_NRF_Read(SPI1,R_REGISTER+STATUS,Status,1);//读取Status SPI_NRF_Read(SPI1,R_REGISTER+FIFO_STATUS,fifosta,1); printf("STATUS is 0x%02x\r\n",Status[0]); printf("FIFO_STATUS is 0x%02x\r\n",fifosta[0]);*/ SPI_NRF_Write(SPI1, FLUSH_TX,TBuff,0); SPI_NRF_Read(SPI1,R_REGISTER+STATUS,Status,1); SPI1_CE_LOW();//拉低待机 if(Status[0]&0x10) { Status[0]&=0x10; SPI_NRF_Write(SPI1,W_REGISTER+STATUS,Status,1);// printf("Tx Error!\r\n");//重发超时 发送失败 return ERROR; } else { Status[0]&=0x20; SPI_NRF_Write(SPI1,W_REGISTER+STATUS,Status,1);// printf("Tx Success\r\n");//发送成功 return SUCCESS; } }
接收端需小心调试
有点小心得分享,有兴趣的童鞋可以看看:http://forum.eepw.com.cn/thread/249735/2#12
上视频伺候
视频地址:http://player.youku.com/player.php/sid/XNjk4NjQzNTU2/v.swf
发送端从串口助手获取32字节数据,发送给接收端,接收端再通过USART回传PC显示。
That's ALL
晚上玩了一下MPU6050
六轴传感器,集成温度采集
其实这个温度采集
厂家的原意是不是进行温度补偿?
等综合测试的时候,拿这个温度来补偿一下采集到的姿态数据
以前玩加速度计总是读出原始值,再进行运算求得各个分量上的加速度值
谷歌了一下,据说MPU6050内部就集成了DMP
也就是说,DMP模块处理之后,输出直接就是姿态的数据了
这下可省了不少事情
厂家内置的DMP算法,应该会比俺这种新手再进行姿态测量计算要强一些吧
不过要激活MPU6050的DMP,还得先刷新它的固件
其实也不算固件,也就是很多的一段驱动代码,每次上电都需要写进去一次
直接上DMP的驱动程序
回复可见
烈火飞行器IIC使用的硬件IIC2
上相关代码
#include "STM32_I2C.h" #define SCL_H GPIOB->BSRR = GPIO_Pin_10 /* GPIO_SetBits(GPIOB , GPIO_Pin_10) */ #define SCL_L GPIOB->BRR = GPIO_Pin_10 /* GPIO_ResetBits(GPIOB , GPIO_Pin_10) */ #define SDA_H GPIOB->BSRR = GPIO_Pin_11 /* GPIO_SetBits(GPIOB , GPIO_Pin_11) */ #define SDA_L GPIOB->BRR = GPIO_Pin_11 /* GPIO_ResetBits(GPIOB , GPIO_Pin_11) */ #define SCL_read GPIOB->IDR & GPIO_Pin_10 /* GPIO_ReadInputDataBit(GPIOB , GPIO_Pin_10) */ #define SDA_read GPIOB->IDR & GPIO_Pin_11 /* GPIO_ReadInputDataBit(GPIOB , GPIO_Pin_11) */ static void I2C_delay(void) { volatile int i = 7; while (i) i--; } static bool I2C_Start(void) { SDA_H; SCL_H; I2C_delay(); if (!SDA_read) return false; SDA_L; I2C_delay(); if (SDA_read) return false; SDA_L; I2C_delay(); return true; } static void I2C_Stop(void) { SCL_L; I2C_delay(); SDA_L; I2C_delay(); SCL_H; I2C_delay(); SDA_H; I2C_delay(); } static void I2C_Ack(void) { SCL_L; I2C_delay(); SDA_L; I2C_delay(); SCL_H; I2C_delay(); SCL_L; I2C_delay(); } static void I2C_NoAck(void) { SCL_L; I2C_delay(); SDA_H; I2C_delay(); SCL_H; I2C_delay(); SCL_L; I2C_delay(); } static bool I2C_WaitAck(void) { SCL_L; I2C_delay(); SDA_H; I2C_delay(); SCL_H; I2C_delay(); if (SDA_read) { SCL_L; return false; } SCL_L; return true; } static void I2C_SendByte(uint8_t byte) { uint8_t i = 8; while (i--) { SCL_L; I2C_delay(); if (byte & 0x80) SDA_H; else SDA_L; byte <<= 1; I2C_delay(); SCL_H; I2C_delay(); } SCL_L; } static uint8_t I2C_ReceiveByte(void) { uint8_t i = 8; uint8_t byte = 0; SDA_H; while (i--) { byte <<= 1; SCL_L; I2C_delay(); SCL_H; I2C_delay(); if (SDA_read) { byte |= 0x01; } } SCL_L; return byte; } void i2cInit(void) { GPIO_InitTypeDef gpio; //已更改 gpio.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11; gpio.GPIO_Speed = GPIO_Speed_2MHz; gpio.GPIO_Mode = GPIO_Mode_Out_OD; GPIO_Init(GPIOB, &gpio); } bool i2cWriteBuffer(uint8_t addr, uint8_t reg, uint8_t len, uint8_t * data) { int i; if (!I2C_Start()) return false; I2C_SendByte(addr << 1 | I2C_Direction_Transmitter); if (!I2C_WaitAck()) { I2C_Stop(); return false; } I2C_SendByte(reg); I2C_WaitAck(); for (i = 0; i < len; i++) { I2C_SendByte(data[i]); if (!I2C_WaitAck()) { I2C_Stop(); return false; } } I2C_Stop(); return true; } ///////////////////////////////////////////////////////////////////////////////// int8_t i2cwrite(uint8_t addr, uint8_t reg, uint8_t len, uint8_t * data) { if(i2cWriteBuffer(addr,reg,len,data)) { return TRUE; } else { return FALSE; } //return FALSE; } int8_t i2cread(uint8_t addr, uint8_t reg, uint8_t len, uint8_t *buf) { if(i2cRead(addr,reg,len,buf)) { return TRUE; } else { return FALSE; } //return FALSE; } ////////////////////////////////////////////////////////////////////////////////// bool i2cWrite(uint8_t addr, uint8_t reg, uint8_t data) { if (!I2C_Start()) return false; I2C_SendByte(addr << 1 | I2C_Direction_Transmitter); if (!I2C_WaitAck()) { I2C_Stop(); return false; } I2C_SendByte(reg); I2C_WaitAck(); I2C_SendByte(data); I2C_WaitAck(); I2C_Stop(); return true; } bool i2cRead(uint8_t addr, uint8_t reg, uint8_t len, uint8_t *buf) { if (!I2C_Start()) return false; I2C_SendByte(addr << 1 | I2C_Direction_Transmitter); if (!I2C_WaitAck()) { I2C_Stop(); return false; } I2C_SendByte(reg); I2C_WaitAck(); I2C_Start(); I2C_SendByte(addr << 1 | I2C_Direction_Receiver); I2C_WaitAck(); while (len) { *buf = I2C_ReceiveByte(); if (len == 1) I2C_NoAck(); else I2C_Ack(); buf++; len--; } I2C_Stop(); return true; } uint16_t i2cGetErrorCounter(void) { // TODO maybe fix this, but since this is test code, doesn't matter. return 0; }
还好这次IIC调试比较顺利
记得以前在硬件IIC上面栽过不少跟头,汗啊......
来瞧瞧主函数
#define q30 1073741824.0f float q0=1.0f,q1=0.0f,q2=0.0f,q3=0.0f; float Pitch,Roll,Yaw; int main(void) { /*!< At this stage the microcontroller clock setting is already configured, this is done through SystemInit() function which is called from startup file (startup_stm32f10x_xx.s) before to branch to application main. To reconfigure the default setting of SystemInit() function, refer to system_stm32f10x.c file */ int result; /* Configure all unused GPIO port pins in Analog Input mode (floating input trigger OFF), this will reduce the power consumption and increase the device immunity against EMI/EMC *************************************************/ Clock_Enable(); GPIO_Configuration(); USART_Configuration(); i2cInit();//IIC总线的初始化,尼玛纠结了这么长时间 result = mpu_init(); if(!result) { //mpu_init(); PrintChar("mpu initialization complete......\n "); //mpu_set_sensor if(!mpu_set_sensors(INV_XYZ_GYRO | INV_XYZ_ACCEL)) { PrintChar("mpu_set_sensor complete ......\n"); } else { PrintChar("mpu_set_sensor come across error ......\n"); } //mpu_configure_fifo if(!mpu_configure_fifo(INV_XYZ_GYRO | INV_XYZ_ACCEL)) { PrintChar("mpu_configure_fifo complete ......\n"); } else { PrintChar("mpu_configure_fifo come across error ......\n"); } //mpu_set_sample_rate if(!mpu_set_sample_rate(DEFAULT_MPU_HZ)) { PrintChar("mpu_set_sample_rate complete ......\n"); } else { PrintChar("mpu_set_sample_rate error ......\n"); } //dmp_load_motion_driver_firmvare if(!dmp_load_motion_driver_firmware()) { PrintChar("dmp_load_motion_driver_firmware complete ......\n"); } else { PrintChar("dmp_load_motion_driver_firmware come across error ......\n"); } //dmp_set_orientation if(!dmp_set_orientation(inv_orientation_matrix_to_scalar(gyro_orientation))) { PrintChar("dmp_set_orientation complete ......\n"); } else { PrintChar("dmp_set_orientation come across error ......\n"); } //dmp_enable_feature if(!dmp_enable_feature(DMP_FEATURE_6X_LP_QUAT | DMP_FEATURE_TAP | DMP_FEATURE_ANDROID_ORIENT | DMP_FEATURE_SEND_RAW_ACCEL | DMP_FEATURE_SEND_CAL_GYRO | DMP_FEATURE_GYRO_CAL)) { PrintChar("dmp_enable_feature complete ......\n"); } else { PrintChar("dmp_enable_feature come across error ......\n"); } //dmp_set_fifo_rate if(!dmp_set_fifo_rate(DEFAULT_MPU_HZ)) { PrintChar("dmp_set_fifo_rate complete ......\n"); } else { PrintChar("dmp_set_fifo_rate come across error ......\n"); } run_self_test(); if(!mpu_set_dmp_state(1)) { PrintChar("mpu_set_dmp_state complete ......\n"); } else { PrintChar("mpu_set_dmp_state come across error ......\n"); } } while(1) { unsigned long sensor_timestamp; short gyro[3], accel[3], sensors; unsigned char more; long quat[4]; //float Yaw,Roll,Pitch; dmp_read_fifo(gyro, accel, quat, &sensor_timestamp, &sensors, &more); /* Gyro and accel data are written to the FIFO by the DMP in chip * frame and hardware units. This behavior is convenient because it * keeps the gyro and accel outputs of dmp_read_fifo and * mpu_read_fifo consistent. */ /* if (sensors & INV_XYZ_GYRO ) send_packet(PACKET_TYPE_GYRO, gyro); if (sensors & INV_XYZ_ACCEL) send_packet(PACKET_TYPE_ACCEL, accel); */ /* Unlike gyro and accel, quaternions are written to the FIFO in * the body frame, q30. The orientation is set by the scalar passed * to dmp_set_orientation during initialization. */ if (sensors & INV_WXYZ_QUAT ) { PrintChar(" In Calculating quaternion steps.....\r\n"); q0=quat[0] / q30; q1=quat[1] / q30; q2=quat[2] / q30; q3=quat[3] / q30; Pitch = asin(-2 * q1 * q3 + 2 * q0* q2)* 57.3; // pitch Roll = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* 57.3; // roll Yaw = atan2(2*(q1*q2 + q0*q3),q0*q0+q1*q1-q2*q2-q3*q3) * 57.3; delay_ms(1); printf("Pirch = %10f\r\n",Pitch); delay_ms(1); printf("Roll = %10f\r\n",Roll); delay_ms(1); printf("Yaw = %10f\r\n",Yaw); delay_ms(1); delay_ms(300); } if(sensors & INV_XYZ_GYRO) {} if(sensors & INV_XYZ_ACCEL) {} //send_packet(PACKET_TYPE_QUAT, quat); } }
这里头要特别注意MUP6050初始化和启用DMP的流程
必须要完成相关操作才能够正常使用DMP。
特别值得的是,定义了一个算子
#定义Q30 1073741824.0f
为什么是这么多,我也不知道,原厂的驱动里面就是这货。
的printf函数真心好用,浮点变量瞬间输出
主函数里边就能够输出偏航,滚动和间距等姿态数据了
没有定标,不知道最终输出的数据是不是准确
上视频:
视频地址:http://player.youku.com/player.php/sid/XNjk4OTIyODY4/v.swf
回复
有奖活动 | |
---|---|
【有奖活动——B站互动赢积分】活动开启啦! | |
【有奖活动】分享技术经验,兑换京东卡 | |
话不多说,快进群! | |
请大声喊出:我要开发板! | |
【有奖活动】EEPW网站征稿正在进行时,欢迎踊跃投稿啦 | |
奖!发布技术笔记,技术评测贴换取您心仪的礼品 | |
打赏了!打赏了!打赏了! |