这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » 【换取逻辑分析仪】使用STM32F103C8T6芯片制作的无线模块从机兼作DAP

共1条 1/1 1 跳转至

【换取逻辑分析仪】使用STM32F103C8T6芯片制作的无线模块从机兼作DAP下载器

助工
2025-01-01 13:39:05     打赏

    项目设计的前言:作为一个嵌入式的工程师,在编写代码时候,难免会遇到不同电平之间的转换。例如:RS485通讯方式,RS232,TTL信号,为了查看通讯之间数据的正确性,经常使用到不同的电平转换模块,还需要一个USB转串口工具,将数据上传到PC端,这样我们才能将查看控制器或者是负载返回的数据,为了避免调试代码起来方便,让自己的工作变得整洁,方便我们自己的工作,前端时间正好研究SPI通讯方式的NRF24L01这款无线芯片,利用业余时间做了一个无线通讯模块。在这里分享一下开发的经验。

一:原理图展示部分:

12-1.png

这里使用的是STM32F103的主控。实物效果仿真图如下所示:

12-2.png

三:软硬件设计的思路:

在本次项目中使用主控为STM32F103C8T6芯片,当时使用该块芯片是因为该款芯片具有USB功能,该芯片可以虚拟一个USB的串口进行数据交互,而且封装也比较小,在PCB布局方面比较简单。

无线模块采用的是一款SPI通讯的NRF24L01芯片,在实际的使用过程中比较稳定,可以满足数据之间的交互。

软件设计思路:利用串口1的引脚的接收发送引脚,使用消息队列的方式,可以将USB下发的的数据发送出去。

使用串口2完成与SPI之间的交互,

软件配置参数:使用STM32cube MX软件生成USB,SPI,串口底层的驱动部分,这样开发起来也比较容易;

cube软件配置图如下,主要是对该项目中使用到的引脚进行配置

12-4.png

