EC20是一款集成度非常高的4G无线通信模块,支持多种常见通信频段,能满足几乎所有的M2M(MachinetoMachine)应用需求。模块支持TCP/UDP/FTP等一众网络协议,内置多星座高精度定位GNSS接收机,快速提供准确的经纬度信息,UART接口提供AT命令控制和数据传输。
物联网很多的网关设备因需要会安装在有线网络不易布到线的地方,而有些网关则需要跟着运输工具一起移动,那么就需要产品实现与上位机服务器进行无线通信,而4G的无线通信模块就提供了一种非常便利的实现。本例使用了FreeRTOS作为实时操作系统,嵌入式代码运行在FreeRTOS之上,使用提供系统调用完成了多任务编程。
目录
概念说明
实现原理
嵌入式程序
顶层结构
任务与串口初始化
EC20任务
传感器任务
概念说明
- 电平转换:在我们设计的电路中,不同芯片的引脚使用的电压不同,比如常见的1.8V、3.3V、5V等,在两种不同电压芯片引脚之间进行通讯时候,我们需要使得两边的电平都符合自身的需求且能够进行正常的通讯,这就叫电平转换。我们STM32与4G模块连接的控制与通信的引脚就需要做电平转化才能正常通信。
- AT指令:在物联网中,AT(Attention)命令集可用于控制和调测设备、通信模块入网等。AT 指令以"AT"开始,以"\r "或者"\r\n" 结尾,参数之间使用" ," 隔开,字符串参数使用双引号 "" 包裹,整形参数不适用双引号。
- FreeRTOS:FreeRTOS是面向微控制器和小型微处理器的实时操作系统 (RTOS),本例在STM32的平台上使用了FreeRTOS作为实时操作系统,以更合理、更有效地利用CPU的资源,简化应用软件的设计,缩短系统开发时间,提供多任务编程环境,并且更好地保证系统的实时性和可靠性。
- 4G模块:4G模块通常是插针到电路板或将贴片式模块焊到电路板上,并插入相应的SIM卡,通过4G蜂窝网络进行联网。
- 物联网sim卡:物联网(IoT)的发展及其独特的需求推动了物联网SIM卡的发展,IoT SIM卡是传统SIM卡的变体,旨在存储用户信息并将手机连接到对应运营商的蜂窝网络。
- APN:接入点名称(APN)用户的模块通过哪种接入方式来访问什么样的网络。一个典型的APN包含的参数有名称、MCC、MNC、接入点、类型。
实现原理
嵌入式程序运行在STM32芯片之上,通过UART与4G模块进行通信,控制协议为AT指令格式。STM32与4G模块之间还有两个引脚:
- 开/关模块引脚:引脚拉高保持300ms后拉低开机,拉高保持800ms后拉低关机
- 复位引脚:引脚拉高500ms后拉低复位
下图为实现原理的示意图:
嵌入式程序
顶层结构
本例基于STM32平台,完整工程代码已上传。工程一共涉及三个串口,功能分别如下:
- 连接4G模块完成无线数据收发
- 负责与传感器等数据采集模块连接
- 产品参数配置串口
代码的顶层结构由任务和消息队列与中断构成。程序开始初始化消息队列与串口控制器和相关GPIO之后,注册串口接收函数。接收函数在串口接收中断处调用,将信息发送到消息队列中。最后启动三个任务,来处理三个串口接收队列中的信息。完整的数据流图如下:
任务与串口初始化
下面是除了串口的初始化工作,已增加了中文注释帮助理解,主要包括任务的初始化和启动:
#define START_TASK_PRIO 1 //任务优先级
#define START_STK_SIZE 64 //任务堆栈大小
TaskHandle_t StartTask_Handler; //任务句柄
void start_task(void *pvParameters); //开始任务声明
#define EC20_TASK_PRIO 10
#define EC20_STK_SIZE 128
TaskHandle_t ec20_task_handler;
void ec20_task(void *pvParameters);
#define STM32_TASK_PRIO 11
#define STM32_STK_SIZE 128
TaskHandle_t stm32_task_handler;
void stm32_task(void *pvParameters);
#define IDLE_TASK_PRIO 8
#define IDLE_STK_SIZE 128
TaskHandle_t idle_task_handler;
void idle_task(void *pvParameters);
#define SET_TASK_PRIO 9
#define SET_STK_SIZE 128
TaskHandle_t set_task_handler;
void set_task(void *pvParameters);
int main(void)
{
Flash_EnableReadProtection();//开Flash读保护
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
//创建开始任务
xTaskCreate((TaskFunction_t )start_task,
(const char* )"start_task",
(uint16_t )START_STK_SIZE,
(void* )NULL,
(UBaseType_t )START_TASK_PRIO,
(TaskHandle_t* )&StartTask_Handler);
vTaskStartScheduler();
}
/**********************
@GPIO初始化
**********************/
void gpio_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_ResetBits(GPIOB, GPIO_InitStructure.GPIO_Pin);
}
/**********************
@启动任务函数
**********************/
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
gpio_init(); //GPIO初始化
delay_init(); //延时函数初始化
global_init(); //全局变量初始化
msg_init(); //消息队列初始化
uart3_init(global.baud);//串口3初始化
uart1_init(115200);//串口1初始化
uart2_init(global.baud);//串口2初始化
//创建ec20任务
xTaskCreate((TaskFunction_t )ec20_task,
(const char* )"ec20_task",
(uint16_t )EC20_STK_SIZE,
(void* )NULL,
(UBaseType_t )EC20_TASK_PRIO,
(TaskHandle_t* )&ec20_task_handler);
//创建传感器任务
xTaskCreate((TaskFunction_t )stm32_task,
(const char* )"stm32_task",
(uint16_t )STM32_STK_SIZE,
(void* )NULL,
(UBaseType_t )STM32_TASK_PRIO,
(TaskHandle_t* )&stm32_task_handler);
// //创建空闲任务
// xTaskCreate((TaskFunction_t )idle_task,
// (const char* )"idle_task",
// (uint16_t )IDLE_STK_SIZE,
// (void* )NULL,
// (UBaseType_t )IDLE_TASK_PRIO,
// (TaskHandle_t* )&idle_task_handler);
//创建参数设置任务
xTaskCreate((TaskFunction_t )set_task,
(const char* )"set_task",
(uint16_t )SET_STK_SIZE,
(void* )NULL,
(UBaseType_t )SET_TASK_PRIO,
(TaskHandle_t* )&set_task_handler);
vTaskDelete(StartTask_Handler); //删除开始启动任务
taskEXIT_CRITICAL(); //退出临界区
}
下面以串口1为例提供相关初始化以及发送和接收中断代码:
#include "global.h"
#include "usart1.h"
#include "msg.h"
#include "global.h"
extern uint8_t callback_ec20_deault(MSG *rx);
static volatile MSG *tx;
static COMMUCATION_RECV_CALLBACK callBack_recv = 0;
//串口接收中断数据处理函数
static COMMUCATION_RECV_CALLBACK default_callback = callback_ec20_deault;
//串口配置
static void uart1_config(u32 baud)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
//PA9 -> UART1_TX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//PA10 -> UART1_RX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//USART1 参数配置
USART_DeInit(USART1);
USART_InitStructure.USART_BaudRate = baud;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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);
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE ); //¿ª¿ÕÏÐÖжÏ
USART_ITConfig(USART1, USART_IT_ERR, ENABLE); //¿ª´íÎóÖжÏ
USART_ClearFlag(USART1, USART_FLAG_TC);
USART_ClearFlag(USART1, USART_FLAG_RXNE);
USART_ClearFlag(USART1, USART_FLAG_IDLE);
USART_ClearFlag(USART1, USART_FLAG_ORE);
USART_Cmd(USART1, ENABLE);
//NVIC ÅäÖÃ
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6;
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
//dma接收配置 未用
static void dma_recv_config(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_DeInit(DMA1_Channel5);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART1->DR);
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)msg_recv_ec20[0].buf;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = global.bufferSize;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel5, &DMA_InitStructure);
DMA_ClearFlag(DMA1_FLAG_GL4); //Çå³ýDMA1ËùÓбêÖ¾
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
DMA_Cmd(DMA1_Channel5, ENABLE);
}
//dma发送配置 未用
static void dma_send_config(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //DMA1 ʱÖÓ
DMA_DeInit(DMA1_Channel4);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART1->DR);
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)tx->buf;
DMA_InitStructure.DMA_BufferSize = 0;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel4, &DMA_InitStructure);
DMA_ClearFlag(DMA1_FLAG_GL4);
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
DMA_Cmd(DMA1_Channel4, DISABLE);
DMA_ClearFlag(DMA1_FLAG_GL4);
DMA_ClearFlag(DMA1_FLAG_HT4);
DMA_ClearFlag(DMA1_FLAG_TC4);
DMA_ClearFlag(DMA1_FLAG_TE4);
}
//串口1中断处理函数
void USART1_IRQHandler(void)
{
uint32_t sr;
sr = sr;
if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)//·¢ËÍ¿Õ
{
USART_ClearITPendingBit(USART1, USART_IT_TXE);
}
if(USART_GetITStatus(USART1, USART_IT_TC) != RESET)//·¢ËÍÍê³É
{
USART_ClearITPendingBit(USART1, USART_IT_TC);
USART_ITConfig(USART1, USART_IT_TC, DISABLE);
}
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)//ÊÕµ½
{
USART_ClearITPendingBit(USART1, USART_IT_RXNE);//Çå½ÓÊÕÖжϱêÖ¾
}
if(USART_GetITStatus(USART1, USART_IT_ORE) != RESET)
{
sr = USART_ReceiveData(USART1);
USART_ClearITPendingBit(USART1, USART_IT_ORE);
}
if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)//¿ÕÏÐ
{
sr = USART_ReceiveData(USART1);
DMA_Cmd(DMA1_Channel5, DISABLE);
USART_ClearITPendingBit(USART1, USART_IT_IDLE );
msg_recv_ec20[global.cur_ec20_msg].state = 1;
msg_recv_ec20[global.cur_ec20_msg].len = global.bufferSize - DMA_GetCurrDataCounter(DMA1_Channel5);
callBack_recv((MSG*)&msg_recv_ec20[global.cur_ec20_msg]);//启动回调函数函数处理中断内容
callBack_recv = default_callback;
DMA1_Channel5->CMAR = (u32)(ec20_rx()->buf);
DMA1_Channel5->CNDTR = global.bufferSize;
DMA_Cmd(DMA1_Channel5, ENABLE);
}
if(USART_GetITStatus(USART1 ,USART_IT_PE | USART_IT_FE | USART_IT_NE) != RESET)//´íÎó
{
sr = USART_ReceiveData(USART1);
USART_ClearITPendingBit(USART1, USART_IT_PE | USART_IT_FE | USART_IT_NE);
}
}
//串口接收DMA中断
void DMA1_Channel5_IRQHandler(void)
{
if(DMA_GetITStatus(DMA1_IT_TC5) == SET)
{
DMA_ClearITPendingBit(DMA1_IT_TC5);
}
if(DMA_GetITStatus(DMA1_IT_TE5) == SET)
{
DMA_ClearITPendingBit(DMA1_IT_TE5);
}
DMA_ClearITPendingBit(DMA1_IT_TC5);
DMA_ClearITPendingBit(DMA1_IT_TE5);
DMA_Cmd(DMA1_Channel5, DISABLE);
DMA1_Channel5->CNDTR = 100;
DMA_Cmd(DMA1_Channel5, ENABLE);
}
//串口发送DMA中断
void DMA1_Channel4_IRQHandler(void)
{
if(DMA_GetITStatus(DMA1_IT_TC4) == SET)
{
DMA_ClearITPendingBit(DMA1_IT_TC4);
}
if(DMA_GetITStatus(DMA1_IT_TE4) == SET)
{
DMA_ClearITPendingBit(DMA1_IT_TE4);
}
if(DMA_GetITStatus(DMA1_IT_GL4) == SET)
{
DMA_ClearITPendingBit(DMA1_IT_GL4);
}
if(DMA_GetITStatus(DMA1_IT_HT4) == SET)
{
DMA_ClearITPendingBit(DMA1_IT_HT4);
}
DMA_Cmd(DMA1_Channel4, DISABLE);
}
//串口1初始化
void uart1_init(u32 baud)
{
uart1_config(baud);
dma_recv_config();
dma_send_config();
callBack_recv = default_callback;//配置串口中断回调函数
}
//串口1发送函数
uint8_t uart1_send(uint8_t *pt_txbuf , uint32_t tx_len, COMMUCATION_RECV_CALLBACK callback)
{
if(pt_txbuf == 0)
return FALSE;
if(callback != 0)
callBack_recv = callback;
DMA_ClearFlag(DMA1_FLAG_TC4);
DMA_ClearFlag(DMA1_FLAG_TE4);
DMA_Cmd(DMA1_Channel4, DISABLE);
DMA1_Channel4->CMAR = (u32)(pt_txbuf);
DMA1_Channel4->CNDTR = tx_len;
DMA_Cmd(DMA1_Channel4, ENABLE);
return TRUE;
}
下面只介绍 EC20任务和传感器任务的实现,剩下一个任务的实现有兴趣的读者可以到我的资源里把工程代码下载下来进行研究,欢迎评论区留言讨论。
EC20任务
下面是EC20模块初始化,建立TCP连接以及接收数据处理整个状态机在一切结果正常的情况下的流程图:
下面是任务状态机的实现,包括一些模块返回错误的流程跳转,代码中以增加中文注释帮忙理解:
//AT指令
const char chk_baud[] = {"AT+IPR?\r\n"}; //查询波特率
const char set_baud[] = {"AT+IPR=115200\r\n"}; //设置波特率
const char chk_signal[] = {"AT+CSQ\r\n"}; //查询信号强度
const char close_cmd_echo[] = {"ATE0\r\n"}; //关闭数据回显
const char save_param[] = {"AT&W\r\n"}; //保存设置参数,应答超时300ms
const char chk_iccid[] = {"AT+QCCID\r\n"}; //查询ccid
const char chk_pin[] = {"AT+CPIN?\r\n"}; //查询sim卡密码,5s超时
const char chk_pin_back1[] = {"+CME ERROR: 10"}; //sim卡未插好
const char chk_pin_back2[] = {"+CPIN: READY"}; //sim卡准备好
const char chk_sim_reg[] = {"AT+CREG?\r\n"}; //查询sim卡是否注册
const char chk_net_reg[] = {"AT+CGREG?\r\n"}; //查询net注册
//运营商APN
const char set_link_ChinaMobile[] = {"AT+QICSGP=1,1,\"CMNET\",\"\",\"\",1\r\n"};//设置接入点(移动)APN,USERNAME,PASSWORD
const char set_link_UNICOM[] = {"AT+QICSGP=1,1,\"UNINET\",\"\",\"\",1\r\n"};//设置接入点(联通) APN,USERNAME,PASSWORD
const char set_link_p[] = {"AT+QICSGP=1,1,\"CTLTE\",\"\",\"\",1\r\n"};//设置接入点(电信) APN,USERNAME,PASSWORD
const char chk_link_point[] = {"AT+QICSGP=1\r\n"}; //查询接入点
const char active_pdp_context[] = {"AT+QIACT=1\r\n"};//激活接入点150s超时
const char chk_pdp_context[] = {"AT+QIACT?\r\n"}; //查询context,返回当前接入点信息(150s超时)
const char deactive_pdp_context[] = {"AT+QIDEACT=1\r\n"};//关闭接入点40s超时
//const char creat_tcp[] = {"AT+QIOPEN=1,0,\"TCP\",\"120.55.115.113\",5008,0,2\r\n"};//创建TCP连接(150s超时)
//const char creat_tcp[] = {"AT+QIOPEN=1,0,\"TCP\",\"120.26.204.86\",8001,0,2\r\n"};//创建TCP连接(150s超时)
const char creat_tcp_top[] = {"AT+QIOPEN=1,0,\"TCP\",\""};
const char creat_tcp_tail[] = {"\","};//创建TCP连接150s
const char creat_tcp_port[] = {",0,1\r\n"};//服务端口号+传输模式【推送,非透传】
const char creat_tcp_back1[] = {"CONNECT"};//创建TCP成功,透传模式的回应
const char close_tcp[] = {"AT+QICLOSE=0\r\n"}; //关闭TCP连接(自己设置超时时间)
const char chk_tcp[] = {"AT+QISTATE=1,0\r\n"}; //查询TCP连接(指定connetcID)
const char chk_data_echo[] = {"AT+QISDE?\r\n"}; //查询数据回显
//const char close_data_echo[] = {"AT+QISDE=0\r\n"}; //关闭数据回显(透传模式不需要)
const char chk_err_code[] = {"AT+QIGETERROR\r\n"}; //查询最近的错误码
const char exit_transpartent[] = {"+++"}; //退出透传模式
const char exit_transpartent2[] = {"++++++++\r\n"}; //命令模式下+++操作
const char change_transparent_mode[] = {"AT+QISWTMD=1,2\r\n"}; //改为透传模式
const char enter_transparent_mode[] = {"ATO\r\n"}; //进入透传模式
const char common_ack[] = {"OK"}; //通用回复"OK"
const char chk_Operator[] = {"AT+COPS?\r\n"};//查是什么卡,移动联通电信
const char Operater1_ack[] = {"CHINA MOBILE"};//中国移动
const char Operater2_ack[] = {"CHN-UNICOM"};//中国联通
const char Operater3_ack[] = {"CHN-CT"};//中国电信
// const char Operater1_ack[] = {"china mobile"};//中国移动
// const char Operater2_ack[] = {"chn-unicom"};//中国联通
// const char Operater3_ack[] = {"chn-ct"};//中国电信
const char chk_signal_quality[] = {"AT+CSQ\r\n"};//查询信号强度
const char chk_signal_ack[] = {"+CQS"};//查询信号强度返回值
const char remote_recv[] = {"+QIURC"};//服务器数据返回
const char tcp_direct_ack[] = {"+QIOPEN:"};//tcp连接 direct push 模式返回值
const char send_head[] = {"AT+QISEND=0,"};//通知ec20即将发送数据
const char send_head_ack[] = {">"};
const char sended_data_ack[] = {"SEND OK"};
const char query_sended[] = {"AT+QISEND=0,0\r\n"};//查询发送数据
const char query_sended_ack[] = {"+QISEND:"};
/
//EC20主任务
void ec20_task(void)
{
u8 rs;
ec20.fsm = ST_PWR_ON;//初始化状态机
UPDATE_REBOOT_TIME; //初始化重启计时
CLEAR_REBOOT_COUNT; //初始化重启计数
while(1)
{
switch(ec20.fsm)
{
case ST_PWR_ON: //上电
// c4g_power_off();
c4g_power_on();
ec20.fsm = ST_CLOSE_CMD_ECHO;//跳转关闭回显
break;
case ST_REBOOT: //重启
c4g_power_reset();
ec20.fsm = ST_CLOSE_CMD_ECHO;//跳转关闭回显
UPDATE_REBOOT_TIME;
CLEAR_REBOOT_COUNT;
global.reboot_time_count = xTaskGetTickCount();//更新全局数据发送时间间隔
break;
case ST_CLOSE_CMD_ECHO://关闭数据回显
if(c4g_close_cmd_echo())
{
UPDATE_REBOOT_TIME;
ec20.fsm = ST_CHK_SIM;
}
if(JUDGE_REBOOT_TIME(30))//超时则重启
{
ec20.fsm = ST_REBOOT;
printf("EC20 reboot\r\n\r\n");
}
break;
case ST_CHK_SIM: //查看sim卡,物理卡
rs = c4g_chk_sim();
if(rs == TRUE)
{
ec20.fsm = ST_CHK_SIGNAL;//跳转信号强度
UPDATE_REBOOT_TIME;
}else
if(rs == 2)//未插卡
{
printf("no sim\r\n");
UPDATE_REBOOT_TIME;
}
else//重启
{
if(JUDGE_REBOOT_TIME(20))//查询时间超过20s
{
printf("check sim timeout\r\n\r\n");
ec20.fsm = ST_REBOOT;
}
}
break;
case ST_CHK_SIGNAL: //查询信号强度
rs = c4g_chk_signal();
if(rs == TRUE)
{
ec20.fsm = ST_CHK_SIM_REG;//跳转sim卡注册
UPDATE_REBOOT_TIME;
}
else
{
if(JUDGE_REBOOT_TIME(20))//查询时间超过20s
{
printf("check signal timeout\r\n\r\n");
ec20.fsm = ST_REBOOT;
}
}
break;
case ST_CHK_SIM_REG: //查询sim卡注册
rs = c4g_sim_reg();
if(rs == TRUE)
{
ec20.fsm = ST_CHK_NET_REG;
UPDATE_REBOOT_TIME;
}
else
if(rs == 2)
{
if(JUDGE_REBOOT_TIME(90))//查询时间超过90s
{
printf("check sim reg timeout\r\n\r\n");
ec20.fsm = ST_REBOOT;
}
}
break;
case ST_CHK_NET_REG: //查询网络注册
rs = c4g_net_reg();
if(rs == TRUE)
{
ec20.fsm = ST_CHK_OPERATER;//跳转查询运营商
UPDATE_REBOOT_TIME;
}
else
if(rs == 2)
{
if(JUDGE_REBOOT_TIME(60))//查询时间超过60s
{
printf("check net reg timeout\r\n\r\n");
ec20.fsm = ST_REBOOT;
}
}
break;
case ST_CHK_OPERATER: //查询运营商
rs = c4g_check_Operater();
if(rs == TRUE)
{
ec20.fsm = ST_SET_POINT;//跳转设置接入点
UPDATE_REBOOT_TIME;
}
else
{
if(JUDGE_REBOOT_TIME(60))//查询时间超过60s
{
printf("Operater check err\r\n\r\n");//没查到运营商
ec20.fsm = ST_REBOOT;
}
}
break;
case ST_SET_POINT: //设置接入点,APN
rs = c4g_set_point();
if(rs == TRUE)
{
ec20.fsm = ST_ACTIVE_POINT;//Ìøת->"¼¤»î½ÓÈëµã"
UPDATE_REBOOT_TIME;
}
else
{
if(JUDGE_REBOOT_TIME(60))//²éѯʱ¼ä³¬¹ý60s
{
printf("set apn err\r\n\r\n");
ec20.fsm = ST_REBOOT;
}
}
break;
case ST_CHK_POINT: //查询接入点
break;
case ST_ACTIVE_POINT: //激活接入点
rs = c4g_active_context();
if(rs == TRUE)
{
ec20.fsm = ST_CREAT_TCP;//跳转创建TCP
UPDATE_REBOOT_TIME;
CLEAR_REBOOT_COUNT;
}
else
if(rs == 2)//收到数据,但是数据错误
{
printf("active PDP Context err 1\r\n\r\n");
ec20.fsm = ST_DEACTIVE_POINT;//跳转关闭接入点
UPDATE_REBOOT_TIME;
}
else//超过150秒未收到回复
{
printf("active PDP Context err 2\r\n\r\n");
ec20.fsm = ST_REBOOT;//跳转到重启设备
}
break;
case ST_DEACTIVE_POINT: //关闭接入点
rs = c4g_deactive_context();
if(rs == TRUE)
{
ec20.fsm = ST_CHK_SIM;//跳转查询sim卡
CLEAR_REBOOT_COUNT;//清除重启计数
UPDATE_REBOOT_TIME;
}
else
if(rs == 2)//有返回,但是返回数据错误
{
if(JUDGE_REBOOT_COUNT(3))//最多查4次,如果还是失败则重启设置
{
printf("link point err 1\r\n\r\n");//接入点设置失败
ec20.fsm = ST_REBOOT;//跳转重启设备
}
}
else//40秒超时无回答
{
printf("link point err 2\r\n\r\n");//接入点设置失败
ec20.fsm = ST_REBOOT;//跳转重启设备
}
break;
case ST_CHK_CONTEXT: //查询接入点文本
rs = c4g_chk_context();
if(rs == TRUE){//查询成功,但是未激活
ec20.fsm = ST_ACTIVE_POINT;
UPDATE_REBOOT_TIME;
}else
if(rs == 3){//已经激活
ec20.fsm = ST_CHK_TCP;//先查询是否建立了TCP连接,如没有则建立
}else
if(rs == 2){//错误信息
printf("context err 1\r\n\r\n");//查询接入点文本错误
}
break;
case ST_CREAT_TCP: //创建TCP连接
rs = c4g_creat_tcp();
if(rs == TRUE)
{
ec20.fsm = ST_COMMUCATION;//跳转通信
global.socket_state = 1;//tcp连接成功
CLEAR_REBOOT_COUNT;//清除重启计数
UPDATE_REBOOT_TIME;
}
else
if(rs !=0 && rs != TRUE)//有返回,则返回数据错误
{
if(JUDGE_REBOOT_COUNT(5))//五次失败重启
{
char tmp[2];
sprintf(tmp, "%u", rs);
printf("creat tcp err:");
printf((const char*)tmp);
printf("\r\n\r\n");
ec20.fsm = ST_REBOOT;//跳转重启
}
}
else//创建TCP超时,150s
{
printf("creat tcp timout\r\n\r\n");
ec20.fsm = ST_REBOOT;//跳转重启
}
break;
case ST_CHK_TCP: //查询TCP
rs = c4g_chk_tcp();
if(rs == TRUE)//没有建立tcp,需要重新建立
{
ec20.fsm = ST_CREAT_TCP;//跳转创建tcp
UPDATE_REBOOT_TIME;
CLEAR_REBOOT_COUNT;
}
else//********************
if(rs == 3)//TCP连接已存在
{
if( xTaskGetTickCount() - global.reboot_time_count > global.reboot_time_set )//超过系统超时时间
{
global.reboot_time_count = xTaskGetTickCount();//更新时间
ec20.fsm = ST_CLOSE_TCP;//跳转"关闭CTP"
UPDATE_REBOOT_TIME;
}
else
{
ec20.fsm = ST_COMMUCATION;//跳转等待接收数据
UPDATE_REBOOT_TIME;
}
}
else
if(rs == 4)//正在关闭TCP
{
ec20.fsm = ST_CLOSE_TCP;//跳转关闭TCP
UPDATE_REBOOT_TIME;
}
else//超时无人应答
{
printf("chk tcp timeout\r\n\r\n");//查询TCP失败
ec20.fsm = ST_REBOOT;//跳转重启
}
break;
case ST_CLOSE_TCP: //关闭TCP
rs = c4g_close_tcp();
if(rs == TRUE)//关闭TCP成功
{
ec20.fsm = ST_CREAT_TCP;//跳转创建TCP
UPDATE_REBOOT_TIME;
CLEAR_REBOOT_COUNT;
}
else
if(rs == 2)//错误信息
{
if( JUDGE_REBOOT_COUNT(5) )//最多查5次
{
printf("close tcp err\r\n\r\n");
ec20.fsm = ST_REBOOT;//跳转重启
}
}
else
{
printf("close tcp timeout\r\n\r\n");//关闭TCP超时
ec20.fsm = ST_REBOOT;//Ì跳转重启
}
break;
case ST_COMMUCATION://数据通信
{
MSG*cmsg;
u8 err;
err = getting_queue_ec20_remote((void*)&cmsg, 10*OS_TICKS_PER_SEC);//每10s查询一次
if(err == pdTRUE)//收到远端数据
{
send_to_stm32(cmsg);//发送给传感器串口
printf("send to user\r\n\r\n");
}
else
{
if(global.socket_state == 2)
{
printf("direct reboot\r\n\r\n");
ec20.fsm = ST_REBOOT;//跳转重启
}
else
if(global.socket_state == 0)
{
printf("transfer to close tcp\r\n\r\n");
ec20.fsm = ST_CLOSE_TCP;//跳转关闭TCP
UPDATE_REBOOT_TIME;
}
}
}
break;
case ST_CHK_DATA_ECHO: //查询数据回显
printf("idle \r\n\r\n");
break;
case ST_CLOSE_DATA_ECHO://关闭数据回显
break;
case ST_CHK_ERRCODE: //查询最近的错误码
break;
}
}
}
传感器任务
下面是传感器任务的实现,将传感器通过串口的发过来的数据通过EC20模块发送出去:
void stm32_recv_task(void)
{
uint8_t rs, err, resend = 0;
MSG *cmsg;
while(1)
{
err = getting_queue_stm32((void*)&cmsg, global.reboot_time_set);//无限等待传感器队列消息
if(err == pdTRUE)
{
global.reboot_time_count = xTaskGetTickCount();
if(global.socket_state != 1)//tcp未连接
{
msg_free(cmsg);//不处理,并释放队列空间
printf("stm32 recv data no dealwith\r\n");
}
else
{
//1.将数据发送至ec20
//2.查询数据是否发送成功,即tcp是有否回应。如果没有发送成功,此条数据重新丢进队列,关闭TCP,重新连接tcp,重新发送此数据
rs = 0xff; resend = 0;
do
{
rs = send_commucation_head(cmsg->len);
}while(resend++ < 3 && rs != 1);
if(rs == 1)//发送数据头,通知ec20即将发送数据
{
u8 i = 0;
do{
uart1_send(cmsg->buf, cmsg->len, 0);//发数据
rs = sended_result();
if(rs != 1)//发送失败(EC20本身发送失败,并不是发送远端失败)
{
printf("ec20 send err\r\n\r\n");
}
else//发送成功
{
rs = query_sended_result(cmsg->len);//检查发送到remote是否成功
if(rs == 0)//超时90s
{
rs = 111;
global.socket_state = 2;//重启
printf("sended no ack\r\n\r\n");
}
}
}while(i++ < 3 && rs != 1);
if(rs == 1)//发送remote成功
{
}
else/发送失败
{
printf("send data err\r\n\r\n");
if(rs != 111)
{
global.socket_state = 0;//重新建立tcp
printf("sended data fail\r\n\r\n");
}
}
}
else//发送数据头,无'>'回复
{
printf("head is no ack\r\n\r\n");
global.socket_state = 2;//重启
}
msg_free(cmsg);//释放消息,这里无论remote是否收到没有
}
}
else//超过最长发送时间间隔
{
global.socket_state = 2;//重新建立TCP
printf("global timeout\r\n\r\n");
}
}
}
十六宿舍 原创作品,转载必须标注原文链接。
©2023 Yang Li. All rights reserved.文章来源:https://www.toymoban.com/news/detail-438569.html
欢迎关注 『十六宿舍』,大家喜欢的话,给个👍,更多关于嵌入式相关技术的内容持续更新中。文章来源地址https://www.toymoban.com/news/detail-438569.html
到了这里,关于STM32+EC20实现4G无线通信的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!