注意:1.本实验不是利用传统意义上的多通道实现的
使用 STM32F407 开发板、NRF2401 WIFI 模块,完成以下内容:
实验功能简介:开机的时候先检测 NRF24L01 模块是否存在,在检测到 NRF24L01 模块之后,根据 KEY0 和 KEY1 的设置来决定模块的工作模式,在设定好工作模式之后,就会发送/接收数据,可以通过 KEY0 和 KEY1 完成模式切换。
所要用到的硬件资源如下:
NRF24L01 模块属于外部模块,开发板上 NRF24L01 模块接口和 STM32F4 的连接情况,他们的连接关系下图所示:
这里 NRF24L01 也是使用的 SPI1,和 W25Q128 共用一个 SPI 接口,所以在使用的时候,他们分时复用 SPI1 。本章我们需要把 W25Q128 的片选信号置高,以防止这个器件对NRF24L01的通信造成干扰。另外, NRF_IRQ 和 RS485_RE 共用了 PG8 ,所以,他们不能同时使用,不过我们一般用不到 NRF_IRQ 这个信号,因此, RS485 和 NRF 一般也可以同时使用。
利用 USB 转 NRF24L01 上位机对模块进行配置。
其实没什么用,到时候用单片机,配置代码全在单片机中,其中还有一个很奇怪的现象,用上位机或者AT指令改完配置之后,把NRF24L01模块插入另一个转换器中,用AT指令显示配置会发现配置变成了另外一个。
u8 NRF24L01_Check(void)
{
u8 buf[5]={
0XA5,0XA5,0XA5,0XA5,0XA5};
u8 i;
SPI1_SetSpeed(SPI_SPEED_8);
NRF24L01_Write_Buf(NRF_WRITE_REG+TX_ADDR,buf,5);
NRF24L01_Read_Buf(TX_ADDR,buf,5);
for(i=0;i<5;i++)if(buf[i]!=0XA5)break;
if(i!=5)return 1;
return 0;
}
模块检测的原理为:在模块的寄存器上写入数据,然后再将数据读取出来,若读取的数据与原理相同,则表明,模块存在且功能正常。
void EXTI3_IRQHandler(void)
{
delay_ms(10);
if(KEY1==0)
{
delay_ms(2000);
if(KEY1==0){
printf("进入发送2模式\n");
mode=2;
}
else {
printf("进入发送1模式\n");mode=1;}
}
EXTI->PR=1<<3;
}
void EXTI4_IRQHandler(void)
{
delay_ms(10);
if(KEY0==0)
{
delay_ms(10);
if(KEY0==0)
{
printf("进入接收模式\n");
mode=3;
}
}
EXTI->PR=1<<4;
}
void EXTIX_Init(void)
{
KEY_Init();
Ex_NVIC_Config(GPIO_E,3,FTIR);
Ex_NVIC_Config(GPIO_E,4,FTIR);
MY_NVIC_Init(2,2,EXTI3_IRQn,2);
MY_NVIC_Init(2,2,EXTI4_IRQn,2);
}
长短按键的实现主要是依靠外部中断。利用延迟函数,判断按键前后的状态来实现 key1 的长按和短按。利用全局变量 mode 获取按键按下后得到的值,然后将 mode 传到主函数,进入相应的模式。
if(KEY_Scan(0)==1||KEY_Scan(0)==0){
break;}
在主函数中的相关模式下加入如上代码,可以实现当前模式结束,判断按键,进入下一个模式,实现利用 key1 和 key0 完成模式转换操作。前提是将key.c中无键值返回改为除0和1以外的其他数。
对于 key1和 key2的切换不太好用,也许在主函数整体加个while(1)会比较好。
TIM3_Int_Init(10000-1,8400-1);
void TIM3_IRQHandler(void)
{
if(TIM3->SR&0X0001)
{
time++;
}
TIM3->SR&=~(1<<0);
}
第一行在main函数中,用来调整定时器的频率。 T o u t = [ ( a r r + 1 ) × ( p s c + 1 ) ] Tout={\left[{(arr+1)\times(psc+1)}\right]} Tout=[(arr+1)×(psc+1)] 利用公式可以求出现在计时器为1秒。利用while
循环,time 每增大1就过去1秒,到达一定数值后利用break
函数跳出循环,这样就能实现相应的功能。
LCD_Fill(0,170,lcddev.width,170+16*3,WHITE);
LCD_ShowString(30,150,200,16,16,"NRF24L01 TX_Mode1");
NRF24L01_TX_Mode();
while(1)
{
adcx=Lsens_Get_Val();
bon[0]=adcx/10;
bon[1]=adcx%10;
bon[2]=37;
if(NRF24L01_TxPacket(tmp_buf)==TX_OK)
{
LCD_ShowString(30,170,239,32,16,"Sended DATA:(LSENS_VAL)");
LCD_ShowString(30,190,55,16,16,tmp_buf);
LCD_DrawLine(47, 205, 56, 191);
LCD_Draw_Circle(50,192,2);
LCD_Draw_Circle(54,202,2);
tmp_buf[0]=bon[0]+48;
tmp_buf[1]=bon[1]+48;
for(t=3;t<32;t++)
{
tmp_buf[t]=' ';
}
tmp_buf[31]=3;
tmp_buf[32]=0;
}else
{
LCD_Fill(0,170,lcddev.width,170+16*3,WHITE);
LCD_ShowString(30,170,lcddev.width-1,32,16,"Send Failed ");
};
while(1)
{
if(time>=1){
time=0;break;}
}
if(KEY_Scan(0)==1||KEY_Scan(0)==0){
break;}
}
首先是LCD_Fill()
函数进行屏幕清除,NRF24L01_TX_Mode()
进入发送模式,利用 adcx 获取光照强度。将获取到的 adcx 值进行一些运算存贮在 bon[ ] 数组中,然后将 bon[ ] 数组中的值赋值给 tmp_buf[ ] 数组中,利用for
循环将 tmp_buf[ ] 数组中没有数据的位置空,最后在 tmp_buf[ ] 数组中的倒数第二位加上标识符确定身份,在 tmp_buf[ ] 数组中的最后一位加入结束符。至此第一个数据包制作完成,进入while(1)
语句中延迟 1 秒后继续发送数据包。
标识符是为了识别是那个单片机传的数据,所以三套单片机的程序是有一些区别的。
if(NRF24L01_TxPacket(tmp_buf)==TX_OK)
{
LCD_ShowString(30,170,239,32,16,"Sended DATA:");
LCD_ShowString(30,190,55,16,16,tmp_buf);
key=mode;
key1=mode;
for(t=0;t<32;t++)
{
tmp_buf[t]=a[key-key1];
key++;
if(key>=key1+7)key=0;
}
mode++;
tmp_buf[31]=3;
tmp_buf[32]=0;
for(mode=0;mode<6;mode++)
{
bin[0]=a[mode];
a[mode]=a[mode+1];
a[mode+1]=bin[0];
}
}else
{
LCD_Fill(0,170,lcddev.width,170+16*3,WHITE);
LCD_ShowString(30,170,lcddev.width-1,32,16,"Send Failed ");
};
mode=0;
while(1)
{
if(time>=2){
time=0;break;}
}
if(KEY_Scan(0)==1||KEY_Scan(0)==0){
break;}
发送模式 2 与发送模式 1 同理,只是将光照强度改为数组 a[7]={“IOT-BCU”} 的值。为了实现每 2 秒左移的功能,利用for
语句将第一个值右移6次即等价于数组左移 1 次。然后就是相同的操作,最后在 tmp_buf[ ] 数组中的倒数第二位加上标识符确定身份,在 tmp_buf[ ] 数组中的最后一位加入结束符。至此第一个数据包制作完成,进入while(1)
语句中延迟 2 秒后继续发送数据包。
这里写啰嗦了,第一次是想用另一种方法写的,结果失败了,后面又懒得改了。
LCD_Fill(0,170,lcddev.width,170+16*3,WHITE);
LCD_ShowString(30,150,200,16,16,"NRF24L01 RX_Mode");
LCD_ShowString(30,170,200,16,16,"Received DATA:");
NRF24L01_RX_Mode();
while(1)
{
if(NRF24L01_RxPacket(tmp_buf)==0)
{
tmp_buf[32]=0;
for(i=i;i<32;i++)
{
tmp_buf[i]=tmp_buf[i+1];
}
if(tmp_buf[0]<58&&tmp_buf[31]==1&&tmp_buf[0]!='-')
{
LCD_ShowString(150,170,200,16,16,"(LSENS_VAL)");
LCD_DrawLine(47, 205, 56, 191);
LCD_Draw_Circle(50,192,2);
LCD_Draw_Circle(54,202,2);
}
if(tmp_buf[0]<58&&tmp_buf[31]==2&&tmp_buf[0]!='-')
{
LCD_ShowString(150,170,200,16,16,"(LSENS_VAL)");
LCD_DrawLine(47, 225, 56, 211);
LCD_Draw_Circle(50,212,2);
LCD_Draw_Circle(54,222,2);
}
if(tmp_buf[0]<58&&tmp_buf[31]==3&&tmp_buf[0]!='-')
{
LCD_ShowString(150,170,200,16,16,"(LSENS_VAL)");
LCD_DrawLine(47, 245, 56, 231);
LCD_Draw_Circle(50,232,2);
LCD_Draw_Circle(54,242,2);
}
if(tmp_buf[31]==1)LCD_ShowString(30,190,55,16,16,tmp_buf);
if(tmp_buf[31]==2)LCD_ShowString(30,210,55,16,16,tmp_buf);
if(tmp_buf[31]==3)LCD_ShowString(30,230,55,16,16,tmp_buf);
}
else delay_us(100);
t++;
if(t==10000)
{
t=0;
LED0=!LED0;
}
if(KEY_Scan(0)==1||KEY_Scan(0)==0){
break;}
}
与发送模式大同小异。清屏,进入接收模式,将 tmp_buf[ ] 数组中的 32 位数据依次显示在屏幕上,其中利用了if
语句,判断数据的格式、标识符来确定输出的位置及信息。
程序刚开始运行,没有插入NRF24L01模块。会显示“NRF24L01 Error”不断闪烁。
让其中 1 号和 2 号板子进入发送模式 1,3 号板子进入接收模式。
问题一:check() 函数没有问题,可是屏幕上一直显示 “NRF24L01 Error”。
解决:ST-Link 与 NRF24L01 冲突,导致 NRF24L01 模块无法正常的写读。在烧录程序之后,将 ST-Link 断开再调试。
问题二:利用通道 1 和 2 实现一收多发存在一系列问题。
解决:将三个模块的收发地址全部改为相同的地址。tmp_buf 的倒数第二位存入标识符再发送,在读取数据的时候,根据标识符即可判断数据来自那个模块。
问题三:光照强度存入 tmp_buf 中,最后无法显示或者显示乱码。
解决:tmp_buf 存入的数据为 ASCII 码,故要在相应的位上加 48。
问题四:通信协议
解决:根据相关资料,tmp_buf 的第 0 个字节系统保留,用于每次传输的数据包长度统计。这个是针对于使用了这中上位机模块如果使用的是两个 24L01 相互通信,完全不用考虑这个。
链接:https://pan.baidu.com/s/18P0SynB54PjweIedVff3AA
提取码:cyqy
微信扫码: