🎀 文章作者:二土电子
🌸 关注公众号获取更多资料!
🐸 期待大家一起学习交流!
文章目录
一、SIM900A简介
在一些项目设计中,我们可能会用到发送短信的功能,我们可以借助SIM900A模块来实现,该模块使用方便,只需要用简单的AT指令即可实现发送短信的功能,当然SIM900A的功能并不仅仅是发送短信,它还有其他可扩展的功能,但是本文只讨论它的短信发送功能最终给出其程序实现。下面我们来看一下SIM900A长什么样子
二、Unicode简介
2.1 什么是Unicode
Unicode被称为统一码或者万国码,我们这里仅仅介绍Unicode是什么,至于它是如何进行编码的这种更加深入的问题这里不去探讨,大家可以自行搜索了解一下。个人认为,Unicode可以理解成一个字符集,它的出现是为了能够将全世界的字符用一个统一的表来覆盖,每一个字符都有其唯一对应的二进制数,这样就可以避免发生一个在甲编码方案下编辑保存的文件转到乙编码方案下打开出现乱码的情况。
2.2 Unicode转换工具
我们在使用SIM900A是需要将收件人手机号和短信内容转换成Unicode,我们可以利用Unicode转换工具来实现
在“双字节汉字”中输入收信人手机号或者短信内容,点击“汉字转Unicode”即可得到对应的Unicode码,使用时注意删掉中间的空格。
三、部分AT指令简介
使用简单的AT指令即可控制SIM900A发送短信,下面简单介绍一下常用的几条AT指令(以发送中文消息为例配置参数),至于其他的AT指令,可至文末链接下载查看。
- AT+CPIN?
查询SIM卡是都可以正常使用,如果返回+CPIN:READY(换行回车)OK,说明SIM卡可以正常使用。 - AT+CMGF=1
设置短信模式为文本模式。 - AT+CSCS=“UCS32”
编码模式设置为UCS32。 - AT+CSMP=17,167,0,8
设置短信文本模式参数,关键点子啊与最后一个参数,最后一个参数为0时,表示为英文模式,第四位参数为8时表示为中文普通文本模式。 - AT+CMGS=“收信人手机号的Unicode码”
设置收信人手机号,准备发送短信,发送完该条指令后,可以直接发送转换好的短信内容。
最后在发送0x1A,等待一小段时间收信人就会收到我们编辑的短信。
四、发送短信调试
在准备开始程序设计前我们可以先利用USB-TTL来尝试发送一下短信,首先我们来看一下接线
USB-TTL | SIM900A |
---|---|
5V | VCC |
GND | GND |
TXD | 5VR |
RXD | 5VT |
连接好线后我们再将收信人手机号转换成Unicode码,注意删掉中间的空格。
然后把需要发送的文本内容编辑好,最后我们打开串口助手,依次发送上面AT指令小节中介绍的内容即可,需要注意的是,在发送0x1A时需要将串口助手的发送模式调整为HEX模式。
五、程序设计
上面利用串口助手调试发送短信成功后,下面的程序设计就不复杂了,只需要用程序代码实现发送上面那些AT指令即可。
5.1 串口初始化程序
串口初始化程序可以利用一个函数初始化两个串口,下面的指令发送函数中会有利用串口2反馈指令发送成功失败的提示,如果需要的话可以取消注释同时利用此函数初始化一下串口2。
/*
*==============================================================================
*函数名称:uart_init
*函数功能:初始化USART
*输入参数:UARTx:串口几;bound:波特率
*返回值:无
*备 注:可以修改成输入初始化哪个USART
*==============================================================================
*/
void uart_init(UART_TypeDef UARTx,u32 bound)
{
// 相关结构体定义
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
switch (UARTx)
{
case 0:
// 使能USART1,GPIOA时钟
RCC_APB2PeriphClockCmd (RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
// USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; // PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA.9
// USART1_RX GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA.10
// Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ; // 抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; // 子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // IRQ通道使能
NVIC_Init(&NVIC_InitStructure); // 根据指定的参数初始化VIC寄存器
// USART 初始化设置
USART_InitStructure.USART_BaudRate = bound; // 串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No; // 无奇偶校验位
// 无硬件数据流控制
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 收发模式
USART_Init(USART1, &USART_InitStructure); // 初始化串口1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 开启串口接收中断
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); // 使能空闲中断
USART_Cmd(USART1, ENABLE); // 使能串口1
break;
case 1:
// 使能USART2,GPIOA时钟
RCC_APB1PeriphClockCmd (RCC_APB1Periph_USART2 | RCC_APB2Periph_GPIOA, ENABLE);
// USART2_TX GPIOA.2
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; // PA.2
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA.2
// USART2_RX GPIOA.3初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; // PA3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA.3
// Usart2 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ; // 抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; // 子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // IRQ通道使能
NVIC_Init(&NVIC_InitStructure); // 根据指定的参数初始化VIC寄存器
// USART2 初始化设置
USART_InitStructure.USART_BaudRate = bound; // 串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No; // 无奇偶校验位
// 无硬件数据流控制
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 收发模式
USART_Init(USART2, &USART_InitStructure); // 初始化串口2
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); // 开启串口接收中断
USART_ITConfig(USART2, USART_IT_IDLE, ENABLE); // 使能空闲中断
USART_Cmd(USART2, ENABLE); // 使能串口2
break;
default:
break;
}
}
5.2 串口中断服务函数
/*
*==============================================================================
*函数名称:USART1_IRQHandler
*函数功能:USART1中断服务函数
*输入参数:无
*返回值:无
*备 注:无
*==============================================================================
*/
u32 gReceCount = 0; // 接收计数变量
u32 gClearCount = 0; // 清空接收数组计数变量
u8 gReceFifo[1500]; // 接收数组
u8 gReceEndFlag = 0; // 接收完成标志位
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收到一个字节
{
gReceFifo[gReceCount++] = USART_ReceiveData(USART1);
}
else if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET) //接收到一帧数据
{
USART1->SR; // 先读SR
USART1->DR; // 再读DR
gReceEndFlag = 1; // 接收完成标志置1
}
}
5.3 串口接收内容解析函数
需要注意的是该函数只能检测到返回信息中包含“OK”的指令,至于其他不包含“OK”的返回信息无法检测。
/*
*==============================================================================
*函数名称:Uart_Rece_Pares
*函数功能:解析串口接收内容
*输入参数:无
*返回值:无
*备 注:无
*==============================================================================
*/
extern u8 gOkFlag; // 配置成功标志位
void Uart_Rece_Pares(void) // 串口接收内容解析函数
{
u16 tempVar = 0; // 临时循环变量
if (gReceEndFlag == 1) // 如果接收完成
{
// 解析接收内容
for (tempVar = 0;tempVar < gReceCount;tempVar ++)
{
if (gReceFifo[tempVar] == 'O' && gReceFifo[tempVar + 1] == 'K')
{
gOkFlag = 1; // 成功标志位置1
break;
}
}
// 清空接收数组
for (gClearCount = 0;gClearCount < gReceCount;gClearCount ++)
{
gReceFifo[gClearCount] = ' ';
}
gReceEndFlag = 0; // 清除接收完成标志位
gReceCount = 0; // 清零接收计数变量
}
}
5.4 串口发送函数
/*
*==============================================================================
*函数名称:USART_Send
*函数功能:串口发送函数
*输入参数:str:要发送的数据的数组首地址;UARTx:串口几
*返回值:无
*备 注:调用前先将需要发送的内容利用sprintf()函数转换成字符串,再进行发送
*==============================================================================
*/
void USART_Send (UART_TypeDef UARTx,u8 *str)
{
u8 index = 0;
do
{
switch (UARTx)
{
case 0:
USART_SendData(USART1,str[index ++]);
while (USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
break;
case 1:
USART_SendData(USART2,str[index ++]);
while (USART_GetFlagStatus(USART2,USART_FLAG_TXE) == RESET);
break;
}
}
while(str[index] != 0);
}
5.5 指令发送函数
/*
*==============================================================================
*函数名称:Med_Sim900a_SendCmd
*函数功能:SIM900A发送指令
*输入参数:str:要发送的指令
*返回值:无
*备 注:调用前先将需要发送的内容利用sprintf()函数转换成字符串
串口1发送指令,串口2返回信息
*==============================================================================
*/
u8 gSendCunt = 0; // 记录发送次数
void Med_Sim900a_SendCmd (u8 *str)
{
u8 string[100];
while (!gOkFlag)
{
// 发送AT指令
USART_Send(UART1,str);
delay_ms(1000);
gSendCunt = gSendCunt + 1; // 发送次数加1
// 检测接收内容
Uart_Rece_Pares();
if (gSendCunt > 10)
{
sprintf((char*)string,"%s指令发送失败!\r\n",str);
// USART_Send(UART2,string);
}
}
sprintf((char*)string,"%s指令发送成功!\r\n",str);
// USART_Send(UART2,string);
gSendCunt = 0; // 清零发送次数
gOkFlag = 0; // 清零配置成功变量
}
5.6 SIM900A发送短信业务函数
/*
*==============================================================================
*函数名称:App_Sim900a_SendMsg
*函数功能:SIM900A发送短信
*输入参数:无
*返回值:无
*备 注:有待添加返回值,可更改接收手机号,默认发送“酒驾危险”
*==============================================================================
*/
void App_Sim900a_SendMsg (void)
{
sprintf((char*)gString,"AT+CMGF=1\r\n");
Med_Sim900a_SendCmd(gString);
delay_ms(200);
sprintf((char*)gString,"AT+CSCS=\"UCS2\"\r\n");
Med_Sim900a_SendCmd(gString);
delay_ms(200);
sprintf((char*)gString,"AT+CSMP=17,167,0,8\r\n");
Med_Sim900a_SendCmd(gString);
delay_ms(200);
sprintf((char*)gString,"AT+CMGS=\"%s\"\r\n",NEMBER);
USART_Send(UART1,gString);
delay_ms(500);
sprintf((char*)gString,"%s\r\n",CONTENT);
USART_Send(UART1,gString);
delay_ms(500);
sprintf((char*)gString,"%c\r\n",0x1a);
USART_Send(UART1,gString);
delay_ms(1000);
delay_ms(1000);
}
六、注意事项
- 有些文章推荐使用外部电源供电,但是博主测试时分别使用了USB-TTL供电和单片机5V供电,模块均能正常完成发送短信的业务;
- 在执行短信发送业务时,尽量管不其他中断,否则可能会导致发送失败;
- 使用时注意尽量使用移动的SIM卡,别的SIM卡可能无法使用;
七、资料链接
- SIM900A模块AT指令合集
链接:https://pan.baidu.com/s/1igSMUnegOEBBY0dUcoJDyg
提取码:5qiy
–来自百度网盘超级会员V4的分享 - Chiness_Unicode-中文转UNICON编码工具
链接:https://pan.baidu.com/s/1fS3HglVCI0yRIw0YclbtHw
提取码:nvk6
–来自百度网盘超级会员V4的分享