RT-Thread AT组件源码分析(含修改、以EC20为例)

5 篇文章 0 订阅
4 篇文章 0 订阅

 AT组件设置

路径:RT-Thread Settings / 组件 / 网络 / AT命令

【使能调试日志的输出】:开启后可以看到日志级别为debug的相关日志。

【使能打印通信数据的RAW格式】:开启后可以看到每次执行的 AT 指令以及返回的执行结果。下图为开启后,运行中的打印数据左侧为原始hex数据,右侧为hex数据的ASCII形式

使用建议:

建议在开发、调试、故障排查、故障复现时开启【使能打印通信数据的RAW格式】

由于远程升级过程中数据量巨大且这里是同步打印,会严重影响远程升级功能,发布正式版本、无人值守的现场调试版本必须关闭此选项

AT软件包设置

路径:RT-Thread Settings / 软件包 / IoT-物联网 / AT设备 / 移远EC20

【使能在线程中初始化】:

关闭:AT初始化将在RT-Thread的main函数前,系统初始化中进行,这种情况下AT初始化将非常耗时(初始化失败还会重试5次),从设备上电到程序正常运行(屏幕点击可操作)约需要2分钟。

开启:AT初始化需要自己创建线程进行,从设备上电到程序正常运行耗时短,但是这样会与应用程序中判断离线的逻辑存在一定的冲突。

【电源引脚、电源状态引脚】-1表示不配置,建议不使用软件包控制引脚,最好是在在应用程序中配合应用层联网逻辑控制4G模组得电断电

GPRS网络注册状态检查

AT组件中会自动创建一个网络注册状态检查线程,使用 “AT+CGREG?” 指令进行GPRS网络注册状态的检查,并根据指令返回的结果修改网卡设备的标志位。

线程的入口函数是 ec20_check_link_status_entry(packages/at_device-v2.0.4/class/ec20/at_device_ec20.c),该线程每间隔 EC20_LINK_DELAY_TIME毫秒(当前版本为30s)发送一条 AT 指令进行 GPRS 网络注册状态的检查,并根据返回的结果在函数 netdev_low_level_set_link_status() 中修改 netdev->flags。各种情况的执行结果分析如下所示

上次状态

本次状态

操作

未注册

未注册

未注册

注册

netdev->flags |= NETDEV_FLAG_LINK_UP

执行check_netdev_internet_up_work()函数测试联网

注册

未注册

netdev->flags &= ~NETDEV_FLAG_LINK_UP

netdev->flags &= ~NETDEV_FLAG_INTERNET_UP

若开启网络自动切换,则执行通过check_netdev_internet_up_work()函数切换到默认网络设备

注册

注册

使用建议:

建议关闭,这个线程仅能判断网络注册状态,无法判断TCP连接状态,应用层现有检测离线的逻辑(连续90s未收到数据、连续5帧发送失败(每帧重发3次)等)能够满足使用需求。

关闭方式:

在文件:packages / at_device-v2.0.4 / class / ec20 / at_device_ec20.c

找到函数:void ec20_init_thread_entry(void *parameter)

屏蔽行:ec20_netdev_check_link_status(device->netdev);

连接外网检查,实际使用一定要屏蔽

rt-thread/components/net/sal_socket/src/sal_socket.c 中的 check_netdev_internet_up_work() 函数会自动与 “link.rt-thread.org:8101” 创建UDP连接,进行数据收发测试,从而判断是否可以连接外网。

该函数的执行的过程大致为在 ec20 初始化线程 ec20_init_thread_entry() 中将将外网检查任务提交到 sys_work 工作队列中,系统工作队列处理线程 _workqueue_thread_entry() 会不断的检测是否有需要运行的任务,如果有则执行相应的任务。

建议关闭,这个连接外网检查通过会置网络设备状态为“成功连接至互联网”,由于这个IP的问题,使用定向卡的设备会检查失败,从而影响后续收发的逻辑

关闭方式:

在文件:rt-thread/components/net/sal_socket/src/sal_socket.c

找到函数:

static void check_netdev_internet_up_work(struct rt_work *work, void *work_data)

修改为直接置标志位为成功,如下:

static void check_netdev_internet_up_work(struct rt_work *work, void *work_data)
{
    struct netdev *netdev = (struct netdev *)work_data;
    netdev->flags |= NETDEV_FLAG_INTERNET_UP;
    return;
}

数据接收流程

解析+QIURC时,存在无法正常解析数据的情况,需要增加以下兜底方式,实测有效。

数据接收流程中的修改:

文件:packages / at_device-v2.0.4 / class / ec20 / at_socket_ec20.c

函数:

static void urc_recv_func(struct at_client *client, const char *data, rt_size_t size)

行:

sscanf(data, "+QIURC: \"recv\",%d,%d", &device_socket, &bfsz);

此行作用是解析需要接收的数据长度。

从网络接收数据时,数据会以如下格式直接输 出到 COM 口上:

+QIURC: "recv",<connectID>, <currectrecvlength><CR><LF><data>

RT-Thread中的接收逻辑:

  1. 解析<currectrecvlength>数据,获取有效数据的字节数
  2. 根据字节数来接收数据

