CSR8675 使用串口 UART 收发功能

CSR8675 实现 UART 功能有两种方式,一种是托管连接,另一种是直接连接

托管连接:不直接操作 Stream,通过 VM 层创建 Source 和 Sink 来实现数据传输,由库进行处理,实现比较方便。
直接连接:直接操作 Stream ,源是一个存储区域,通过对内存进行操作实现数据传输,处理数据传输效率更高。

此处暂时先用 托管连接 的方式来介绍,后面有空再更新 直接连接

1、使用 RAW 传输

在工程属性下,将 Transport 属性改为 RAW。

2、打开调试宏

在 sink_debug.h 中加入调试宏

3、编辑串口代码,实现 loopback 功能

主要实现4个函数

  1. 消息处理函数
  2. 串口初始化函数
  3. 串口接收函数
  4. 串口发送函数

<sink_uart.h>

#ifndef __UART_H_
#define __UART_H_void UARTStreamMessageHandler (Task pTask, MessageId pId, Message pMessage);
void uart_data_stream_rx_data(Source src);
void uart_data_stream_tx_data(const uint8 *data, uint16 length);
void uart_data_stream_init(void);#endif /* __UART_H_ */

<sink_uart.c>

#include <stream.h>
#include <sink.h>
#include <source.h>
#include <string.h>
#include <panic.h>
#include <message.h>
#include <app/uart/uart_if.h>
#include <stdio.h>
#include <string.h>
#include "uart.h"
#include "sink_debug.h"#ifdef DEBUG_UART
#define UART_DEBUG(x) DEBUG(x)
#else
#define UART_DEBUG(x)
#endiftypedef struct
{TaskData task;Sink uart_sink;Source uart_source;
}UARTStreamTaskData;UARTStreamTaskData theUARTStreamTask;void uart_data_stream_init(void)
{/* Assign task message handler */theUARTStreamTask.task.handler = UARTStreamMessageHandler;/* Configure uart settings */StreamUartConfigure(VM_UART_RATE_38K4, VM_UART_STOP_ONE, VM_UART_PARITY_NONE);/* Get the sink for the uart */theUARTStreamTask.uart_sink = StreamUartSink();if(theUARTStreamTask.uart_sink != 0)PanicNull(theUARTStreamTask.uart_sink);/* Get the source for the uart */theUARTStreamTask.uart_source = StreamUartSource();if(theUARTStreamTask.uart_source != 0)PanicNull(theUARTStreamTask.uart_source);/* Register uart source with task */MessageSinkTask(StreamSinkFromSource(theUARTStreamTask.uart_source),&theUARTStreamTask.task);
}void uart_data_stream_tx_data(const uint8 *data, uint16 length)
{uint16 offset = 0;uint8 *dest = NULL;/* Claim space in the sink, getting the offset to it */offset = SinkClaim(theUARTStreamTask.uart_sink, length);if(offset == 0xFFFF) Panic();/* Map the sink into memory space */dest = SinkMap(theUARTStreamTask.uart_sink);PanicNull(dest);/* Copy data into the claimed space */memcpy(dest+offset, data, length);/* Flush the data out to the uart */PanicZero(SinkFlush(theUARTStreamTask.uart_sink, length));
}void uart_data_stream_rx_data(Source src)
{uint16 length = 0;const uint8 *data = NULL;/* Get the number of bytes in the specified source before the next packet boundary */if(!(length = SourceBoundary(src)))return;/* Maps the specified source into the address map */data = SourceMap(src);PanicNull((void*)data);/* Transmit the received data */uart_data_stream_tx_data(data, length);UART_DEBUG(("UART: Rx: length = %d\n",length));/* Discards the specified amount of bytes from the front of the specified source */SourceDrop(src, length);
}void UARTStreamMessageHandler (Task pTask, MessageId pId, Message pMessage)
{switch (pId){case MESSAGE_MORE_DATA:uart_data_stream_rx_data(((MessageMoreData *)pMessage)->source);break;default:break;}
}

4、调用函数,实现 loopback

修改PSKey
01EA UART_BITRATE 改为38400

01C2 UART_CONFIG_USR 改为 0880

在 main 函数中,MessageLoop()之前,调用串口初始化函数。

按F5下载到开发板上。
接上串口,打开串口工具,串口号在电脑的设备管理器查看,波特率设置为38400,数据位为8,停止位为1,奇偶校验位为none。

但是存在一个小问题,发了10个数据,在接收函数中打印一下数据长度,会发现进入了2次接收函数,第一次接收了1个字节,第二次将剩余的字节全部接收。

操作多几次验证,发现一个规律,就是第一次总是接收1个字节,然后剩下的数据接收的长度会随机改变,原因可能是底层库通过流的方式实现,此处用的是异步串口,没有硬件流控,而且CSR8675本身的串口 buffer 空间不大,才会导致接收的时候将数据截断。
因此,在使用异步串口过程中,需要根据实际情况,对接收的数据增加拼接的处理,同时加入自定义的协议,增加帧头、帧尾、数据长度、数据校验等信息,才能够保证接收到的内容是正确的。


持续更新–2019.10.31

5、解决 UART 接收时数据分段出来的现象

MESSAGE_MORE_DATA 此 id 附带的 message,本质也是一个流,只要达到接收 buffer 上有数据,就会发送这个 MessageID。
但是,只要不对这个 buffer 进行 Drop,下一次的 MESSAGE_MORE_DATA 依然会将原来数据数据积累起来,在下一次的时候发送。
buffer 大小为4K,如果不及时 Drop,则会内存溢出挂掉
因此,只需要对收到的数据进行长度的判断:
长度足够 --> 处理收到的内容,并将其 Drop。
长度不够 --> return,退出函数,等待下一次消息。

串口数据协议可以自定义,暂时按照这种方式,可传输的数据最大为 255 Byte。可以根据需要增加 数据长度CRC校验

head command length data end description
07 01 n n Byte 0A 可变长度数据处理
07 02 1 1 Byte 0A Notification
07 03 1 1 Byte 0A Status
07 04 1 1 Byte 0A Ask

下面对接收函数进行修改,思路是如果收到正确的数据,则处理掉,否则检查数据完整性,并丢掉无效数据;如果长度不够,则跳过此次接收。

void uart_data_stream_rx_data(Source src)
{uint16 length = 0;uint16 length_drop = 0;const uint8 *data = NULL;recheck:    if(!(length = SourceBoundary(src)))return;data = SourceMap(src);PanicNull((void*)data);UART_DEBUG(("rx source size = %d \n",SourceSize(src)));  /* 数据不够最小一帧,跳出,下次再处理 */if(length < UART_DATA_MINLEN){return;}/* 第一个位帧头,开始解析 */else if(data[0] == UART_DATA_HEAD){switch(data[1]){case UART_HIBY_LINK_DATA:/* 收到的数据长度不够,跳过不处理 */if(data[2] > length-COMMAND_LENGTH ){UART_DEBUG(("HiBy Link command,length = %d ,rx len = %d,NOT enough\n",data[2],length));  return;}/* 收到足够的数据,处理 */else {length_drop = data[2] + COMMAND_LENGTH ;UART_DEBUG(("HiBy Link command length = %d , length = %d\n",data[2],length_drop));  /* 检查帧尾是否正确 */if(data[(length_drop - 1)] == UART_DATA_END){/* send to spp */UART_DEBUG(("HiBy Link data send : %x ... %x \n",data[3],data[2+data[2]]));  }SourceDrop(src, length_drop);/* 如果还有数据,则在执行一次 */if(length > length_drop ){goto recheck;}}break;case UART_NOTIFICATION:if(data[2] == 1 && data[4] == UART_DATA_END){UART_DEBUG(("Notification command = %x \n",data[3]));  SourceDrop(src, UART_DATA_MINLEN);if(length > UART_DATA_MINLEN){goto recheck;}}break;case UART_STATUS:if(data[2] == 1 && data[4] == UART_DATA_END){UART_DEBUG(("Status command = %x \n",data[3]));  SourceDrop(src, UART_DATA_MINLEN);if(length > UART_DATA_MINLEN){goto recheck;}}break;case UART_ASK:if(data[2] == 1 && data[4] == UART_DATA_END){UART_DEBUG(("Ask command = %x \n",data[3]));  SourceDrop(src, UART_DATA_MINLEN);if(length > UART_DATA_MINLEN){goto recheck;}}break;default:UART_DEBUG(("commmon error, drop min length.\n"));  SourceDrop(src, UART_DATA_MINLEN);if(length > UART_DATA_MINLEN){goto recheck;}break;}}/* 第一个字节不是帧头,可能数据有误,查找帧头,并丢弃之前数据 */else{uint16 err_data_len = 0;while(err_data_len < length){/* 找到帧头 */if(data[err_data_len] == UART_DATA_HEAD){/* 判断是否真的是帧头 ,通过检测上一个字节是否帧尾 */if(data[err_data_len - 1] == UART_DATA_END ){/* 保证下一字节不溢出,并且为命令字节 ,然后去掉前面错误的数据,将正确的数据保留下来,留给下一次处理*/if( (err_data_len + 1) < length  &&(data[err_data_len + 1] == UART_HIBY_LINK_DATA ||data[err_data_len + 1] == UART_NOTIFICATION ||data[err_data_len + 1] == UART_STATUS ||data[err_data_len + 1] == UART_ASK)){UART_DEBUG(("Drop error data,and has find the real data.\n"));  SourceDrop(src, err_data_len);goto recheck;}}}err_data_len ++;}SourceDrop(src, length);UART_DEBUG(("Drop data, without real data,drop all data.\n"));  return;}
}

CSR8675 使用串口 UART 收发功能相关推荐

  1. AC695X_独立3路串口UART收发数据配置

    SDK:AC695N_soundbox_sdk_release_3.1.0 AC695X目前这个SDK是最新的,普通音响的软件包. 其实AC608_AC696X_也可以类似配置,也可以3路串口独立收发 ...

  2. Keil实例仿真AT89C51串口UART收发数据(附程序)

    目录 一.引言 二.所用软件 1.串口调试工具 2.虚拟串口软件 3.Keil μVision5 三.软件设置 1.串口调试助手软件设置 2.虚拟串口软件设置 3.Keil C51设置 A.调试(.i ...

  3. micropython stm32f030_STM32F0单片机快速入门六 用库操作串口(UART)原来如此简单

    1.从 GPIO 到 UART 前面几节我们讲了MCU如何启动,如何用翻转IO引脚,以及用按键去触发中断.接下来我们介绍的也是最常用的一个模块,串口(UART). 串口可以说是最古老,而且生命力最强的 ...

  4. rxtx串口事件不触发_一种串口高效收发思路及方案

    摘要:本文在探讨传统数据收发不足之后,介绍如何使用带FIFO的串口来减少接收中断次数,通过一种自定义通讯协议格式,给出帧打包方法:之后介绍一种特殊的串口数据发送方法,可在避免使用串口发送中断的情况下, ...

  5. hal库串口dma卡死_HAL库版DMA循环模式串口数据收发

    在<STM32CubeMX初识与工程创建>的基础上,首先对串口进行设置,以实现通过串口对数据的收发.STM32CubeMX生成的HAL库中,提供了三类串口数据收发的接口,分别为阻塞模式,非 ...

  6. csr867x入门之串口数据收发(二)

    目录 Uart功能模块实现 将uart库导入工程中 添加应用层逻辑 使用 Uart功能模块实现 默认4.1的adk中是不包含uart收发的应用层逻辑,所以需要自己实现 在src/lib目录下新建uar ...

  7. 单片机串口高效收发数据的实现方法

    想学习单片机的同学可以关注.私信我或者在评论区回复我要入门.这一期我们探讨传统数据收发不足之后,如何使用带FIFO的串口来减少接收中断次数,通过一种自定义通讯协议格式,给出帧打包方法:之后介绍一种特殊 ...

  8. 痞子衡嵌入式:嵌入式里串口(UART)自动波特率识别程序设计与实现

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是嵌入式里串口(UART)自动波特率识别程序设计与实现. 串口(UART)是嵌入式里最基础最常用也最简单的一种通讯(数据传输)方式,可以说 ...

  9. stm32f103——串口UART

    在学习UART之前,我们先来了解一下单片机与外围设备之间的通信: 单片机与外围设备之间的信息交换和传输我们称为通信.过去通信方式有两种:并行通信和串行通信. 并行通信: 定义:并行通信是指利用多条传输 ...

最新文章

  1. 【Android 插件化】使用插件化引擎对应用进行重打包的恶意软件特征 ( 检测困难 | 成本低 | 恶意插件可更换 | 容易传播 )
  2. zoj 1670 Jewels from Heaven
  3. GitHub推出“AI程序员”插件 !
  4. maven netty 配置_进阿里、腾讯、字节跳动、美团必掌握的Netty
  5. 智慧城市助力城市管理 推动新兴产业升级
  6. Spring MVC表单实例
  7. 15投影矩阵与Moore-Penrose逆(1)
  8. RSLogix 5000 含序列号 20.03版本,带授权
  9. Python实现文件编码转换GB2312、GBK、UTF-8
  10. 关于N卡录制双音轨问题以及PR2020 注册机
  11. 手机java时代浏览器_巅峰之战 三款最热java手机浏览器横评
  12. linux 6重启网卡,centos 网卡重启方法
  13. 第3章第11节:如何将众多图片制作成照片墙并作为幻灯片的背景 [PowerPoint精美幻灯片实战教程]
  14. 计算机基础文献检索考核,文献检索实验
  15. 【车牌识别】基于模板匹配实现车牌识别含Matlab源码
  16. Java中找出缺失的数字
  17. Linux-无密码访问、远程拷贝、无密码登录
  18. 算法学习过程入门篇(2)-算法初步
  19. 微信公众号怎么进行测试?
  20. 【深度学习之模型优化】模型剪枝、模型量化、知识蒸馏概述

热门文章

  1. 压测工具Jmeter入门使用
  2. python:实现二进制补码算法(附完整源码)
  3. Python数据分析学习路径图:堪称史上最全
  4. [Rust实战]初探 actix_web
  5. linux sd卡驱动教程,Linux SD卡驱动开发(四) —— SD 控制器之真正的硬件操作
  6. 我国研究计算机的科学家,我国量子计算机研究取得重大国际突破
  7. Hbase Sql 层 Phoenix 的三个特性Row timestamp, Sequences 和 Salted Tables
  8. 基于STM32+OneNet设计的物联网智慧路灯
  9. VB.net GDI+ 内存不足,一般性错误,对象错误等解决方案和优化代码。
  10. 21.04.07-直角坐标系的Location类