联盛德 HLK-W801(三):在SDK粥中盛出UART

发布者:bianzitong521最新更新时间:2022-08-03 来源: csdn关键字:联盛德  SDK  UART 手机看文章 扫描二维码
随时随地手机看文章

1. 开发环境

系统:win10

开发板:联盛德 HLK-W801开发板

SDK版本:wm_sdk_w80x_20211115

IDE:cdk-windows-V2.12.1

2. 使用sdk初始化串口的步骤

2.1 新建文件夹和文件

在工程下新建文件夹user,然后在新建一个uart.c和uart.h文件,添加到工程,如图:

在这里插入图片描述

注意:uaer文件夹下的其他文件不用管,那是我添加的其他测试文件。


在uart.h里面写下面程序:


#ifndef __UART_H_

#define __UART_H_


#ifdef __cplusplus

extern "C"{

#endif


void create_my_uart_task(void);


#ifdef __cplusplus

}

#endif


#endif //__UART_H_


在uart.h里面写下面程序:


以串口1为例:


#include "uart.h"

#include "wm_uart.h"

#include "wm_include.h"

#include "wm_config.h"

#include



//task资源

#define  UART_TASK_SIZE      1024

static OS_STK uart_task_stk[UART_TASK_SIZE];



//存储接收到的数据

char rx_fifo_buf[1024] = {0};

//存储收到数据的长度

uint16_t rx_length = 0;

//定义一个信号量

tls_os_sem_t * sem_rx = NULL;



//接收回调函数

s16 my_uart_rx_callback(u16 len, void* user_data)

{

//将接收到字符累加,这里很坑,下面章节会具体讲这个函数。

rx_length += len;

tls_os_sem_release(sem_rx);

    return WM_SUCCESS;

}


//串口初始化

void my_uart_init()

{

//初始化uart引脚,函数内部会自动开启串口时钟,

wm_uart1_rx_config(WM_IO_PB_07);

wm_uart1_tx_config(WM_IO_PB_06);

//配置uart参数

tls_uart_options_t opt;

opt.baudrate = UART_BAUDRATE_B9600;//波特率

opt.paritytype = TLS_UART_PMODE_DISABLED;//无奇偶校验

opt.stopbits = TLS_UART_ONE_STOPBITS;//一个停止位

opt.charlength = TLS_UART_CHSIZE_8BIT;//数据长度

opt.flow_ctrl = TLS_UART_FLOW_CTRL_NONE;//没有流控制

//初始化串口,这个函数内部会开启串口中断

if (WM_SUCCESS != tls_uart_port_init(TLS_UART_1, &opt, 0))

{

printf("uart1 init errorn");

}

//为串口绑定接收回调函数

tls_uart_rx_callback_register((u16) TLS_UART_1, (s16(*)(u16, void*))my_uart_rx_callback, NULL);

    //发送完回调函数

//tls_uart_tx_callback_register(2, (s16(*) (struct tls_uart_port *))tls_uart_free_tx_sent_data);

}


//uart线程

void my_uart_task(void *sdata)

{

uint16_t rx_len = 0;

//用于判断信号量是否成功获取

tls_os_status_t os_status  = TLS_OS_ERROR;

//创建信号量,用于回调函数和线程之间通信

tls_os_sem_create(&sem_rx, 0);

//初始化串口

my_uart_init();

for(;;){

os_status = tls_os_sem_acquire(sem_rx, 20);

if(os_status){

if(rx_length > 0){

rx_len = tls_uart_read(TLS_UART_1, rx_fifo_buf, rx_length);

rx_length = 0;

tls_uart_write(TLS_UART_1, rx_fifo_buf, rx_len); //输出

}

}

}

}


//创建uart线程

void create_my_uart_task(void)

{

tls_os_task_create(NULL, NULL,

   my_uart_task,

   NULL,

   (void *)uart_task_stk,          /* task's stack start address */

   UART_TASK_SIZE * sizeof(u32), /* task's stack size, unit:byte */

   32,

   0);

}


然后在主函数中调用create_my_uart_task函数,如图:

在这里插入图片描述

2.2 解释代码

2.2.1 初始化串口解析

初始化串口对应的引脚。按住[Ctrl]+鼠标左键,点击函数可以跳转到函数定义,由定义可知串口可以使用的引脚,同时可知,初始化uart引脚程序中,函数内部会自动开启串口时钟。


定义uart的配置结构体,为结构体设置参数


调用tls_uart_port_init函数配置串口,函数共三个参数,其中第三个在函数注释中声明,当使用串口2的时候,传0表示当普通串口使用,传1表示当作7816 模式。当使用其他串口,这个参数只能传0。


设置中断回调函数,回调函数定义为:


s16(*rx_callback) (u16 len, void* user_data)

1

函数有两个参数,通过观察中断函数:


ATTRIBUTE_ISR void UART2_4_IRQHandler(void)

{

...

port->rx_callback(rxlen, port->priv_data);

...

}


和串口的定义结构体:


/**

 * @typedef struct tls_uart_port

 */

typedef struct tls_uart_port

{

    u32 uart_no;                    /**< uart number: 0 or 1 */

    u32 uart_irq_no;             /**< uart interrupt number */

    u32 plus_char_cnt;

    TLS_UART_MODE_T uart_mode;      /**< uart work mode: interrupt mode or poll mode */

    struct tls_uart_options opts;       /**< uart config parameters */

    int fcStatus;                           /**< flow ctrl status,0 closed ,1 opened */

    enum TLS_UART_RX_FLOW_CTRL_FLAG rxstatus;

    u32 tx_fifofull;                    /**< uart tx fifo trigger level */

    TLS_UART_REGS_T volatile *regs;     /**< uart registers struct pointer */

    struct tls_uart_icount icount;          /**< uart statistics information */

    struct tls_uart_circ_buf recv;          /**< uart ring buffer */

// struct tls_uart_circ_buf xmit;

    struct dl_list tx_msg_pending_list;

    struct dl_list tx_msg_to_be_freed_list;

    u8 hw_stopped;

    tls_os_sem_t *tx_sem;

    char *buf_ptr;

    u16 buf_len;

    s16(*rx_callback) (u16 len, void* priv_data);

    s16(*tx_callback) (struct tls_uart_port * port);

    s16(*tx_sent_callback) (struct tls_uart_port * port);

    bool tx_dma_on;

bool rx_dma_on;

void *priv_data;

} tls_uart_port_t;


可知第二个参数是当回调的时候会将串口结构体的最后一个参数传回去,这样做有一个好处,就是可以将所有的串口接收回调绑到一个函数上,在回调函数执行的时候,通过传回去的第二个参数,来判断是那个串口产生回调。


初始化串口还有另一种方法(这个方法我没有验证,不保证是对的,太累了),如下:


//另一种串口初始化

void my_uart_init()

