基于RK3588在Linux系统上使用移远EM05模块

Linux物联网学习实践记录


前言

开发背景

从MCU物联网转向Linux物联网发展,入手了正点原子的RK3588开发板,但是和单片机时期不同的是实战例子很少,所以就记录一下自己手搓的过程。本文主要阐述从0开始搭建环境,最终实现通过AT指令的方式向指定IP发送数据。


一、Linux基本环境配置

1.Windows端环境配置

  1. 用VMware创建一个虚拟机,再安装Ubuntu20.04.具体创建过程不再赘述有很多例子,按照步骤一步一步来不会出错。VMware安装教程
  2. 安装传输软件,MobaXterm(显示开发板的串口打印,也可以通过SSH和虚拟机进行文件传输);FileZilla(windows 和 Ubuntu 相互传输文件,我习惯用这个,目录结构看的很清晰)

2.Ubuntu端环境配置

  1. 搭建FTP服务器
sudo apt-get update
sudo apt-get install vsftpd
sudo vi /etc/vsftpd.conf

安装完毕后,打开配置文件,去掉 line31 write_enable=YES 前的#号注释

  1. 搭建SSH服务器
sudo apt-get install openssh-server
  1. 安装VScode
    打开Ubuntu自带的火狐浏览器,去VScode下载Linux系统版本的安装包,下载完成后用命令
sudo dpkg -i code_1.45.1-1589445302_amd64.deb

4.安装交叉编译器
就是一个翻译官,将你在Ubuntu环境下写的代码转为开发板环境下可执行的文件。所以需要先知道开发板是什么环境。

#查看系统版本
uname -a 
Linux ATK-DLRK3588 5.10.160 #1 SMP Wed Oct 16 10:37:57 CST 2024 aarch64 GNU/Linux

然后在Ubuntu装对应的编译器,其他的gcc等可自行搜索安装

二、编写.c代码

在Linux中写代码和在mcu中有些不同,Linux是针对文件的操作,所以我们想要进行串口通信,需要先查明这个设备是挂载在哪里,然后对这个串口进行配置,然后再向这个串口写入字符串。

代码如下:

int fd;
struct termios options;
const char EM05_default_path[] = "/dev/ttyUSB2"; // 默认的EM05模块挂载路径
EM05_State_e EM05_FsmLoopState = EM05_STATE_DEFAULT;

// 初始化串口
int init_serial(SerialConfig *config, int *fd)
{
    // 打开串口设备
    *fd = open(config->device, O_RDWR | O_NOCTTY | O_SYNC);
    if (*fd < 0)
    {
        perror("open_port: 打开串口设备失败");
        return -1;
    }

    // 清空串口缓冲区
    tcflush(*fd, TCIOFLUSH);
    // 获取当前串口配置
    tcgetattr(*fd, &options);

    // 设置输入输出波特率
    cfsetispeed(&options, config->baudrate);
    cfsetospeed(&options, config->baudrate);
    // 接收使能
    options.c_cflag |= CREAD; // 接收使能
    // 设置数据位
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;
    // 无校验位
    options.c_cflag &= ~PARENB;
    options.c_iflag &= ~INPCK;
    // 1个停止位
    options.c_cflag &= ~CSTOPB;
    // MIN,TIME 设置为0
    options.c_cc[VTIME] = 0;
    options.c_cc[VMIN] = 0;
    // 设置串口属性
    if (tcsetattr(*fd, TCSANOW, &options) != 0)
    {
        perror("tcsetattr: 设置串口属性失败");
        close(*fd);
        return -1;
    }
    else
    {
        printf("串口初始化成功\r\n");
    }

    return 0;
}

// 发送AT命令并读取响应
int send_at_command(int fd, const char *cmd, char *response, size_t response_size)
{
    char buffer[BUFFER_SIZE];
    snprintf(buffer, sizeof(buffer), "%s\r\n", cmd);
    if (write(fd, buffer, strlen(buffer)) < 0)
    {
        perror("写入串口失败");
        return -1;
    }

    fd_set readfds;
    struct timeval timeout;
    int retval;
    size_t total_read = 0;

    FD_ZERO(&readfds);
    FD_SET(fd, &readfds);

    timeout.tv_sec = TIMEOUT_SECONDS;
    timeout.tv_usec = 0;

    while (1)
    {
        retval = select(fd + 1, &readfds, NULL, NULL, &timeout);
        if (retval == -1)
        {
            perror("select调用失败");
            return -1;
        }
        else if (retval == 0)
        {
            fprintf(stderr, "AT命令超时\n");
            break; // 超时退出循环
        }

        int len = read(fd, response + total_read, response_size - total_read - 1);
        if (len < 0)
        {
            perror("读取串口失败");
            return -1;
        }

        total_read += len;

        // 检查是否读取到了完整的响应(通常是以\r\n结尾)
        if (strstr(response + total_read - 2, "\r\n") != NULL)
        {
            break; // 如果找到了\r\n,则认为响应完整,退出循环
        }

        // 如果缓冲区已满且没有找到\r\n,则可能需要更复杂的逻辑来处理(如扩展缓冲区或截断响应)
        if (total_read >= response_size - 1)
        {
            fprintf(stderr, "响应缓冲区溢出\n");
            break; // 缓冲区溢出,退出循环(这里应该根据实际情况决定如何处理)
        }

        // 重置select的超时时间,因为我们可能还没有读取到完整的响应
        timeout.tv_sec = TIMEOUT_SECONDS; // 根据需要,这里可以设置为更短的时间以减少等待
        timeout.tv_usec = 0;

        // 清除readfds,因为select会修改它
        FD_ZERO(&readfds);
        FD_SET(fd, &readfds);
    }

    // 确保响应以null字符结尾
    response[total_read] = '\0';

    // 去除响应末尾的回车换行符(如果存在)
    char *newline_pos = strstr(response, "\r\n");
    if (newline_pos != NULL)
    {
        *newline_pos = '\0';
    }

    printf("读取 %zu 字节: %s\n", total_read, response);
    return 0;
}

// 关闭串口
void close_serial(int fd)
{
    close(fd);
}
/**
 * @description: 查询回复数据
 * @param {char} *keyword 期望的答复
 * @param {char} *source 实际返回的结果
 * @return {*}
 */
int check_response(char *keyword, char *source)
{
    char *pos = strstr(source, keyword);
    if (pos == NULL)
        return -1; // 关键字未找到

    // 找到单词的开始位置
    char *start = pos;
    while (start > source && isalnum((unsigned char)*(start - 1)))
    {
        start--;
    }

    // 找到单词的结束位置
    char *end = pos + strlen(keyword);
    while (*end && isalnum((unsigned char)*end))
    {
        end++;
    }

    // 计算单词的长度(包括开始和结束之间的所有字符)
    size_t length = end - start;

    // 分配内存并复制单词
    char *result = (char *)malloc(length + 1); // +1 for null terminator
    if (result == NULL)
        return -1; // 内存分配失败

    strncpy(result, start, length);
    result[length] = '\0'; // 添加空字符作为字符串结束符

    return result;
}
// 主函数
int main()
{
    SerialConfig config = {EM05_default_path, B115200};
    int fd;

    if (init_serial(&config, &fd) != 0)
    {
        fprintf(stderr, "串口初始化失败\n");
        return EXIT_FAILURE;
    }
    // async_io_init();//调用该函数,初始化串口的异步I/O

    char response[1024];
    if (send_at_command(fd, "AT", response, sizeof(response)) != 0)
    {
        fprintf(stderr, "发送AT命令失败\n");
        close_serial(fd);
        return EXIT_FAILURE;
    }

    printf("AT命令响应: %s\n", response);

    close_serial(fd);
    return EXIT_SUCCESS;
}

三、编译和传输

#对.c文件进行编译
aarch64-linux-gnu-gcc -o emtest EM05_test.c
#传到开发板
scp emtest root@169.254.139.43:/

四、执行

在开发板文件目录下,执行,看到模块的OK回复指令,这样整个就是调通了。之后EM05的模块配置,关闭回显,获取APN,查看SIM卡,开启TCP连接等等,和MCU的流程一样。


总结

本文从0开始,通过串口与EM05模块进行通信,实现功能要求。

RK3399是一款高性能的ARM处理器,常用于嵌入式系统中。它支持Linux操作系统,并且可以搭配AP6255无线模块使用。AP6255使用的是博通BCM43455方案,因此在主线Linux中,只需使能该模块的驱动即可。驱动文件位于`drivers/net/wireless/broadcom`目录下。此外,根据兼容性,还需要配置相应的驱动选项。例如,可以通过配置文件启用以下选项: ``` CONFIG_RFKILL=m CONFIG_RFKILL_LEDS=y CONFIG_RFKILL_INPUT=y ``` 这些选项可以在驱动文件`net/rfkill/rfkill-wlan.c`中找到,其中的宏定义`CONFIG_RFKILL`用于控制驱动的使能。另外,对于RK3399平台,可以在设备树的pinctrl节点下添加对SDIO电源序列的控制,以及wifi使能引脚的配置。具体配置如下: ``` pinctrl节点下面添加: sdio-pwrseq { wifi_enable_h: wifi-enable-h { rockchip,pins = <0 RK_PB2 RK_FUNC_GPIO &pcfg_pull_none>; }; }; ``` 请注意,以上是一般的配置指南,具体的配置可能会依赖于所使用Linux发行版和内核版本。建议参考相关文档和社区资源,以确保正确配置和使用RK3399与AP6255。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [RK3399驱动开发 | 14 - AP6255 SDIO WiFi 调试(基于linux5.4.32内核)](https://blog.csdn.net/Mculover666/article/details/127686569)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值