部分代码分享如下:

		if(bDeviceState == CONFIGURED)          //判断 USB的连接状态被置位时
		{
			NRF24L01_Stop();		          //停止NRF24L01的工作
			if(GetEPRxStatus(ENDP4) == EP_RX_NAK)     //判断一下USB下发的字节长度
			{
			    if(CDC_MAXSIZE - queue_size(&Qusart2Send) >= 64 ) //由于无线模块本身的限制,每次只能传输64个字节长度,这里做分包处理
			   SetEPRxValid(ENDP4);
			}
			/*下述代码,是判断与主机模块的波特率,校验位,停止位不一致时候,主机需要重新下发串口的配置信息*/
			if(	linecoding_old.bitrate != linecoding.bitrate ||
					linecoding_old.datatype != linecoding.datatype ||
					linecoding_old.format != linecoding.format ||
					linecoding_old.paritytype != linecoding.paritytype )
			{
				if(	usart2_flag == 0 )   //下发配置信息命令
				{
					uart2_config_change(linecoding);
					memcpy((void *)(&linecoding_old),(void *)&linecoding,sizeof(linecoding));
				}
			}
			else if(queue_size(&Qusart2Send) > 0 && usart2_flag == 0 && GetEPRxStatus(ENDP4) == EP_RX_VALID) //该处代码为下发串口数据指令
			{
				QueueElementByte data_byte;
				usart2_flag = 1;
				if(dequene(&Qusart2Send,&data_byte) == 0 )
				{
					usart2_send(data_byte.buffer);
//					USART_SendData(USART2,data_byte.buffer);
				}
				USART_ITConfig(USART2, USART_IT_TXE, ENABLE);//开启ENABLE/关闭DISABLE中断
			}
			
			if((queue_size(&Daplink_Rec) > 0))   //当下发用作无线DAP时候,需要将数据传输到从机模块的数据引脚
			{
				QueueElement64Bytes response;
				dequene(&Daplink_Rec,&data);
				
				if(data.buffer[0] == 0 && data.buffer[1] == 2)
				{
					quene_clean(&Daplink_Send);
				}
				response.len = DAP_ExecuteCommand(data.buffer,response.buffer);
				enquene(&Daplink_Send,&response);
				SetEPRxValid(ENDP6);
			}
			else
			{
				if(GetEPRxStatus(ENDP6) == EP_RX_NAK)
				{
					SetEPRxValid(ENDP6);
				}
			}
			
			if(GetEPTxStatus(ENDP6) == EP_TX_NAK && GetEPTxStatus(ENDP4) == EP_TX_NAK && GetEPTxStatus(ENDP0) == EP_TX_STALL)
			{
				if (dequene(&Daplink_Send,&data) == 0)
				{
						UserToPMABufferCopy(data.buffer, ENDP6_TXADDR, data.len);
						SetEPTxCount(ENDP6, data.len);
						SetEPTxValid(ENDP6);
				}
				else
				{
//					if(usart2Rec_flag == 0 || queue_size(&Qusart2Rec) >= 64)
					if(usart2Rec_flag == 0)
					{
						QueueElementByte data1;
						if(queue_size(&Qusart2Rec) > 0)
						{
							for(i = 0; i < 64; i ++)
							{
								if(dequene(&Qusart2Rec,&data1) == -1 )
									break;
								buffer[i] = data1.buffer;
							}
							UserToPMABufferCopy(buffer, ENDP4_TXADDR, i);
							SetEPTxCount(ENDP4, i);
							SetEPTxValid(ENDP4);
						}
					}
				}
			}
		}
		else
		{
			NRF24L01_Start();
			if (dequene(&Daplink_Rec,&data) == 0)
			{
				QueueElement64Bytes data1 = {0};
				data1.len = DAP_ExecuteCommand(data.buffer, data1.buffer) & 0xFFFF;
				enquene(&Daplink_Send,&data1);
			}
			if(queue_size(&Qusart2Send) > 0 && usart2_flag == 0)
			{
				QueueElementByte data_byte;
				usart2_flag = 1;
				if(dequene(&Qusart2Send,&data_byte) == 0 )
				{
					usart2_send(data_byte.buffer);
				}
				USART_ITConfig(USART2, USART_IT_TXE, ENABLE);//开启ENABLE/关闭DISABLE中断
			}
		}
	}
//外部中断1服务程序 
void EXTI4_IRQHandler(void)
{
	uint8_t buffer[32] = {0};
	QueueElementByte data = {0};
	uint8_t i;
	LINE_CODING line = {0};
	static char flag = 0;
	if(NRF24L01_RxPacket(buffer) == 0)  
	{
		if(nrf2dap_Data(buffer) == -1)
		{
			if(buffer[0] == 33)//串口配置指令
			{
				memcpy((void *)&line,(void *)(buffer+1),sizeof(line));
				uart2_config_change(line);
				NRF24L01_Write_ACKPacket(0,buffer,sizeof(line) + 1);
				LED1 = 0;
				usart_led = 1;
				flag = 1;
				return ;
			}
			else if(buffer[0] < 32 && buffer[0] != 0)
			{
				for(i = 0; i < buffer[0]; i ++) //从机模块将无线接收到的数据,通过串口2 发出去,并作出提示
				{
					data.buffer = buffer[i + 1];
					enquene(&Qusart2Send,&data);
				}
				LED1 = 0;
				usart_led = 1;
			}
		}
		if(dap2nrf_Data() == -1)
		{
			if(flag == 1)
			{
				buffer[0] = 0;
				if(queue_size(&Qusart2Rec) > 0)
				{
					for(i = 1; i < 32; i ++)
					{
    						if(dequene(&Qusart2Rec,&data) == -1 )
    							break;
    						buffer[i] = data.buffer;
					}
					buffer[0] = i - 1;	
					LED1 = 0;
					usart_led = 1;
				}
				NRF24L01_Write_ACKPacket(0,buffer,buffer[0] + 1);
			}
			else
			{
				buffer[0] = 33;//上电后自动要求配置串口
				NRF24L01_Write_ACKPacket(0,buffer,1);
			}			
		}
	}
	EXTI_ClearITPendingBit(EXTI_Line4); //清除LINE4上的中断标志位  
}

int dap2nrf_Data(void)
{
	static uint8_t state = 0,send_len = 0,send_maxlen = 29;
	static QueueElement64Bytes data={0};
	uint8_t buffer[32];
	if(state == 0)
	{
		if((queue_size(&Daplink_Send) > 0))
		{
			dequene(&Daplink_Send,&data);
			state ++;
		}
		else
		{
			return -1;
		}
	}
	if(state == 1)
	{
		if((int)data.len - send_len >= send_maxlen)
		{
			buffer[0] = 34;
			buffer[1] = id;
			buffer[2] = send_maxlen;
			memcpy(buffer+3,data.buffer + send_len,buffer[2]);
			NRF24L01_Write_ACKPacket(0,buffer,32);;
			send_len += buffer[2];
		}
		else
		{
			buffer[0] = 34;
			buffer[1] = id;
			buffer[2] = (int)data.len - send_len;
			memcpy(buffer+3,data.buffer + send_len,buffer[2]);
			NRF24L01_Write_ACKPacket(0,buffer,buffer[2]+3);
			send_len = 0;
			id ++;
			state = 0;
		}
	}
	return 0; 
}
int nrf2dap_Data(uint8_t *buf)
{
	static uint8_t state = 0,id_last = 0,rec_len = 0,rec_maxlen = 29;
	static QueueElement64Bytes data={0};
	if(buf[0] != 34)
		return -1;

	if(buf[1] != id_last)//此条件成立即数据传输出错
	{
		id_last = buf[1];
		quene_clean(&Daplink_Rec);
		quene_clean(&Daplink_Send);
		rec_len = 0;
		id ++;
	}
	if(buf[2] == rec_maxlen)
	{
		memcpy(data.buffer + rec_len,buf+3,buf[2]);
		rec_len += buf[2];
	}
	else
	{
		memcpy(data.buffer + rec_len,buf+3,buf[2]);
		rec_len += buf[2];
		data.len = rec_len;
		enquene(&Daplink_Rec,&data);
		rec_len = 0;
		id_last ++;
	}
	return 0; 
}

在实际使用中发现,硬件功耗大概在130ma左右,在数据交互的时候功耗会增加至170ma左右,不过并不影响模块的使用。

使用该模块的最大好处就是可以让硬件工程师的桌面更加的整洁。

硬件设计的时候,设计注意事项:

使用PS7516用作升压芯片,将电池电压3.7V升压至5V,为外设可以提供5v的电压输出。

MP2155为降压型LDO,具有可关断功能,当不使用时候,可以使整个系统处于低功耗状态,从而延长电池寿命。

PW4054为锂电池充电芯片,可使用type-c为电池充电,充电电流由R26取样电阻设定。

同时PL5356A芯片为锂电池电压检测芯片,可以显示当前的电池电量,后期将电池电压分压至单片机的ADC内,从而精准的检测电池电压。

在软件开发时候,有以下的注意事项:

抗干扰能力:NRF24L01工作在2.4GHz频段,该频段易受到WiFi、蓝牙等设备的干扰。在布局布线时要尽量减少干扰源的影响,并合理设置通信参数以提高抗干扰能力。

功耗管理:在不需要通信时及时将NRF24L01置于待机模式或掉电模式以降低功耗。

距离与障碍物:无线通信距离受环境因素影响较大,障碍物会严重影响通信质量。在实际应用中要考虑通信距离和障碍物的影响并采取相应的措施。

NRF24L01的开发过程需要仔细规划硬件连接、合理配置软件参数、掌握调试技巧并关注实际应用中的注意事项。通过不断的实践和总结可以逐渐提高开发效率和应用效果。

实物效果如下所示:

12-3.jpg

测试效果如下所示:

12-6.png

原理图如下所示:

无线下载器原理图.pdf





关键词: STM32F103     无线模块从机     DAP    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]