简易对讲机项目

项目说明

本项目采用air001主控芯片结合SR_FRS_2WUS无线对讲机模块,打造了一款简易对讲机。设备支持20个公共对讲机频道,支持亚音和扰频功能,提供更强的通信保密性。采用CW2015电池监测芯片,可实时监测电池电量,确保设备长时间稳定运行。同时,利用AT24C02芯片实现了参数设置的掉电存储功能,保障用户设置的参数在掉电情况下不会丢失,提升了用户体验和便利性。

开源协议

GPL3.0

项目相关功能

  • 支持待机息屏功能,节省能量。
  • 提供409.7500MHz~409.9875MHz共20个公共对讲机频道选择。
  • 支持亚音技术,有效防止串频现象的发生。并且支持扰频功能,可对语音进行加密,提升通信安全性。
  • 用户可灵活切换收发模式或监听模式,灵活应对不同场景需求。
  • 在城市环境下,对讲机的通讯距离可达1公里以上,语音传输清晰且具备良好的分辨度。
  • 内置电量监测功能,可实时监测电池电量。此外,还支持参数设置的掉电存储,提高了设备的可靠性和便利性。

电路设计原理

系统框图

在这里插入图片描述

电源电路

电源部分采用TP4056进行锂电池的充放电。需要注意的是,电路板未添加锂电池保护电路,因此建议选择带有保护电路的锂电池以确保安全使用。
在这里插入图片描述

电池电量监测采用CW2015芯片,其外围电路较为简单。并且可以通过I2C通信直接读取电量百分比值以及剩余运行时间。
在这里插入图片描述
主控电路和射频模块电路分别使用XC6201和SX1308进行供电。其中SX1308需要将EN引脚上拉从而启动电压转换。
在这里插入图片描述
在这里插入图片描述

air001最小系统电路

AIR001系列采用高性能的ARM Cortex-M0+ 32位内核,最高工作频率达到48MHz,并且支持低功耗模式。内置存储器包括32KB Flash和4KB SRAM。此外,还搭载了9个定时器、1个12位ADC,并配备了2个SPI接口、1个I2C接口以及2个USART接口。外围电路设计简单,无需外部晶振即可正常工作。这里仅需将boot引脚下拉,并接上复位电路即可完成air001最小系统电路的设计。
在这里插入图片描述

独立按键

考虑到air001拥有较多的GPIO口,我们采用了独立按键进行输入。各个按键的功能包括选择、取消、上、下、左、右。
在这里插入图片描述

eeprom存储

我们在对讲机的操作中需要设置频率、亚音、扰频、音量等级等参数。如果每次上电都需要重新设置,将会增加使用的复杂度。因此为了方便用户使用,我们采用AT24C02芯片对相关参数数据进行存储,实现了参数设置的掉电保存功能,从而提高了设备的便利性。该芯片使用I2C进行通信,与CW2015芯片共用I2C总线,减小GPIO的使用。
在这里插入图片描述

射频模块

射频模块采用SR_FRS_2WUS,它是一款性价比极高的无线语音对讲及数传模块。该模块内置高性能的射频收发芯片、微控制器以及射频功放。外部控制器可以通过标准的异步串行接口(UART)通信,实现设置模块的工作参数并控制整个模块的收发。此模块只需外接天线、MIC和语音功放,即可组装成一台完整的对讲机或数传电台。
在这里插入图片描述
在阻抗匹配部分采用了"嘉立创阻抗计算神器"进行走线线宽的简单计算,并预留了派型阻抗匹配电路,可用于更精确的阻抗匹配。如果不进行阻抗匹配,可在使用时通过接入0欧电阻将L2短路,即可正常使用。
在这里插入图片描述

mic输入

采用驻极体电容麦克风作为语音输入。
在这里插入图片描述

音频输出

音频输出部分采用LM4871音频功率放大器,能够轻松驱动4欧8W的扬声器,特别适用于小体积、轻便的便携系统。LM4871的使能端连接射频模块的SQ端,当未接收到信号时,可将LM4871置于休眠模式,有效降低功耗并防止噪音输出。此外,LM4871内置过热自动关断保护机制,工作稳定,单位增益可调。通过配置外部电阻,可轻松调整放大器的电压增益,满足不同应用需求。
在这里插入图片描述

软件部分说明

程序部分主要分为五大部分:OLED界面、按键控制、对讲模块功能设置、电量读取、参数存储。

OLED界面

IIC通信使用软件进行实现,实现较为简单。

