ML307R OpenCPU UART使用

news2025/3/17 3:51:27

一、串口使用流程图

二、串口相关函数介绍

三、实现串口收发

一、串口使用流程图

OneMO ML307R模组提供了2路UART给开发者用于通讯开发,以及1路DBG UART用于log的打印。UART Demo示例可以在SDK:examples\uart\src\cm_demo_uart.c中查看。

串口使用流程图

二、串口相关函数介绍

可以在UART Demo示例可以在SDK:examples\uart\src\cm_demo_uart.h中查看

从这里可以看到ML307R 是用户可用只有2个串口

/****************************************************************************/

/**
 *  @brief 打开串口
 *  
 *  @param [in] dev 串口设备ID
 *  @param [in] cfg 串口配置
 *  
 *  @return  
 *    = 0  - 成功 \n
 *    < 0  - 失败, 返回值为错误码
 *  
 *  @details open之前必须先对引脚复用功能进行设置。\n
 *           串口支持的波特率及模式见cm_uart_cfg_t结构体注释说明,请详细查看波特率宏定义处的注意事项
 */
int32_t cm_uart_open(cm_uart_dev_e dev, cm_uart_cfg_t *cfg);

/**
 *  @brief 注册串口事件
 *  
 *  @param [in] dev 串口设备ID
 *  @param [in] event 串口事件
 *  
 *  @return  
 *    = 0  - 成功 \n
 *    < 0  - 失败, 返回值为错误码
 *  
 *  @details 事件包括串口数据可读/溢出等。需在open之前注册。\n
 *           内置4K缓存区保存未读出的数据,若注册了溢出事件,缓存区满后将上报溢出,缓存区溢出状态下将丢弃新接收的数据。\n
 *           回调函数中不可输出LOG、串口打印、执行复杂任务或消耗过多资源。
 */
int32_t cm_uart_register_event(cm_uart_dev_e dev, void *event);

/**
 *  @brief 关闭串口
 *  
 *  @param [in] dev 串口设备ID
 *  
 *  @return
 *    = 0  - 成功 \n
 *    < 0  - 失败, 返回值为错误码
 *  
 *  @details 
 */
int32_t cm_uart_close(cm_uart_dev_e dev);

/**
 *  @brief 串口写数据
 *  
 *  @param [in] dev 串口设备ID
 *  @param [in] data 待写入数据
 *  @param [in] len 长度
 *  @param [in] timeout 超时时间(ms)(无效参数)
 *  
 *  @return 
 *    = 实际写入长度 - 成功 \n
 *    < 0 - 失败, 返回值为错误码
 *  
 *  @details 
 */
int32_t cm_uart_write(cm_uart_dev_e dev, const void *data, int32_t len, int32_t timeout);

/**
 *  @brief 串口读数据
 *  
 *  @param [in] dev 串口设备ID
 *  @param [out] data 待读数据
 *  @param [in] len 长度
 *  @param [in] timeout 超时时间(ms)(无效参数)
 *  
 *  @return 
 *    = 实际读出长度 - 成功 \n
 *    < 0 - 失败, 返回值为错误码
 *  
 *  @details 
 */
int32_t cm_uart_read(cm_uart_dev_e dev, void* data, int32_t len, int32_t timeout);

三、实现串口收发

3.1   配置串口设置信息

    /* 配置参数 */
    cm_uart_cfg_t config = 
    {
        CM_UART_BYTE_SIZE_8, 
        CM_UART_PARITY_NONE,
        CM_UART_STOP_BIT_ONE, 
        CM_UART_FLOW_CTRL_NONE, 
        CM_UART_BAUDRATE_115200,
        0   //配置为普通串口模式,若要配置为低功耗模式可改为1
    };

3.2 设置串口事件信息     事件里面添加了 接收到新的数据事件和 接收FIFO缓存溢出事件,这个在之后接收回调中处理。

    /* 事件参数 */
    cm_uart_event_t event = 
    {
        CM_UART_EVENT_TYPE_RX_ARRIVED|CM_UART_EVENT_TYPE_RX_OVERFLOW,   //注册需要上报的事件类型
        "uart0",                                                        //用户参数
        cm_serial_uart_callback                                         //上报事件的回调函数
    };

3.3 设置串口功能复用
 

    /* 配置引脚复用 */

    cm_iomux_set_pin_func(OPENCPU_TEST_UARTTX_IOMUX);

    cm_iomux_set_pin_func(OPENCPU_TEST_UARTRX_IOMUX);

3.4 注册串口事件  主要是串口接收

    /* 注册事件和回调函数 */
    ret = cm_uart_register_event(OPENCPU_MAIN_URAT, &event);
    
    if (ret != RET_SUCCESS)
    {
        cm_log_printf(0, "uart register event err,ret=%d\n", ret);
        return;
    }

3.5 打开串口

    /* 开启串口 */
    ret = cm_uart_open(OPENCPU_MAIN_URAT, &config);
    
    if (ret != RET_SUCCESS)
    {
        cm_log_printf(0, "uart init err,ret=%d\n", ret);
        return;
    }

3.6  设置启用串口接收线程。

    /* 以下为串口接收示例,不影响串口配置,用户可酌情参考 */
    osThreadAttr_t uart_task_attr = {0};
    uart_task_attr.name = "uart_task";
    uart_task_attr.stack_size = 2048;
    uart_task_attr.priority= UART_TASK_PRIORITY;
    gstUartCmdRecv.cmd_execute = 0;
    
    if (g_uart_sem == NULL)
    {
        g_uart_sem = osSemaphoreNew(1, 0, NULL);
    }

    OC_Uart_TaskHandle = osThreadNew(cm_uart_recv_task, 0, &uart_task_attr);

串口事件

/* 回调函数中不可输出LOG、串口打印、执行复杂任务或消耗过多资源,建议以信号量或消息队列形式控制其他线程执行任务 */
static void cm_serial_uart_callback(void *param, uint32_t type)
{
    uart_event_msg_t msg = {0};
    if (CM_UART_EVENT_TYPE_RX_ARRIVED & type)
    {
        /* 收到接收事件,触发其他线程执行读取数据 */
        osSemaphoreRelease(g_uart_sem);
    }
    
    if (CM_UART_EVENT_TYPE_RX_OVERFLOW & type)
    {
        /* 收到溢出事件,触发其他线程处理溢出事件 */
        msg.msg_type = type;
        
        if (uart_event_queue != NULL)//向队列发送数据
        {
            osMessageQueuePut(uart_event_queue, &msg, 0, 0);
        }
    }
}
/* 串口接收示例,平时使用信号量挂起,当收到接收事件后,释放信号量以触发读取任务 */
static void cm_uart_recv_task(void *param)
{
    int temp_len = 0;


    
    while (1)
    {
        if (g_uart_sem != NULL)
        {
            osSemaphoreAcquire(g_uart_sem, osWaitForever);//阻塞
        }
        
        if (rx_rev_len < UART_BUF_LEN)
        {
            temp_len = cm_uart_read(OPENCPU_MAIN_URAT, (void*)&rx_rev_data[rx_rev_len], UART_BUF_LEN - rx_rev_len, 1000);
            rx_rev_len += temp_len;
           cm_uart_write(OPENCPU_MAIN_URAT, rx_rev_data, rx_rev_len, 1000);
           rx_rev_len = 0;
            
        }


    }
}

以上就是把接收到的数据再发送出来

以下是完整的串口代码

#define UART_BUF_LEN            1024

/****************************************************************************
 * Private Types
 ****************************************************************************/
 
typedef struct{
    int msg_type;
} uart_event_msg_t;

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/
 
void cm_demo_printf(char *str, ...);
static void cm_uart_recv_task(void *param);


/****************************************************************************
 * Private Data
 ****************************************************************************/

static int rx_rev_len = 0;
static char rx_rev_data[UART_BUF_LEN] = {0};
static osThreadId_t OC_Uart_TaskHandle = NULL; //串口数据接收、解析任务Handle
static void* g_uart_sem = NULL;
static osMessageQueueId_t uart_event_queue = NULL;
static osThreadId_t uart_event_thread = NULL;

cm_uart_cmd_recv_t gstUartCmdRecv = {0}; //串口命令缓冲区



/****************************************************************************
 * Private Functions
 ****************************************************************************/

/* 用于测试串口事件,用户可参考 */
static void uart_event_task(void *arg)
{
    uart_event_msg_t msg = {0};

    while (1)
    {
        if (osMessageQueueGet(uart_event_queue, &msg, NULL, osWaitForever) == osOK)
        {
            //cm_log_printf(0, "uart event msg type = %d\n", msg.msg_type);
            if (CM_UART_EVENT_TYPE_RX_OVERFLOW & msg.msg_type)
            {
                cm_log_printf(0, "CM_UART_EVENT_TYPE_RX_OVERFLOW... ...");
            }
        }
    }
}

/* 用于测试串口事件,用户可参考 */
static int uart_event_task_create(void)
{
    if (uart_event_queue == NULL)
    {
        uart_event_queue = osMessageQueueNew(10, sizeof(uart_event_msg_t), NULL);
    }

    if (uart_event_thread == NULL)
    {
        osThreadAttr_t attr1 = {
            .name = "uart_event",
            .priority = UART_TASK_PRIORITY,
            .stack_size = 1024,
        };
        uart_event_thread = osThreadNew(uart_event_task, NULL, (const osThreadAttr_t*)&attr1);
    }

    return 0;
}



/* 回调函数中不可输出LOG、串口打印、执行复杂任务或消耗过多资源,建议以信号量或消息队列形式控制其他线程执行任务 */
static void cm_serial_uart_callback(void *param, uint32_t type)
{
    uart_event_msg_t msg = {0};
    if (CM_UART_EVENT_TYPE_RX_ARRIVED & type)
    {
        /* 收到接收事件,触发其他线程执行读取数据 */
        osSemaphoreRelease(g_uart_sem);
    }
    
    if (CM_UART_EVENT_TYPE_RX_OVERFLOW & type)
    {
        /* 收到溢出事件,触发其他线程处理溢出事件 */
        msg.msg_type = type;
        
        if (uart_event_queue != NULL)//向队列发送数据
        {
            osMessageQueuePut(uart_event_queue, &msg, 0, 0);
        }
    }
}

