二、软件部分
1、初始化
模块的波特率为115200,8位数据位,1位停止位,没有校验位和流控。
之前有讲到串口部分,参看:STM32开发 – 串口详解
void Bsp_Usart_Init(u8 USART_ID, u32 baud_rate)
{
GPIO_InitTypeDef gpio_init;
USART_InitTypeDef usart_init;
USART_ClockInitTypeDef usart_clk_init;
/* ----------------- INIT USART STRUCT ---------------- */
usart_init.USART_BaudRate = baud_rate;
usart_init.USART_WordLength = USART_WordLength_8b;
usart_init.USART_StopBits = USART_StopBits_1;
usart_init.USART_Parity = USART_Parity_No ;
usart_init.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
usart_init.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
usart_clk_init.USART_Clock = USART_Clock_Disable;
usart_clk_init.USART_CPOL = USART_CPOL_Low;
usart_clk_init.USART_CPHA = USART_CPHA_2Edge;
usart_clk_init.USART_LastBit = USART_LastBit_Disable;
/*-------------------USART1 用作GSM通信 -----------*/
if (USART_ID == DEF_USART_1)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_USART1,ENABLE); //通讯角映射到PB6,PB7
/* Configure GPIOA.9 as push-pull USART1-TX */
gpio_init.GPIO_Pin = BSP_GPIOB_USART1_TX_PINS;
gpio_init.GPIO_Speed = GPIO_Speed_10MHz;
gpio_init.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &gpio_init);
/* Configure GPIOA.10 as input floating USART1-RX */
gpio_init.GPIO_Pin = BSP_GPIOB_USART1_RX_PINS;
gpio_init.GPIO_Mode = GPIO_Mode_IN_FLOATING;
//gpio_init.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOB, &gpio_init);
/* ------------------ SETUP USART1 -------------------- */
USART_Init(USART1, &usart_init);
USART_ClockInit(USART1, &usart_clk_init);
// Clean interrupt flag, and disable txd & rxd interrupt
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
USART_ClearITPendingBit(USART1, USART_IT_TXE);
USART_GetFlagStatus(USART1, USART_FLAG_TC);/* 记得加上,不先读一下第一字节会发不出去,*/
USART_Cmd(USART1, ENABLE);
}
else if (USART_ID == DEF_USART_2)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
/* ----------------- SETUP USART2 GPIO ---------------- */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* Configure GPIOA.2 as push-pull */
gpio_init.GPIO_Pin = GPIO_Pin_2;
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
gpio_init.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &gpio_init);
/* Configure GPIOA.3 as input floating */
gpio_init.GPIO_Pin = GPIO_Pin_3;
gpio_init.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &gpio_init);
/* ------------------ SETUP USART2 -------------------- */
USART_Init(USART2, &usart_init);
//USART_ClockInit(USART2, &usart_clk_init);
// Clean interrupt flag, and disable txd & rxd interrupt
USART_ITConfig(USART2, USART_IT_RXNE, DISABLE);
USART_ITConfig(USART2, USART_IT_TXE, DISABLE);
USART_ClearITPendingBit(USART2, USART_IT_RXNE);
USART_ClearITPendingBit(USART2, USART_IT_TXE);
USART_GetFlagStatus(USART2, USART_FLAG_TC);/* 记得加上,不先读一下第一字节会发不出去,*/
USART_Cmd(USART2, ENABLE);
}
else if (USART_ID == DEF_USART_3)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
/* Configure GPIOB.10 as push-pull */
gpio_init.GPIO_Pin = GPIO_Pin_10;
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
gpio_init.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &gpio_init);
gpio_init.GPIO_Pin = GPIO_Pin_11;
gpio_init.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &gpio_init);
/* ------------------ SETUP USART3 -------------------- */
USART_Init(USART3, &usart_init);
USART_ClockInit(USART3, &usart_clk_init);
// Clean interrupt flag, and disable txd & rxd interrupt
USART_ITConfig(USART3, USART_IT_RXNE, DISABLE);
USART_ITConfig(USART3, USART_IT_TXE, DISABLE);
USART_ClearITPendingBit(USART3, USART_IT_RXNE);
USART_ClearITPendingBit(USART3, USART_IT_TXE);
USART_GetFlagStatus(USART3, USART_FLAG_TC);/* 记得加上,不先读一下第一字节会发不出去,*/
USART_Cmd(USART3, ENABLE);
}
//--------------------UART4 打印 调试窗口
else if (USART_ID == DEF_UART_4)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE);
/* ----------------- SETUP USART4 GPIO ---------------- */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
/* Configure GPIOC.10 as push-pull */
gpio_init.GPIO_Pin = GPIO_Pin_10;
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
gpio_init.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOC, &gpio_init);
/* Configure GPIOC.11 as input floating */
gpio_init.GPIO_Pin = GPIO_Pin_11;
gpio_init.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOC, &gpio_init);
/* ------------------ SETUP UART4 -------------------- */
USART_Init(UART4, &usart_init);
USART_ClockInit(UART4, &usart_clk_init);
// Clean interrupt flag, and disable txd & rxd interrupt
USART_ITConfig(UART4, USART_IT_RXNE, DISABLE);
USART_ITConfig(UART4, USART_IT_TXE, DISABLE);
USART_ClearITPendingBit(UART4, USART_IT_RXNE);
USART_ClearITPendingBit(UART4, USART_IT_TXE);
USART_GetFlagStatus(UART4, USART_FLAG_TC);/* 记得加上,不先读一下第一字节会发不出去,*/
USART_Cmd(UART4, ENABLE);
}
}
}
static void BSP_NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/*!!注意!!*/
/*应用程序参数地址定义在CPU FLASH中间位置(具体定义见hal_h.h宏定义)*/
/*如果去掉程序偏移,当写系统参数时将导致直接改写应用程序代码,从而导致死机*/
//设置向量地址偏移
/* Set the Vector Table base location at 0x0800A000 */
//NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0xA000);
//NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0);
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x2000);
/* Configure one bit for preemption priority */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
/* USART1_IRQn中断 [GSM] */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* CAN1接收中断 */
NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* USART2_IRQn中断 [GPS] */
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 4;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* USART3_IRQn中断 [IBeacon] */
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 5;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
这里需要注意的几点:
1、串口时钟使能
APB1(低速)、APB2(高速)
APB2负责 AD,I/O,高级TIM,串口1
APB1负责 DA,USB,SPI,I2C,CAN,串口2345,普通TIM,PWR
USART1是挂载在 APB2 下面的外设,所以使能函数为:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1);
2、GPIO端口模式设置
TX的GPIO工作模式为:GPIO_Mode_AF_PP;//复用推挽输出
RX的GPIO工作模式为:GPIO_Mode_IN_FLOATING;//浮空输入或者上拉输入
2、AT指令操作
AT 即 Attention,AT 指令集是从终端设备(Terminal Equipment,TE)或数据终端设备(DataTerminal Equipment, DTE)向终端适配器(Terminal Adapter, TA)或数据电流终端设备(DataCircuit Terminal Equipment, DCE)发送的。通过 TA, TE 发送 AT 指令来控制移动台(MobileStation, MS)的功能,与 GSM 网络业务进行交互。用户可以通过 AT 指令进行呼叫、短信、电话本、数据业务、传真等方面的控制。
AT 指令必须以“AT”或“at”开头,以回车()结尾。模块的响应通常紧随其后,格式为: <回车><换行><响应内容><回车><换行>。
GPRS 通信
相信都能在网上找到相关的AT指令手册,所以不一一介绍了。
这里着重看一下GPRS 通信相关的AT指令。
1,ATI
显示模块信息
ATI
Manufacturer: LYNQ
Model: LYNQ_L506
Revision: L506v03.02bxxxx
SN: P4HC1204020xxx
IMEI: 86569903007xxxx
+GCAP: +CGSM,+MS,+DS
OK
2,ATE0
关闭回显
ATE0
OK
在通过电脑串口调试助手调试的时候,我们发送: ATE1,开启回显,可以方便调试,但是我们通过单片机程序控制的时候,用不到回显功能,所以发送: ATE0,将其关闭。
3,AT+CPIN?
查询SIM卡的状态
AT+CPIN?
+CPIN: READY
OK
4,AT+CREG=2
网络注册信息设置命令,自动上报网络注册未知结果码,同时带有位置信息,+CREG: [,,]
AT+CREG=2
OK
5,AT+CICCID
读取SIM卡的CICCID
AT+CICCID
+CICCID: 898607B32217710307xx
OK
6,AT+CSQ
查询信号,最大的有效值是 31
AT+CSQ
+CSQ: 21,99
OK
7,AT+CREG?
网络注册信息查询命令,当stat的值为1(本地网络)或5(漫游)的时,网络注册成功。
AT+CREG?
+CREG: 2,1,"FFFE","160D601",7
OK
8,AT+CGATT?
查询是否附着网络,0是分离,1是附着
AT+CGATT?
+CGATT: 1
OK
9,AT+CGATT=1
设置附着网络
AT+CGATT=1
OK
10,AT+CIPMODE=0
设置传输模式
参数:
–> 0: 非透明传输模式, 1: 透明传输模式(透明传输模式下新建的连接将被强制到 0 号连接上)
AT+CIPMODE=0
OK
透明传输是指不管所传数据是什么样的比特组合,都应当能够在链路上传送。当所传数据中的比特组合恰巧与某一个控制信息完全一样时,就必须采取适当的措施,使收方不会将这样的数据误认为是某种控制信息。这样才能保证数据链路层的传输是透明的。
11,AT+NETOPEN
打开封包网络
AT+NETOPEN
OK
12,AT+CIPOPEN=0,”TCP”,”211.152.x.xxx”,10102,0
建立与TCP服务器的连接
AT+CIPOPEN=0,"TCP","211.152.x.xxx",10102,0
OK
+CIPOPEN:SUCCESS,0
13,AT+CIPSEND=0,4
在 0 号 link 上发送 4 个字符
AT+CIPSEND=0,4
>2233
+CIPSEND:SUCCESS,0,4,4
// 0:连接索引 4:请求发送数据个数 4:实际发送数据个数
14,CIPRXGET
AT+CIPRXGET=0,0
设置接收模式,默认应该是自动获取网络数据的方式,0号link。所以可以不配置?
AT+CIPRXGET=0,0
OK
然后,自动接收模式下在 0 号 link 上收到数据(无需用户使用指令读取)
+CIPRXGET: SUCCESS,0,0,15,
//成功收到数据 0:自动接收模式 1:连接索引 15:接收数据个
ddddddddddddddf //当前读取的数据
15,AT+CIPCLOSE=0
关闭 0 号 link
AT+CIPCLOSE=0
OK
+CIPCLOSE: SUCCESS,0
在网络或者服务器主动断开的情况下系统会自动给出如下提示:
+SERVER DISCONNECTED:0 //0:被动断开的连接索引 (服务器断开提示)
+NETWORK DISCONNECTED:0//0:被动断开的连接索引(网络断开提示)
16,AT+NETCLOSE
关闭网络
AT+NETCLOSE
OK
+NETCLOSE: SUCCESS
3、GPIO配置
之前有讲到GPIO部分,参看:STM32开发 – GPIO详解
然后再看原理图,主要是串口收发、EN_GSM、PWRKEY、STATUS这几个引脚配置
GPIO配置方法的话都是大同小异的。
这里需要注意的几点:
1、GPIO 8种工作模式应用场合
(1) GPIO_Mode_AIN 模拟输入
(2) GPIO_Mode_IN_FLOATING 浮空输入
(3) GPIO_Mode_IPD 下拉输入
(4) GPIO_Mode_IPU 上拉输入
(5) GPIO_Mode_Out_OD 开漏输出
(6) GPIO_Mode_Out_PP 推挽输出
(7) GPIO_Mode_AF_OD 复用开漏输出
(8) GPIO_Mode_AF_PP 复用推挽输出
这8种工作模式相比很清楚。但是它们的应用场合需要搞清楚。
①上拉输入、下拉输入可以用来检测外部信号;例如,按键等;
②浮空输入模式,由于输入阻抗较大,一般把这种模式用于标准通信协议的I2C、USART的接收端;
③普通推挽输出模式一般应用在输出电平为0和3.3V的场合。而普通开漏输出模式一般应用在电平不匹配的场合,如需要输出5V的高电平,就需要在外部一个上拉电阻,电源为5V,把GPIO设置为开漏模式,当输出高阻态时,由上拉电阻和电源向外输出5V电平。
④对于相应的复用模式(复用输出来源片上外设),则是根据GPIO的复用功能来选择,如GPIO的引脚用作串口的输出(USART/SPI/CAN),则使用复用推挽输出模式。如果用在I2C、SMBUS这些需要线与功能的复用场合,就使用复用开漏模式。
⑤在使用任何一种开漏模式时,都需要接上拉电阻。
TX的GPIO工作模式为:GPIO_Mode_AF_PP;//复用推挽输出
RX的GPIO工作模式为:GPIO_Mode_IN_FLOATING;//浮空输入或者上拉输入
EN_GSM的GPIO工作模式为:GPIO_Mode_Out_PP;//推挽输出
PWRKEY的GPIO工作模式为:GPIO_Mode_Out_PP;//推挽输出
STATUS的GPIO工作模式为:GPIO_Mode_IPU;上拉输入
PS,STATUS使用了三极管,信号取反。正常GPIO工作模式应为:
GPIO_Mode_IPD;下拉输入
2、引脚输出高/低电平
GPIO_SetBits 置高电平
GPIO_ResetBits 置低电平
根据上一篇文章所讲
- 4G模组开机:
PWRKEY先低电平,延时500ms,然后再高电平。
PS,又因为使用了三极管,信号取反。
GSM_PORKEY_LOW();
DelayMS(500);
GSM_PORKEY_HIGH();
#define GSM_PORKEY_LOW() (GPIO_SetBits (BSP_GPIOC_PORTS, BSP_GPIOC_GSM_PORKEY_PINS))
#define GSM_PORKEY_HIGH() (GPIO_ResetBits (BSP_GPIOC_PORTS, BSP_GPIOC_GSM_PORKEY_PINS))
- 模块主电源使能引脚(EN_GSM)
控制 EN_GSM 引脚,高电平GSM工作,低电平GSM不工作。
#define GSM_POWER_ON() (GPIO_SetBits (BSP_GPIOA_PORTS, BSP_GPIOA_GSM_POWEN_PINS))
#define GSM_POWER_OFF() (GPIO_ResetBits (BSP_GPIOA_PORTS, BSP_GPIOA_GSM_POWEN_PINS))
3、 读取指定端口管脚的输入
GPIO_ReadInputDataBit 读取指定端口管脚的输入
返回值为:0(低电平)、1(高电平)
- STATUS引脚作为模块状态标志,高电平表示开机,低电平表示关机。
GPIO_ReadInputDataBit 返回值 0(低电平、关机)、1(高电平、开机)
PS,又因为使用了三极管,信号取反。
DETECT_GSM_STATUS() 返回值 0 (开机),1 (关机)
#define DETECT_GSM_STATUS() (GPIO_ReadInputDataBit (BSP_GPIOC_PORTS, BSP_GPIOC_GSM_STATUS_PINS))