因此,如果这个<currectrecvlength>解析出现错误,则接收不到正确的串口数据,导致应用层无法处理数据。

为此,增加了两种不同的<currectrecvlength>解析方式:

  1. 数逗号:<currectrecvlength>一定是在第二个数字的位置,即第一个逗号后
  2. 直接解析对应位置的字符:由于我们所涉及的<connectID>仅一个,不涉及2位数及以上的情况,因此可以确定

   

 sscanf(data, "+QIURC: \"recv\",%d,%d", &device_socket, &bfsz);
    if (0 == bfsz && size)
    {
        LOG_E("[xx]urc_recv_func: size is %d, data is:%s", size, data);
        LOG_E("get bfsz is 0, retry");
        char *result = NULL;
        int count = 0;
        int start_urc_len = -1;
        int urc_len = rt_strlen(data);
        for (int i = 0; i < urc_len; i++)
        {
            if (data[i] == ',')
            {
                count++;
                if (count == 2)
                {
                    start_urc_len = i;
                }
            }
        }
        if (start_urc_len != -1)
        {
            result = (char *)rt_malloc(sizeof(char) * (urc_len - start_urc_len + 2));
            rt_strncpy(result, data + start_urc_len + 1, urc_len - start_urc_len);
            result[urc_len - start_urc_len] = '\0';
            bfsz = atoi(result);
            rt_free(result);
        }
        LOG_E("[xx]urc_recv_func: (manual parsing)bfsz is %d, data is:%s", bfsz, data);
        if (bfsz == 0)
        {
            if (data[17] >= '0' && data[17] <= '9' && data[18] >= '0' && data[18] <= '9')
            {
                bfsz = (data[17] - '0') * 10 + (data[18] - '0');
                LOG_E("[ZGX]urc_recv_func: (manual parsing)bfsz is 0, second is %d", bfsz);
            }
        }
    }

 数据发送流程

  • 28
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我会尽力回答你的问题。首先,RT-Thread是一个实时操作系统,它提供了一些基本的实时操作系统功能,例如线程、信号量、消息队列、定时器等。其中,定时器是一种非常重要的实时操作系统功能,它可以用来在指定的时间间隔内执行一些操作。 在RT-Thread中,定时器的实现是基于软件定时器,即在内核中维护一个定时器列表,每个定时器都有一个超时时间。当定时器到期时,内核会调用定时器回调函数来执行相应的操作。 下面是RT-Thread定时器的源码分析: 1. 定时器结构体 在RT-Thread中,定时器的结构体定义如下: ```c struct rt_timer { char name[RT_NAME_MAX]; /* 定时器名称 */ rt_list_t list; /* 定时器列表 */ rt_tick_t timeout_tick; /* 定时器超时时间 */ rt_tick_t init_tick; /* 定时器初始时间 */ rt_uint8_t flag; /* 定时器标志 */ rt_uint8_t state; /* 定时器状态 */ void (*timeout_func)(void* parameter); /* 定时器回调函数 */ void* parameter; /* 回调函数参数 */ }; ``` 可以看到,定时器结构体包了定时器的名称、超时时间、标志、状态、回调函数等信息。 2. 定时器创建 在RT-Thread中,定时器的创建函数是rt_timer_create(),它的函数原型如下: ```c rt_err_t rt_timer_create(rt_timer_t *timer, const char *name, void (*timeout_func)(void* parameter), void* parameter, rt_tick_t time, rt_uint8_t flag); ``` 其中,timer表示定时器指针,name表示定时器名称,timeout_func表示定时器回调函数,parameter表示回调函数参数,time表示定时器超时时间,flag表示定时器标志。 rt_timer_create()函数会在内核中创建一个定时器,并将定时器添加到定时器列表中。如果创建成功,函数返回RT_EOK,否则返回错误码。 3. 定时器启动 在RT-Thread中,定时器的启动函数是rt_timer_start(),它的函数原型如下: ```c rt_err_t rt_timer_start(rt_timer_t timer); ``` rt_timer_start()函数会启动指定的定时器,并将其状态设置为RT_TIMER_FLAG_ACTIVATED。如果启动成功,函数返回RT_EOK,否则返回错误码。 4. 定时器停止 在RT-Thread中,定时器的停止函数是rt_timer_stop(),它的函数原型如下: ```c rt_err_t rt_timer_stop(rt_timer_t timer); ``` rt_timer_stop()函数会停止指定的定时器,并将其状态设置为RT_TIMER_FLAG_DEACTIVATED。如果停止成功,函数返回RT_EOK,否则返回错误码。 5. 定时器删除 在RT-Thread中,定时器的删除函数是rt_timer_delete(),它的函数原型如下: ```c rt_err_t rt_timer_delete(rt_timer_t timer); ``` rt_timer_delete()函数会删除指定的定时器,并释放相应的资源。如果删除成功,函数返回RT_EOK,否则返回错误码。 以上就是RT-Thread定时器的源码分析,希望能对你有所帮助。如果你有其他问题,可以继续问我。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值