ML307R 串口开发--OpenCPU应用程序开发学习笔记(2)
前言:
上一篇文章已经把如何配置和安装开发环境介绍了一下,开发环境准备完毕之后就可以进行具体的项目开发了,这个款芯片的外设接口很多,我个人觉得用的最多的还是串口,这篇文章就介绍一下串口的开发流程,如果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 1024
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;
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 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 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, // 波特率为9600
0, //配置为普通串口模式,若要配置为低功耗模式可改为1
0, //环形缓存区大小按照默认配置8k
0,
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固件程序,烧录到开发板里面即可。
然后运行测试。
测试成功,发送上面接收上面,并打印出来。