/* 串口接收示例,平时使用信号量挂起,当收到接收事件后,释放信号量以触发读取任务 */
static void cm_uart_recv_task(void *param)
{
    int temp_len = 0;


    
    while (1)
    {
        if (g_uart_sem != NULL)
        {
            osSemaphoreAcquire(g_uart_sem, osWaitForever);//阻塞
        }
        
        if (rx_rev_len < UART_BUF_LEN)
        {
            temp_len = cm_uart_read(OPENCPU_MAIN_URAT, (void*)&rx_rev_data[rx_rev_len], UART_BUF_LEN - rx_rev_len, 1000);
            rx_rev_len += temp_len;
           cm_uart_write(OPENCPU_MAIN_URAT, rx_rev_data, rx_rev_len, 1000);
           rx_rev_len = 0;
            
        }


    }
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/* 从测试串口打印字符串 */
void cm_demo_printf(char *str, ...)
{
    static char s[600]; //This needs to be large enough to store the string TODO Change magic number
    va_list args;
    int len;
    
    if ((str == NULL) || (strlen(str) == 0))
    {
        return;
    }

    va_start(args, str);
    len = vsnprintf((char*)s, 600, str, args);
    va_end(args);
    cm_uart_write(OPENCPU_MAIN_URAT, s, len, 1000);
}

void my_printf(const char *str, ...)
{
    static char s[1000]; //This needs to be large enough to store the string TODO Change magic number
    va_list args;
    int len;
    
    if ((str == NULL) || (strlen(str) == 0))
    {
        return;
    }

    va_start(args, str);
    len = vsnprintf((char*)s, 1000, str, args);
    va_end(args);
    cm_uart_write(OPENCPU_MAIN_URAT, s, len, 1000);
    //uart_write_bytes(UART_NUM_1, (const char *) p_buf, len);
}

/* 若要修改测试串口可在cm_common.h中修改宏定义 */
void cm_demo_uart(void)
{
    int32_t ret = -1;

    /* 配置参数 */
    cm_uart_cfg_t config = 
    {
        CM_UART_BYTE_SIZE_8, 
        CM_UART_PARITY_NONE,
        CM_UART_STOP_BIT_ONE, 
        CM_UART_FLOW_CTRL_NONE, 
        CM_UART_BAUDRATE_115200,
        0   //配置为普通串口模式,若要配置为低功耗模式可改为1
    };

    /* 事件参数 */
    cm_uart_event_t event = 
    {
        CM_UART_EVENT_TYPE_RX_ARRIVED|CM_UART_EVENT_TYPE_RX_OVERFLOW,   //注册需要上报的事件类型
        "uart0",                                                        //用户参数
        cm_serial_uart_callback                                         //上报事件的回调函数
    };

    cm_log_printf(0, "uart NUM = %d demo start... ...", OPENCPU_MAIN_URAT);

    /* 配置引脚复用 */
    cm_iomux_set_pin_func(OPENCPU_TEST_UARTTX_IOMUX);
    cm_iomux_set_pin_func(OPENCPU_TEST_UARTRX_IOMUX);

    /* 注册事件和回调函数 */
    ret = cm_uart_register_event(OPENCPU_MAIN_URAT, &event);
    
    if (ret != RET_SUCCESS)
    {
        cm_log_printf(0, "uart register event err,ret=%d\n", ret);
        return;
    }

    /* 开启串口 */
    ret = cm_uart_open(OPENCPU_MAIN_URAT, &config);
    
    if (ret != RET_SUCCESS)
    {
        cm_log_printf(0, "uart init err,ret=%d\n", ret);
        return;
    }

    /* 配置串口唤醒 */
    /* 只有UART0具有串口唤醒功能 */
    if (OPENCPU_MAIN_URAT == CM_UART_DEV_0)
    {
        /* 配置uart唤醒功能,使能边沿检测才具备唤醒功能,仅主串口具有唤醒功能,用于唤醒的数据并不能被uart接收,请在唤醒后再进行uart数传 */
        cm_iomux_set_pin_cmd(OPENCPU_UART_WEKEUP_PIN, CM_IOMUX_PINCMD1_LPMEDEG, CM_IOMUX_PINCMD1_FUNC1_LPM_EDGE_RISE);
    }

    cm_log_printf(0, "cm_uart_register_event start... ...\n");

    /* 以下为串口接收示例,不影响串口配置,用户可酌情参考 */
    osThreadAttr_t uart_task_attr = {0};
    uart_task_attr.name = "uart_task";
    uart_task_attr.stack_size = 2048;
    uart_task_attr.priority= UART_TASK_PRIORITY;
    gstUartCmdRecv.cmd_execute = 0;
    
    if (g_uart_sem == NULL)
    {
        g_uart_sem = osSemaphoreNew(1, 0, NULL);
    }

    OC_Uart_TaskHandle = osThreadNew(cm_uart_recv_task, 0, &uart_task_attr);

    uart_event_task_create();
}

/* 关闭串口 */
void cm_test_uart_close(char **cmd, int len)
{
    cm_uart_dev_e dev = atoi(cmd[2]);
    
    if (0 == cm_uart_close(dev))
    {
        cm_demo_printf("uart%d close is ok\n", dev);
    }
    
    else
    {
        cm_demo_printf("uart%d close is error\n", dev);
    }
}
void bsp_uart_init(void)
{
    cm_demo_uart();
    cm_demo_printf("\r\nuart_init\r\n");
}

串口接收程序逻辑:1、初始化信号量>2、触发接收事件>3、callback函数中释放信号量>4、接收处理线程接收处理数据

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1694820.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

视频号小店的保证金是多少钱?2024最新收费标准,一篇了解!

哈喽~我是电商月月 现实社会&#xff0c;干什么都需要交钱&#xff0c;就连上班&#xff0c;路费也得掏钱 想要入驻视频号小店&#xff0c;在视频号里卖货赚钱&#xff0c;就要缴纳类目保证金 那到底要缴多少钱呢&#xff1f; 今天&#xff0c;月月就把最新的收费标准分享给…

Android Low Storage机制之DeviceStorageMonitorService

一、Android 版本 Android 13 二、low storage简介(DeviceStorageMonitorService) 设备存储监视器服务是一个模块&#xff0c;主要用来&#xff1a; 1.监视设备存储&#xff08;“/ data”&#xff09;。 2.每60秒扫描一次免费存储空间(谷歌默认值) 3.当设备的存储空间不足…

Ollama:一个在本地部署、运行大型语言模型的工具

Ollama&#xff1a;一个在本地部署、运行大型语言模型的工具 Ollama部署、运行大型语言模型概述安装配置Ollama命令模型库使用示例自定义模型从GGUF导入自定义提示从PyTorch或Safetensors导入 开启服务REST API卸载Ollama One-API概述One-API管理本地模型 Open WebUI概述Docker…

VUE3 学习笔记(十)查看vue版本

命令&#xff1a; npm list vue(空) (在项目的根目录下执行以下命令即可查看项目所使用的vue版本) npm list vue version(空) npm info vue (全局查看vue版本号&#xff0c;详细) npm list vue -g(全局查看vue版本号&#xff0c;简单) npm view vue version(查看项目依赖的vue…

Jeecg | 如何解决 ERR Client sent AUTH, but no password is set 问题

最近在尝试Jeecg低代码开发&#xff0c;但是碰到了超级多的问题&#xff0c;不过总归是成功运行起来了。 下面说说碰到的最后一个配置问题&#xff1a;连接redis失败 Error starting ApplicationContext. To display the conditions report re-run your application with deb…

解决鼠标滚动时element-ui下拉框错位的问题

问题描述&#xff1a;elementUi的el-select下拉选择框,打开之后,直到失去焦点才会自动关闭。 在有滚动条的弹窗中使用时就会出现打开下拉框,滚动弹窗,el-select下拉框会超出弹窗范围的问题. 解决方案&#xff1a; 1、先在util文件夹下创建个hideSelect.js文件&#xff0c;代码…

内网穿透--Nps-自定义-上线

免责声明:本文仅做技术交流与学习... 目录 Nps项目: 一图通解: 1-下载nps/npc 2-服务端启动 访问web网页: 添加客户端&#xff0c;生成密匙. 3-kali客户端连接服务端 4-添加协议隧道. 5-kali生成后门&#xff1a; 6-kali创建监听: Nps项目: https://github.com/ehang…

算法刷题笔记 高精度乘法(C++实现)

文章目录 题目描述解题思路解题代码 题目描述 给定两个非负整数&#xff08;不含前导0&#xff09;A和B&#xff0c;请你计算 AB的值。 输入格式 共两行&#xff0c;第一行包含整数 A&#xff0c;第二行包含整数 B。 输出格式 共一行&#xff0c;包含AB的值。 数据范围 …

Putty: 随心御剑——远程启动服务工具plink

一、引言:如何远程控制 也许你会有这样的场景,交互程序(以下简称UI程序)跑在windows端,而控制程序跑在Linux上。我们想要通过windows端 UI程序来启动Linux下面的服务,来一场酣畅淋漓的御剑飞行咋办,难道要自己十年磨一剑,在Linux下编写一个受控服务程序么.计算机科技发…

最新:windows下安装pcl点云库

&#x1f4da;博客主页&#xff1a;knighthood2001 ✨公众号&#xff1a;认知up吧 &#xff08;目前正在带领大家一起提升认知&#xff0c;感兴趣可以来围观一下&#xff09; &#x1f383;知识星球&#xff1a;【认知up吧|成长|副业】介绍 ❤️如遇文章付费&#xff0c;可先看…

【问题解决】ImportError: generic_type: cannot initialize type “ExternalAllocator“

一、问题描述 我的环境是Ubuntu20.04&#xff0c;Cuda版本是11.4&#xff0c;在复现OpenPCDet的时候遇到了下面问题&#xff1a; Traceback (most recent call last):File "train.py", line 7, in <module>from test import repeat_eval_ckptFile "/mnt…

Scala的简单学习一

一 相关知识 1.1 scala的安装 1.在idea中导入依赖&#xff0c;并在Idea下载scala插件 1.2 scala基础知识点 1.scala代码中一行语句的结束是以换行符为标准&#xff0c;可以不用写分号 2.class是一个普通的类&#xff0c;object相当于一个单例对象&#xff0c;object类中的…

git分支策略(github-flow VS git flow,如何选择)

一. 结论 Github flow&#xff1a;最简单 小型项目&#xff0c;持续部署&#xff0c;自动化测试程度高&#xff0c;发布流程简单 Git flow&#xff1a;复杂但最常用 大型项目&#xff0c;发布周期长&#xff0c;需要同时维护多个版本&#xff0c;发布流程复杂 表格提供了不…

36PE启动盘新秀:Ventoy(附各种PE的ISO下载)

PE启动盘新秀:Ventoy(附各种PE的ISO下载) 在我们以前的认知中,一个U盘只能制作包含一个系统的启动盘.比如,安装了微PE工具箱的U盘就不能安装其他什么PE工具箱了.这有时候让我们很无奈,只能买好多U盘,一个U盘一个PE系统. 这个问题的本质是什么?事实上,笔者认为,就是单个的ISO文…

Andoird使用Room实现持久化及使用Room进行增删查改

文章目录 Room概述Room的使用一、在gradle.build中添加依赖库kotlinJava 创建实体类创建抽象Dao层接口创建DataBase层使用创建的查看数据库 总结&#xff1a; 这篇文章会告诉你如何在Android中通过kotlin或者Java来实现数据持久化 Room概述 处理大量结构化数据的应用可极大地受…

2024年【N1叉车司机】考试题及N1叉车司机找解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 N1叉车司机考试题参考答案及N1叉车司机考试试题解析是安全生产模拟考试一点通题库老师及N1叉车司机操作证已考过的学员汇总&#xff0c;相对有效帮助N1叉车司机找解析学员顺利通过考试。 1、【多选题】《特种设备使用…

FreeRTOS 源码概述

FreeRTOS 目录结构 使用 STM32CubeMX 创建的 FreeRTOS 工程中&#xff0c;FreeRTOS 相关的源码如下: 主要涉及2个目录&#xff1a; Core Inc 目录下的 FreeRTOSConfig.h 是配置文件 Src 目录下的 freertos.c 是 STM32CubeMX 创建的默认任务 Middlewares\Third_Party…

深入解析编程逻辑中的关键字与逻辑运算

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、认识关键字及其重要性 二、逻辑运算的关键字 1. and、or 和 not 的运用 2. 逻辑运算的…

42-3 应急响应之服务排查

一、服务排查 服务是后台运行的进程,可在计算机启动时自动启动,也可暂停和重新启动,且不显示用户界面。它们特别适用于长时间运行的功能,以避免影响其他用户在同一台计算机上的工作。在应急响应中,服务常被恶意软件用作驻留方法。 二、Windows服务排查 打开【运行】对话框…