{

//初始化uart引脚,函数内部会自动开启串口时钟,

wm_uart1_rx_config(WM_IO_PB_07);

wm_uart1_tx_config(WM_IO_PB_06);

    //当第二个参数为NULL的时候,串口会按默认参数初始化这个串口,具体请看函数tls_uart_port_init的实现

    if (WM_SUCCESS != tls_uart_port_init(TLS_UART_1, NULL, 0))

{

printf("uart1 init errorn");

    

//默认初始化可能不符合要求,那么就用sdk来修改uart参数

    tls_uart_set_baud_rate(TLS_UART_1, UART_BAUDRATE_B9600)//波特率

tls_uart_set_parity(TLS_UART_1, TLS_UART_PMODE_DISABLED);//无奇偶校验

tls_uart_set_stop_bits(TLS_UART_1, TLS_UART_ONE_STOPBITS);//一个停止位

    

//为串口绑定接收回调函数

tls_uart_rx_callback_register((u16) TLS_UART_1, (s16(*)(u16, void*))my_uart_rx_callback, NULL);


}


2.2.2 回调函数(坑)

开始的时候,我是这样写的程序:


/*这是一个错误的示例*/


//接收回调函数

s16 my_uart_rx_callback(u16 len)

{

rx_length = len;

tls_os_sem_release(sem_rx);

    return WM_SUCCESS;

}


//uart线程函数

...

for(;;){

    os_status = tls_os_sem_acquire(sem_rx, 0);

    if(!os_status){

        if(rx_length > 0){

            rx_len = tls_uart_read(TLS_UART_1, rx_fifo_buf, rx_length);

            tls_uart_write(TLS_UART_1, rx_fifo_buf, rx_len); //输出

        }

    }

 }

...


当向串口发送数据包小于16byte的时候,能正常返回数据,当发送16byte字节以上的时候,会不停的返回乱七八糟的数据,且数据只有前15byte和发给它的是对的上的,后面就会不停的往回发数据。


后来经过不断尝试,打印输出,最后发现如下:


当用户发过来数据的时候,如果一次性数据小于16byte,那么,这个回调就会执行一次,并且len值就是接收到的数据个数。


但是当用户一次性发来的数据多余16byte,那个这个回调函数回调的次数会变得不可预知,但可以知道第一次回调len为16,但是第二次、三次就不知了,len可能是2,也可能是1,例如:您发了19个字节,这个回调可能会执行3次,len分别是16、1、2。也可能会执行2次,len为16、3。这样就导致您根本做不了数据分包,只能不断累加。如下:


//接收回调函数

s16 my_uart_rx_callback(u16 len)

{

rx_length += len;

tls_os_sem_release(sem_rx);

    return WM_SUCCESS;

}


这种情况,有一种分包办法就是在线程里面操作。


2.2.3 线程内用信号量超时来分包

当回调函数收到包,那么就会释放信号量,线程内部等待这个信号量,信号量的返回值为枚举类型:


typedef enum tls_os_status {

    TLS_OS_SUCCESS = 0,

    TLS_OS_ERROR,

    TLS_OS_ERR_TIMEOUT,

} tls_os_status_t;


若是在20个时钟周期收到,那么返回值TLS_OS_SUCCESS,若超时就返回TLS_OS_ERR_TIMEOUT,通过这个就可以进行串口分包:


...

for(;;){

    os_status = tls_os_sem_acquire(sem_rx, 20);//等待信号量到来,等待20个系统时钟周期

    //若发生错误,即在20个周期内未收到sem_rx信号

    //这将表示这个串口在20个时钟周期内没有收到数据

    if(os_status){

        //如果串口有数据,表示一个包已经接收完了。

        if(rx_length > 0){

            //读出这个包的数据,注意这个包不能超过1024个字节

            rx_len = tls_uart_read(TLS_UART_1, rx_fifo_buf, rx_length);

            rx_length = 0;

            tls_uart_write(TLS_UART_1, rx_fifo_buf, rx_len); //输出

        }

    }

 }

...


这里的等待20个时钟周期,是我自己随便估计出来的,自已可以根据串口的波特率情况,修改这个参数。


但是原则上有两条:


值太小的话,会导致线程频繁运行,即使串口没数据,它也会定期运行,值越小单位时间运行的次数就越多,会影响mcu性能。

值太大,会导致连包,也就是明明是两个包,但是读的时候当成了一个包

所以合理的等待时间是很重要的。


3. 运行结果

在这里插入图片描述

关键字:联盛德  SDK  UART 引用地址:联盛德 HLK-W801(三):在SDK粥中盛出UART

上一篇:联盛德 HLK-W801(四):W801的PWM实现
下一篇:联盛德 HLK-W801(一):串口下载复位和使用其他串口工具调试的问题分析和解决方法

推荐阅读最新更新时间:2024-11-13 13:41

开启Cache后UART无法发送新数据
有人使用STM32H743做产品开发, DMA 传输待发送的数据到 UART 发送寄存器做后续UART通信。在开启D-Cache的情况下,发现UART没法发送更新过的数据。 具体应用场景是这样的,源数据放在STM32H743片内D1域的AXI-SRAM区,数据会不定期地被CPU修改,然后让DMA将数据传输到USART3的发送寄存器进行后续UART通信。结合手册可以查得USART3位于D2域。 目前开启了D-Cache/I-Cache。我基于现有场景写了一段简单的如下测试代码【编译环境使用STM32CubeIDE】: __attribute__((section( .Source ))) uint8_t Source ; uin
[单片机]
开启Cache后<font color='red'>UART</font>无法发送新数据
一知半解学CubeMX——UART:Printf实现
一知半解学习环境: 1、CubeMX 4.23.0 2、uVision 5.14.2 3、MCU 启明 STM32F407 开发板(高配版) V3.1 一知半解学习目标: 1、掌握CubeMX下RCC以及SYS的相关配置; 2、掌握CubeMX下串口配置; 3、掌握Printf的实现套路; 一知半解实现过程: 1、配置系统调试接口及时基时钟源; 2、配置外部高速时钟源; 3、根据原理图配置对应串口(USART1); 4、配置时钟树; 5、配置串口参数,保持默认参数即可; 6、生成工程,并添加程序; 首先,打开工程,在usart.c文件的最后添加如下程序;
[单片机]
一知半解学CubeMX——<font color='red'>UART</font>:Printf实现
采用SDK-700系统设计套件的全新物联网解决方案
    Arm宣布推出一套基于PSA规范的全新物联网解决方案——Arm SDK-700系统设计套件,以用于加速安全SoC的开发。作为一套综合的SoC系统框架,使用 Arm SDK-700系统设计套件可设计安全的SoC,并应用于丰富多样的IoT节点、网关设备和嵌入式产品。该解决方案不仅使合作伙伴能够在通用软件开发环境中打造安全设备,同时还使其业务的多样性和差异化可以在新的物联网应用中蓬勃发展。     Arm是物联网的首选架构,迄今为止已为1250亿芯片提供了计算能力。公司有一个宏大的愿景,即到2035年实现1万亿个设备的安全互连。但在实现这一愿景之前,业界需要达成共识——不再让安全成为互连设备价值链各环节的后顾之忧。为确保日益多
[物联网]
stm32f103 uart通信简析
USART 通用同步收发器,UART 通用异步收发器 支持LIN(局部互联网)、智能卡协议、IrDA(红外)、以及调制解调器(CTS/RTS)等。全双工交换数据、即收发同时进行。 串行通信的通信方式 1.同步通信:带时钟同步信号传输,有一根线是同步时钟。例如SPI(全双功)、IIC(半双工)通信接口 2.异步通信:不带时钟同步信号,必须约定好波特率。例如UART(全双功) STM32的串口通信接口 UART:通用异步收发器。 USART:通用同步异步收发器。 STM32F10x系列包含3个USART和2个UART。
[单片机]
STM32的UART读写及printf打印
0.摘要 本文以STM32F1x系列单片机为例,主要介绍了串口的初始化、串口中断、接收/发送、串口调试等内容,也顺带讲到中断分组、半主机模式以及微库MicroLIB。 1.串口初始化 串口初始化主要包括对IO、USART和中断的初始化。根据STM32F1x手册RM0008的P166,USART在全双工模式下,发送口TX要配置成复用推挽输出,接收口RX要配置成浮空输入或上拉输入。此外,本文不使用USART的硬件流控制,所谓硬件流控制就是通过加入额外的引脚(RTS和CTS)来控制数据的收发过程,在数据传输之前确认收发双方均准备好才进行通信,用于防止接收缓冲区满而导致的数据丢失问题。 /********************
[单片机]
STM32的<font color='red'>UART</font>读写及printf打印
基于多域视频联网监控解决方案
跨区域、大范围的 联网监控 系统是一个涉及到多个层面的复杂系统,系统的整体设计和联网技术应用至关重要。在多域联网监控的建设中,使用通信业界通用的、公开的SIP技术,可以具有很好的通用性和复制性;采用监控系统平台间互联互通的方式,可以方便地实现图像资源的跨域共享;而遵循业界已经得到实践的互联标准协议,则可以事半功倍,且开放性好。   一、域的概念 在探讨 多域视频 监控联网解决方案之前,我们首先要对其中域的概念进行说明。 域指的是一个独立的组织单元,在监控系统中,我们把域分为物理域、逻辑域。 物理域也称实体域、自治域,有域内唯一的认证注册管理服务器,负责全域内设备统一注册、用户登录认证和管理,可以直接或间接管理前端设备。通常理解的监
[嵌入式]
ARM7单片机(学习ing)—(三)、UART—01
三、UART 三—(01)、UART相关应用和寄存器的介绍~~ 一、UART0(UART1与UART0相同,只是增加了一个调制解调器(Moderm)接口~~我就不介绍了~~ 1、特性:(个人感觉特性的了解是入门的必经之路~~尤其是FIFO的性能~~无人能挡啊~~) 1)16字节收发FIFO 2)寄存器位置符合‘550’工业标准 3)接收器FIFO触发点可以为1、4、8、和14字节 4)内置波特率发生器 2、管脚描述: 3、寄存器描述:(这个尤其的重要~~) a、接收缓存寄存器 U0RBR 0XEOOOCOOO, DLAB=0, 只读(它包含了最早接收到的字符~~) b、发送器保持寄存器 U0THR 0X
[单片机]
ARM7单片机(学习ing)—(三)、<font color='red'>UART</font>—01
AVR单片机ATMega16的UART通信总结
#include iom16v.h #include macros.h typedef unsigned char uint8_t; #define DF_Config_Uart0_BaudRate 9600 //UART0 初始化 // desired baud rate: 9600 // actual: baud rate:9600 (0.0%) void uart0_init(void) { UCSRB = 0x00; //disable while setting baud rate UCSRA = 0x00; UCSRC = BIT(URSEL) | 0x06; // 配置波特率 #if DF_Confi
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
  • 学习ARM开发(16)
    ARM有很多东西要学习,那么中断,就肯定是需要学习的东西。自从CPU引入中断以来,才真正地进入多任务系统工作,并且大大提高了工作效率。采 ...
  • 学习ARM开发(17)
    因为嵌入式系统里全部要使用中断的,那么我的S3C44B0怎么样中断流程呢?那我就需要了解整个流程了。要深入了解,最好的方法,就是去写程序 ...
  • 学习ARM开发(18)
    上一次已经了解ARM的中断处理过程,并且可以设置中断函数,那么它这样就可以工作了吗?答案是否定的。因为S3C44B0还有好几个寄存器是控制中 ...
  • 嵌入式系统调试仿真工具
    嵌入式硬件系统设计出来后就要进行调试,不管是硬件调试还是软件调试或者程序固化,都需要用到调试仿真工具。 随着处理器新品种、新 ...
  • 最近困扰在心中的一个小疑问终于解惑了~~
    最近在驱动方面一直在概念上不能很好的理解 有时候结合别人写的一点usb的例子能有点感觉,但是因为arm体系里面没有像单片机那样直接讲解引脚 ...
  • 学习ARM开发(1)
  • 学习ARM开发(2)
  • 学习ARM开发(4)
  • 学习ARM开发(6)
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved