前言:
上一篇文章已经把如何配置和安装开发环境介绍了一下,开发环境准备完毕之后就可以进行具体的项目开发了,这个款芯片的外设接口很多,我个人觉得用的最多的还是串口,这篇文章就介绍一下串口的开发流程,如果SDK开发环境还没配置好看上一篇文章。
ML307R SDK开发--OpenCPU应用程序开发学习笔记(1)_ml307r opencpu-CSDN博客
1:OpenCpu-ML307R串口介绍
根据中移的官方资料可以了解到,关于串口一共有三个,两个串口,一个调试串口。
想要学习一个全新的外设最快最简单的方法是看历程。OPenCpu中有很多历程
常使用的外设历程基本都在这里,在此着重说明一下串口的历程也就是uart。
2:串口开发准备
相比第一次接触这个OpenCpu的时候会有点小懵,我在哪里开发自己的程序,应该写在哪里,然后是写好程序后如何编译运行。在此我一一给出说明。
首先所有的用户程序开发均在这个里面。
src里面存放c文件,inc里面存放h文件。
在src里面有个custom_main.c的文件,文件中有个cm_opencpu_entry()函数。这个函数就相当于传统c语言中的main函数,程序就在这里运行。
然后可以在src文件下创建想要的用户程序文件。
我个人习惯是把.c、.h文件放在一起。
创建完用户文件后就需要添加路径,不然编译的时候找不到库函数。
在这个文件夹里面有个SConscript文件里面设置对应的文件路劲的地方。
上面都有对应的注释,按照自己的需求添加在对应的位置即可。
3:串口开发流程
这个串口开发流程我是根据示例程序总结的只代表我个人观点。
在进行具体的程序开发之前我认为应该要对相关的API接口函数有个大致的了解。
这个就是对用的接口函数,了解到相关的接口函数那就开始说开发流程。这个只是读取数据的,写数据同理。
1:配置串口参数,事件结构体
常见配置
事件结构体
2: 设置读取数据事件
3:复用使能引脚
这个要根据原理图来查看。
使用的是串口0,对应的引脚就是17,18
4:注册串口接收数据事件
5:打开串口
6:使用线程来做uart接收任务
7:编写串口接收函数
8:编写回调函数
9:编写uart事件处理任务
上面这个不用完全自己会写,看得懂例子接口即可。
我也附上我的完整程序。
//串口示例头文件
#include "cm_uart.h"
#include "cm_iomux.h"
#include "stdio.h"
#include "stdlib.h"
#include "stdarg.h"
#include "cm_os.h"
#include "cm_mem.h"
#include "cm_common.h"
#include "cm_sys.h"
#include "cm_demo_uart.h"#include "cm_sys.h"#include "test.h"typedef struct{int msg_type;
} uart_event_msg_t;#define UART_BUF_LEN 1024static 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;static void uart_event(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_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, NULL, (const osThreadAttr_t*)&attr1);}return 0;
}extern osEventFlagsId_t cmd_task_flag;/* 回调函数中不可输出LOG、串口打印、执行复杂任务或消耗过多资源,
建议以信号量或消息队列形式控制其他线程执行任务 */
static void cm_uart_callback(void *param, uint32_t type)
{uart_event_msg_t msg = {0};//数据接收回调if (CM_UART_EVENT_TYPE_RX_ARRIVED & type){/* 收到接收事件,触发其他线程执行读取数据 */if (g_uart_sem != NULL) {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(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){memset((void*)rx_rev_data + rx_rev_len, 0, UART_BUF_LEN - rx_rev_len); //读取数据temp_len = cm_uart_read(OPENCPU_MAIN_URAT, (void*)&rx_rev_data[rx_rev_len], UART_BUF_LEN - rx_rev_len, 1000);if(temp_len > 0){rx_rev_len += temp_len;}/* 每次接收到数据后立即打印 *///cm_demo_printf("Received data: %.*s", temp_len, &rx_rev_data[rx_rev_len - temp_len]);}/*数据处理*/if (strstr(rx_rev_data, "\r\n")){ if (rx_rev_len != 0){// 打印完整的接收到的数据信息cm_demo_printf("Full command received: %s", rx_rev_data);// 处理完数据后重置缓冲区memset(rx_rev_data, 0, sizeof(rx_rev_data));rx_rev_len = 0;} }}
}/* 从测试串口打印字符串 */
void cm_demo_printf(char *str, ...)
{char s[600] = {0}; //This needs to be large enough to store the string TODO Change magic numberva_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 cm_uart(void)
{int32_t ret = -1;/* 配置参数 */cm_uart_cfg_t config = {CM_UART_BYTE_SIZE_8, // 数据位长度为8位CM_UART_PARITY_NONE, // 无校验位CM_UART_STOP_BIT_ONE, // 停止位为1位CM_UART_FLOW_CTRL_NONE, // 无流控制CM_UART_BAUDRATE_115200, // 波特率为96000, //配置为普通串口模式,若要配置为低功耗模式可改为10, //环形缓存区大小按照默认配置8k0,0,};/* 设置串口数据接收事件参数 */cm_uart_event_t event = {//接收到新的数据,接收FIFO缓存溢出CM_UART_EVENT_TYPE_RX_ARRIVED|CM_UART_EVENT_TYPE_RX_OVERFLOW, //注册需要上报的事件类型"uart0", //用户参数cm_uart_callback //上报事件的回调函数};cm_log_printf(0, "uart NUM = %d start", OPENCPU_MAIN_URAT);/* 配置引脚复用功能,使能UART TX和RX引脚 使能引脚*/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;}//打开串口/* 开启指定的UART端口,并传递配置参数 */ret = cm_uart_open(OPENCPU_MAIN_URAT, &config);//判断事件是否设置成功if (ret != RET_SUCCESS){cm_log_printf(0, "uart init err,ret=%d\n", ret);return;}// 打印日志信息,表示事件注册完成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;// 线程优先级// 如果信号量g_uart_sem为空,则创建一个新的二值信号量if (g_uart_sem == NULL){g_uart_sem = osSemaphoreNew(1, 0, NULL);//创建二值信号量返回信号量ID}// 创建一个新线程来处理UART接收任务OC_Uart_TaskHandle = osThreadNew(cm_uart_recv, 0, &uart_task_attr);// 创建UART事件处理任务uart_event_create();
}
程序编写完毕后,记得在.h文件中声明,和c语言的程序开发流程基本一样。然后再cm_opencpu_entry();里面调用,这个样就可以了。那么怎么进行编译了。
上一篇文章安装的scons就是编译器,在终端打开,然后输入命令
scons custom=y
就会编译了。
编译成功会在out里面生成一个zpi固件程序,烧录到开发板里面即可。
然后运行测试。
测试成功,发送上面接收上面,并打印出来。