#ifndef __I2C_H
#define __I2C_H

#include "air001xx_hal.h"

#define I2C_GPIO GPIOF
#define I2C_SDA GPIO_PIN_0
#define I2C_SCL GPIO_PIN_1
#define I2C_GPIO_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE()

#define I2C_CMD  0	//写命令
#define I2C_DATA 1	//写数据

void I2C_Init(void);
void I2C_Start(void);
void I2C_Stop(void);
void I2C_WaitAck(void);
void I2C_SendByte(uint8_t dat);
uint8_t I2C_ReadByte(uint8_t ack);

void OLED_WR_Byte(uint8_t dat,uint8_t mode);
#endif

air001通过IIC与OLED进行通信,从而设置显示内容。

void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t size1,uint8_t mode)
{
	uint8_t i,m,temp,size2,chr1;
	uint8_t x0=x,y0=y;
	if(size1==8)size2=6;
	else size2=(size1/8+((size1%8)?1:0))*(size1/2);  //得到字体一个字符对应点阵集所占的字节数
	chr1=chr-' ';  //计算偏移后的值
	for(i=0;i<size2;i++)
	{
		if(size1==8)
			  {temp=asc2_0806[chr1][i];} //调用0806字体
		else if(size1==12)
        {temp=asc2_1206[chr1][i];} //调用1206字体
		else if(size1==16)
        {temp=asc2_1608[chr1][i];} //调用1608字体
		else if(size1==24)
        {temp=asc2_2412[chr1][i];} //调用2412字体
		else return;
		for(m=0;m<8;m++)
		{
			if(temp&0x01)OLED_DrawPoint(x,y,mode);
			else OLED_DrawPoint(x,y,!mode);
			temp>>=1;
			y++;
		}
		x++;
		if((size1!=8)&&((x-x0)==size1/2))
		{x=x0;y0=y0+8;}
		y=y0;
  }
}

按键控制

按键部分采用六个独立按键,分别为确认、取消、上、下、左、右六个功能。在main函数的while循环中对按键进行扫描从而确定按下的按键并执行对应的程序。

uint8_t Key_Scan(void)
{
	if(HAL_GPIO_ReadPin(KEY_GPIOA,KEY_SEL_PIN)==0)
	{
		HAL_Delay(20);
		if(HAL_GPIO_ReadPin(KEY_GPIOA,KEY_SEL_PIN)==0)
		{
			while(HAL_GPIO_ReadPin(KEY_GPIOA,KEY_SEL_PIN)==0);
			return 1;
		}
	}else if(HAL_GPIO_ReadPin(KEY_GPIOA,KEY_RET_PIN)==0)
	{
		HAL_Delay(20);
		if(HAL_GPIO_ReadPin(KEY_GPIOA,KEY_RET_PIN)==0)
		{
			while(HAL_GPIO_ReadPin(KEY_GPIOA,KEY_RET_PIN)==0);
			return 2;
		}
	}else if(HAL_GPIO_ReadPin(KEY_GPIOB,KEY_UP_PIN)==0)
	{
		HAL_Delay(20);
		if(HAL_GPIO_ReadPin(KEY_GPIOB,KEY_UP_PIN)==0)
		{
			while(HAL_GPIO_ReadPin(KEY_GPIOB,KEY_UP_PIN)==0);
			return 3;
		}
	}else if(HAL_GPIO_ReadPin(KEY_GPIOA,KEY_DOWN_PIN)==0)
	{
		HAL_Delay(20);
		if(HAL_GPIO_ReadPin(KEY_GPIOA,KEY_DOWN_PIN)==0)
		{
			while(HAL_GPIO_ReadPin(KEY_GPIOA,KEY_DOWN_PIN)==0);
			return 4;
		}
	}else if(HAL_GPIO_ReadPin(KEY_GPIOA,KEY_LEFT_PIN)==0)
	{
		HAL_Delay(20);
		if(HAL_GPIO_ReadPin(KEY_GPIOA,KEY_LEFT_PIN)==0)
		{
			while(HAL_GPIO_ReadPin(KEY_GPIOA,KEY_LEFT_PIN)==0);
			return 5;
		}
	}else if(HAL_GPIO_ReadPin(KEY_GPIOA,KEY_RIGHT_PIN)==0)
	{
		HAL_Delay(20);
		if(HAL_GPIO_ReadPin(KEY_GPIOA,KEY_RIGHT_PIN)==0)
		{
			while(HAL_GPIO_ReadPin(KEY_GPIOA,KEY_RIGHT_PIN)==0);
			return 6;
		}
	}
	return 0;
}

对讲模块功能设置

对讲机功能设置使用UART与对讲机模块进行通信,并通过AT指令进行参数的设置。

void Uart_TxData(uint8_t index, uint8_t level)
{
	uint8_t *aTxBuffer;
	uint8_t tx_len=0;
	switch(index)
	{
		case 0://音量
			AT_CMD2[10] = 0x31+level;
			aTxBuffer = AT_CMD2;
			tx_len=13;
			AT24CXX_WriteOneByte(ADDR_VOL, level);
			break;
		case 1://亚音
			strncpy(AT_CMD1+30, subtone[level],2);
			strncpy(AT_CMD1+33, subtone[level],2);
			aTxBuffer = AT_CMD1;
			tx_len=41;
			AT24CXX_WriteOneByte(ADDR_SUB, level);
			break;
		case 2://射频开关
			HAL_GPIO_WritePin(GPIOB, PD_PIN, level);
			AT24CXX_WriteOneByte(ADDR_SW, level);
			return;
			break;
		case 3://扰频
			AT_CMD4[16] = 0x30+level;
			aTxBuffer = AT_CMD4;
			tx_len=21;
			AT24CXX_WriteOneByte(ADDR_SCR, level);
			break;
		case 4://免提灵敏度
			AT_CMD3[10] = 0x30+level;
			aTxBuffer = AT_CMD3;
			tx_len=13;
			AT24CXX_WriteOneByte(ADDR_VOX, level);
			break;
		case 5://静噪级别
			AT_CMD4[10] = 0x30+level;
			aTxBuffer = AT_CMD4;
			tx_len=21;
			AT24CXX_WriteOneByte(ADDR_SQU, level);
			break;
		case 6://麦克灵敏度
			AT_CMD4[12] = 0x30+level;
			aTxBuffer = AT_CMD4;
			tx_len=21;
			AT24CXX_WriteOneByte(ADDR_MIC, level);
			break;
	}
	if (HAL_UART_Transmit_IT(&UartHandle, aTxBuffer, tx_len) != HAL_OK) {
			Error_Handler();
	}
}

电量读取

CW2015通过IIC与主控芯片进行通信。在上电时首先设置电池信息,之后通过读取SOC寄存器中的数值即可获取当前电池的电量信息。

uint8_t CW2015_ReadOneByte(uint8_t ReadAddr)
{				  
	uint8_t temp=0;		  	    																 
  I2C_Start();  
	I2C_SendByte(0XC4);   //发送器件地址0XA0,写数据 	   
	I2C_WaitAck(); 
  I2C_SendByte(ReadAddr);   //发送低地址
	I2C_WaitAck();
	I2C_Start();  	 	   
	I2C_SendByte(0XC5);           //进入接收模式			   
	I2C_WaitAck();	 
  temp=I2C_ReadByte(0);		   
  I2C_Stop();//产生一个停止条件	    
	return temp;
}

参数存储

对讲机的频道和其相关功能设置参数存储在AT24C02中,从而实现掉电存储功能。AT24C02通过IIC与主机进行通信。具体存储格式如下:

地址存储内容表示范围
0x00音量0-7
0x01亚音0-4
0x02射频开关0-1
0x03扰频0-7
0x04免提灵敏度0-8
0x05静噪级别0-9
0x06麦克灵敏度0-7

整体核心代码

其中page_index为判断当前为主页还是菜单页。current_index和temp_list_level分别为当前菜单选中的功能和该功能参数的等级。 之后对OLED、UART、按键GPIO、CW2015进行初始化。最后进入循环,通过不断判断按键按下的状态从而实现功能的控制。

int main(void)
{
	uint8_t page_index=0;//page index
	uint8_t current_index=0;//list index
	uint8_t temp_list_level = 0;
	uint8_t key_index = 0;//key index
	
	uint8_t i=0, list_len = (sizeof(list)/sizeof(MENU_LIST));
	
  /* 初始化所有外设,Flash接口,SysTick */
  	HAL_Init();
	UART2_INIT();
	Get_Init_Data();
	
	Show_HomePage(list[0].current_level, list[2].current_level);
	
	CW2015_WriteOneByte(0x0a,0xff);//wake up cw2015
	HAL_Delay(50);
	CW2015_WriteOneByte(0x0a,0x00);//wake up cw2015
	HAL_Delay(50);
	uint8_t battary_data = 0;
	battary_data = CW2015_ReadOneByte(0x08);
	battary_data = battary_data & 0x02;
	HAL_Delay(50);
	if(battary_data == 0)
	{
		Update_Bat_Info();
	}
	temp_list_level = list[0].current_level;
  while (1)
  {
		if(page_index == 0 && display_on)
		{
			OLED_ShowChar(8, 0, list[0].current_level + 0x30, 8, 1);
			battary_data = CW2015_ReadOneByte(0x04);
			HAL_Delay(50);
			OLED_ShowNum(127-24,0,battary_data,3,8,1);	
			OLED_ShowChar(127-6,0,'%',8,1);
			OLED_Refresh();
		}
		
		key_index=Key_Scan();
		if(key_index == 1)
		{
			switch(page_index)//key_select
			{
				case 0://enter menu
					if(display_on){
						page_index +=1;
						current_index=oled_showlist(list_len, current_index, (MENU_LIST *)list, 2);
					}else{
						Show_HomePage(list[0].current_level, list[2].current_level);
						display_on = 1;
					}
					break;
				case 1://select setting
					list[current_index].current_level = temp_list_level;
					Uart_TxData(current_index, list[current_index].current_level);
					break;
				default:
					break;
			}
		}else if(key_index == 2)//key_return
		{
			switch(page_index)
			{
				case 0://none
					display_on = 0;
					OLED_Clear();
					OLED_Refresh();
//					current_index=oled_showlist(list_len, current_index, (MENU_LIST *)list, 1);
					break;
				case 1://return home page
					page_index -= 1;
					Show_HomePage(list[0].current_level, list[2].current_level);
					current_index=0;
					break;
				default:
					break;
			}
		}else if(key_index == 3)//key_up
		{
			if(page_index == 1)//current is menu page
			{
				current_index=oled_showlist(list_len, current_index, (MENU_LIST *)list, 0);//previous(up)
				temp_list_level = list[current_index].current_level;
			}else
			{
				Set_Level_Silent((MENU_LIST *)list, 0, 1);
				Uart_TxData(0, list[0].current_level);
			}
		}else if(key_index == 4)//key_down
		{
			if(page_index == 1)
			{
				current_index=oled_showlist(list_len, current_index, (MENU_LIST *)list, 1);//next(down)
				temp_list_level = list[current_index].current_level;
			}else
			{
				Set_Level_Silent((MENU_LIST *)list, 0, 0);
				Uart_TxData(0, list[0].current_level);
			}
		}else if(key_index == 5)//key_left
		{
			if(page_index == 1)
			{
				temp_list_level= Set_Level((MENU_LIST *)list, current_index, temp_list_level, 0);
			}else{
				channel_index = Uart_SetFreq(channel_index, 0);
			}
		}else if(key_index == 6)//key_right
		{
			if(page_index == 1)
			{
				temp_list_level = Set_Level((MENU_LIST *)list, current_index, temp_list_level, 1);
			}else
			{
				channel_index = Uart_SetFreq(channel_index, 1);
			}
		}
		
		key_index = 0;
  }
}

实物展示

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

设计注意事项

  • 在电路设计中未添加锂电池保护电路,建议选购带有保护电路的锂电池以确保安全使用。
  • 在进行烧录程序时,建议使用DAPLink调试器进行烧录(理论上也可通过串口进行烧录)。在使用DAPLink烧录时,请确保烧录器的3v3和GND与板子相连,以避免烧录失败的可能性。如果出现烧录失败情况,可以适当降低烧录器的时钟频率,并重新尝试。
  • 在使用CW2015时,需要先写入电池信息,然后再读取SOC,以确保读取到的SOC值准确。具体内容可以参考:CW2015电量计使用
  • 注意SX1308芯片的EN引脚控制的是电压转换开关,而不是输出开关。当EN引脚接低电平时,该芯片仍然会输出电压。在第一版设计时可能未注意到这一问题,原理图和PCB对此已进行更正。
  • 天线的匹配电路根据需要进行焊接。若无阻抗匹配条件,也可以使用0欧电阻直接将其短路,经验证也可实现较远距离的通信。
  • 在未连接天线时,请勿按下PTT键,可能会造成未知后果。
  • 软包锂电池接口的尺寸及线序为:1.25黑红

注意!!!

该项目仅供学习交流使用,测试使用时请遵守相关法律法规!
中华人民共和国无线电管理条例
关于公众对讲机管理有关问题的通知

相关链接

  • 31
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zxfeng~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值