一、简介

本文介绍如何在SimpleBLEPeripheral工程中,使用串口。

二、实验平台

协议栈版本:BLE-CC254x-1.4.0

编译软件:IAR 8.20.2

硬件平台:Smart RF(主芯片CC2541)

、版权声明

博主:si_zhou_qun_84342712

声明:喝水不忘挖井人,转载请注明出处。

原文地址:http://write.blog.csdn.net/postedit

联系方式:495527583@qq.com

四轴开源群:84342712

四轴开源(淘宝店):

四、基础知识

1、协议栈的串口默认使用的是查询方式,还是中断方式?

答:默认使用的是DMA的查询方式。

2、PC端为什么收不到2541的串口数据?

答:最常见原因有两个:

1)没有关闭流控。(关闭步骤见下文)

2)开了低功耗。快速解决办法如下

3、低功耗下是否能使用串口?

答:不能。进低功耗前要关闭串口,唤醒后要再使能串口。

4、如何关闭串口?(未实践,提供思路)

答:有群友指出一种方法:

使用dma方式、isr方式的串口,将串口引脚映射为普通IO口,即为关闭。

5、低功耗唤醒后串口没用了?(未实践,提供思路)

答:由于低功耗与正常模式的切换,会导致晶振的32k与32M的切换。而串口等外设使用的是32M的外部晶振,因此低功耗唤醒后需要做两件事:

1)适当延时等待32M晶振稳定。

2)再次初始化一次串口。

五、修改代码

1、IAR设置中添加宏定义

  1. HAL_UART=TRUE

2、修改串口驱动

1)添加头文件(npi.c中)

  1. #include "OSAL.h"

2)新增两个串口发送函数(npi.c中)

  1. //******************************************************************************
  2. //name:     NPI_PrintString
  3. //introduce:    打印字符串
  4. //parameter:    str:字符串
  5. //return:   none
  6. //******************************************************************************
  7. void NPI_PrintString(uint8 *str)
  8. {
  9. NPI_WriteTransport(str, osal_strlen((char*)str));
  10. }
  11. //******************************************************************************
  12. //name:     NPI_PrintValue
  13. //introduce:    打印指定的格式的数值
  14. //parameter:    title:前缀字符串
  15. //              value:需要显示的数值
  16. //              format,需要显示的进制,10或16
  17. //return:   none
  18. //******************************************************************************
  19. void NPI_PrintValue(char *title, uint16 value, uint8 format)
  20. {
  21. uint8 tmpLen;
  22. uint8 buf[128];
  23. uint32 err;
  24. tmpLen = (uint8)osal_strlen( (char*)title );
  25. osal_memcpy( buf, title, tmpLen );
  26. buf[tmpLen] = ' ';
  27. err = (uint32)(value);
  28. _ltoa( err, &buf[tmpLen+1], format );
  29. NPI_PrintString(buf);
  30. }

3)函数声明(npi.h中)

  1. extern void NPI_PrintString(uint8 *str);
  2. extern void NPI_PrintValue(char *title, uint16 value, uint8 format);

3、包含头文件(simpleBLEPeripheral.c中)

  1. #include "npi.h"

4、新增初始化代码(simpleBLEPeripheral.c的SimpleBLEPeripheral_Init中)

  1. //初始化串口
  2. NPI_InitTransport(NpiSerialCallback);
  3. // 按长度输出2
  4. NPI_WriteTransport("SimpleBLETest_Init\r\n", 20);
  5. // 按字符串输出
  6. NPI_PrintString("SimpleBLETest_Init2\r\n");
  7. // 可以输出一个值,用10进制表示
  8. NPI_PrintValue("甜甜的大香瓜1 = ", 168, 10);
  9. NPI_PrintString("\r\n");
  10. // 可以输出一个值,用16进制表示
  11. NPI_PrintValue("甜甜的大香瓜2 = 0x", 0x88, 16);
  12. NPI_PrintString("\r\n");

5、定义并声明一个串口回调函数

1)定义串口回调函数(simpleBLEPeripheral.c中)

  1. static void NpiSerialCallback( uint8 port, uint8 events )
  2. {
  3. (void)port;//加个 (void),是未了避免编译告警,明确告诉缓冲区不用理会这个变量
  4. if (events & (HAL_UART_RX_TIMEOUT | HAL_UART_RX_FULL))   //串口有数据
  5. {
  6. uint8 numBytes = 0;
  7. numBytes = NPI_RxBufLen();           //读出串口缓冲区有多少字节
  8. if(numBytes == 0)
  9. {
  10. return;
  11. }
  12. else
  13. {
  14. //申请缓冲区buffer
  15. uint8 *buffer = osal_mem_alloc(numBytes);
  16. if(buffer)
  17. {
  18. //读取读取串口缓冲区数据,释放串口数据
  19. NPI_ReadTransport(buffer,numBytes);
  20. //把收到的数据发送到串口-实现回环
  21. NPI_WriteTransport(buffer, numBytes);
  22. //释放申请的缓冲区
  23. osal_mem_free(buffer);
  24. }
  25. }
  26. }
  27. }

2)声明串口回调函数(simpleBLEPeripheral.c中)

  1. static void NpiSerialCallback( uint8 port, uint8 events );   //串口回调

6、关流控(npi.h中)

  1. #if !defined( NPI_UART_FC )
  2. #define NPI_UART_FC                    FALSE //TRUE
  3. #endif // !NPI_UART_FC

流控是用于防止串口阻塞的,需要多两根线用于流控。

通常使用中都是关闭的,因此我们这里关闭之。

注意!!如果这里不关闭,会导致串口通信不了。

六、实验结果

初始化时打印了4条语句,最后一条是通过PC端的串口工具发给2541、2541再原样返回的值。

七、串口相关问题

1、上电时串口接收缓冲区会有一个字节,暂不明该数据哪里来的。暂时用接收处理将该无用字节过滤掉。

答:经检查,是P0_2(RX)使用内部上拉电阻,但电压仅为0.6V。上电时电压不稳定导致接收缓冲区会有一个字节的数据“0x00”。

因此解决方法是:外接1个10K上拉电阻到3.3V,将P0_2(RX)的电压拉高至3.3V。

注:TX引脚最好也外接上拉电阻。

2、串口什么时候进回调函数?

答:

1)正常串口端无发送、无接收时,是不会进回调函数的。如果这种情况会进回调函数,TX、RX端外接上拉电阻稳定电平。

2)如果接收端有数据,立马就会进回调。事件是“接收超时事件”。

3)2541发送端发送完数据,会进回调函数。事件是“发送缓冲区空事件”。

3、没开广播时串口正常使用,开了广播后串口出现乱码、丢包?

答:

1)在SimpleBLEPeripheral的应用层初始化中注释掉:
HCI_EXT_ClkDivOnHaltCmd( HCI_EXT_ENABLE_CLK_DIVIDE_ON_HALT );
PS:这条语句会让空闲的CPU自动进入低频以此降低功耗,注释掉代表不自动切换频率。

2)在SimpleBLEPeripheral的应用层初始化中添加:
HCI_EXT_HaltDuringRfCmd(HCI_EXT_HALT_DURING_RF_DISABLE);
PS:默认是ENABLE的,ENABLE会让RF期间停止MCU。因此需要添加本条语句,关闭它。

4、编译时出现警告“Warning[w52]: More than one definition for the byte at address 0x6b in common segment INTVEC. It is defined in  module "hal_uart" as well as in module "hal_key"”?

答:原因是IAR中包含了POWER_SAVING的宏之后,串口唤醒就需要用到IO中断,而协议栈中串口唤醒的默认IO口是P04脚。因此在_hal_uart_dma.c中包含了的P0中断服务函数就与Hal_key.c中包含的P0中断服务函数相冲突。

详情代码如下:

1)当IAR中包含了POWER_SAVING的宏之后,会定义一个DMA_PM的宏(_hal_uart_dma.c中)

  1. #if !defined( DMA_PM )
  2. #if defined POWER_SAVING
  3. #define DMA_PM                     1
  4. #else
  5. #define DMA_PM                     0
  6. #endif // POWER_SAVING
  7. #endif // !DMA_PM

2)一旦有了DMA_PM这个宏,_hal_uart_dma.c中就会包含P0的中断服务函数

  1. #if DMA_PM
  2. /**************************************************************************************************
  3. * @fn      PortX Interrupt Handler
  4. *
  5. * @brief   This function is the PortX interrupt service routine.
  6. *
  7. * @param   None.
  8. *
  9. * @return  None.
  10. *************************************************************************************************/
  11. #if (HAL_UART_DMA == 1)
  12. HAL_ISR_FUNCTION(port0Isr, P0INT_VECTOR)
  13. #else
  14. HAL_ISR_FUNCTION(port1Isr, P1INT_VECTOR)
  15. #endif
  16. {
  17. HAL_ENTER_ISR();
  18. PxIFG = 0;
  19. PxIF = 0;
  20. dmaRdyIsr = 1;
  21. CLEAR_SLEEP_MODE();
  22. HAL_EXIT_ISR();
  23. }
  24. #endif</span>

3)IAR设置中包含HAL_KEY=TRUE时,Hal_key.c中就会包含P0的中断服务函数

/************************************************************************************************** 
  1. * @fn      halKeyPort0Isr
  2. *
  3. * @brief   Port0 ISR
  4. *
  5. * @param
  6. *
  7. * @return
  8. **************************************************************************************************/
  9. HAL_ISR_FUNCTION( halKeyPort0Isr, P0INT_VECTOR )
  10. {
  11. HAL_ENTER_ISR();
  12. #if defined ( CC2540_MINIDK )
  13. if ((HAL_KEY_SW_1_PXIFG & HAL_KEY_SW_1_BIT) || (HAL_KEY_SW_2_PXIFG & HAL_KEY_SW_2_BIT))
  14. #else
  15. if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT)
  16. #endif
  17. {
  18. halProcessKeyInterrupt();
  19. }
  20. /*
  21. Clear the CPU interrupt flag for Port_0
  22. PxIFG has to be cleared before PxIF
  23. */
  24. #if defined ( CC2540_MINIDK )
  25. HAL_KEY_SW_1_PXIFG = 0;
  26. HAL_KEY_SW_2_PXIFG = 0;
  27. #else
  28. HAL_KEY_SW_6_PXIFG = 0;
  29. #endif
  30. HAL_KEY_CPU_PORT_0_IF = 0;
  31. CLEAR_SLEEP_MODE();
  32. HAL_EXIT_ISR();
  33. return;
  34. }

所以,解决办法如下:

方法一(治标不治本):IAR设置中将HAL_UART、HAL_KEY其中一个设置为FALSE。

方法二(治标不治本):不使用低功耗。

方法三(治本但改动了协议栈代码):将两个P0的中断服务函数合二为一。

//(待测)方法四(非强迫症人群使用):任其冲突,反正只是警告。

5、串口接收多少个字节能进入回调函数?

1)测试代码如下:

  1. uint8 cb_time = 0;
  2. uint8 numBytes_time1 = 0;
  3. uint8 numBytes_time2 = 0;
  4. uint8 numBytes_time3 = 0;
  5. /*********************************************************************
  6. *********************************************************************/
  7. static void NpiSerialCallback( uint8 port, uint8 events )
  8. {
  9. (void)port;//加个 (void),是未了避免编译告警,明确告诉缓冲区不用理会这个变量
  10. switch(++cb_time)   //记录进回调函数的次数
  11. {
  12. case 1:
  13. numBytes_time1 = NPI_RxBufLen();//第一次进来时接收的数据长度
  14. break;
  15. case 2:
  16. numBytes_time2 = NPI_RxBufLen();//第二次进来时接收的数据长度
  17. break;
  18. case 3:
  19. numBytes_time3 = NPI_RxBufLen();//第三次进来时接收的数据长度
  20. break;
  21. default:
  22. cb_time = 0;//无用语句,设置断点
  23. break;
  24. }
  25. }

2)测试结果

3)测试结论

2541串口接收端的数据是由DMA去接收的,2541会通过轮询的方式定期查询DMA的缓冲区是否有数据,一旦有数据即会进入回调函数(协议栈默认不开超时等待)。

上图说明了第一次进回调函数时DMA接收到了7个字节数据,它的长度与“主轮询时间”、“串口数据来的时机”有关系。

而第二次、第三次的接收数据之所以会比第一次多,是因为还加上了串口处理时接收到的数据。

【BLE】CC2541之串口收发相关推荐

  1. 蓝牙4.0BLE 手机控制 cc2540 CC2541 的串口透传功能已实现

    蓝牙4.0BLE 手机控制 cc2540 CC2541 的串口透传功能已实现 虽然蓝牙4.0 BLE芯片CC2540 是单芯片(即用户可以对它进行芯片级代码编写), 是8051增强型主控, 带蓝牙4. ...

  2. 基于Atmega8单片机的串口收发程序

    Atmega8出厂默认的内部时钟频率是1Mhz,做串口收发时波特率最高设置为4800,设置为9600会乱码,提高时钟频率才能将波特率提高. #include <avr/io.h> #inc ...

  3. 搭建串口收发与存储双口RAM简易应用系统

    搭建串口收发与存储双口RAM简易应用系统 为了实现通过串口发送数据到 FPGA 中, FPGA 接收到数据后将数据存储在双口 ram 的 一段连续空间中,当需要时,按下按键 S0,则 FPGA 将 R ...

  4. MTK:UART串口收发数据

    MTK之UART串口收发数据 转:https://blog.csdn.net/ivy_reny/article/details/51192110 寄存器 UARTn_RBR: Rx Buffer Re ...

  5. qt 串口粘包_用Qt 5写一个串口收发桌面工具

    今天用Qt写了一个串口收发工具,记录下整个流程. 1.项目工程布局 2.ui文件布局 3.widget.h文件 #ifndef WIDGET_H #define WIDGET_H #include # ...

  6. GD32F4xx串口收发,DMA+空闲中断

    GD32F4xx系列的串口收发DMA+空闲中断基础配置:(这里主要是以DMA+空闲中断为例) 原理就不赘述了,网上资料很多,这里直接进行配置和测试. 1,首先添加GD32F4xx的dma外设库函数文件 ...

  7. FPGA串口收发(四):接收数据并转发,间隔时间发送

    FPGA串口收发(四):接收数据并转发,间隔时间发送 // Description: 串口收发:串口接收数据,内部生成数据,串口间隔特定时间发送数据 // 串口接收数据:串行信号线 1101_1000 ...

  8. 【嵌入式】——串口实验——实现芯片串口收发数据,按键中断串口发送数据,串口接收数据中断来控制LED亮/灭

    实验目的: 实现芯片串口收发数据,按键中断串口发送数据:按下按键,向串口发送数据,并通过虚拟终端显示出来: 串口接收数据中断来控制LED亮/灭:通过串口助手向MCU发送数据,"A" ...

  9. QT5系列教程二---基于qcustomplot的QT5 GUI串口收发绘图软件实现

    QT5系列教程二---基于qcustomplot的QT5 GUI串口收发绘图软件实现 结构 UI部分 代码部分 step1:实现串口数据接受 串口接受数据格式 在`.pro`文件中添加`serialp ...

最新文章

  1. 更改python默认路径_Linux下多版本python共存时,默认执行路径修改方法
  2. php 检测死锁,MySQL 死锁检测
  3. matlab imformats,그래픽스 파일에 이미지 쓰기
  4. 记录——《C Primer Plus (第五版)》第十章编程练习第十题
  5. Python将字符串转为字典最佳实践
  6. COLING 2020 | 一种从科学文献中提取关键词的基于自蒸馏的联合学习方法
  7. React从0到1--JSX样式
  8. Python科学计算系列5—导数
  9. 鸿蒙不是手机系统?智慧屏曝光,华为将布局“贾维斯”智能时代
  10. 语音播报收款Java_Android实现收款成功金额的语音播报功能
  11. 微博有哪些营销价值?
  12. NoSQLBooster for MongoDB延长使用时间
  13. 【时间序列分析】06. AR(p)序列的性质
  14. PowerShield (软件加壳工具) PB混淆加密大师
  15. 文件包含漏洞——实例
  16. python百万邮件群发软件_python如何群发邮件
  17. 什么是SaaS平台?SaaS软件平台有什么优势
  18. AtCoder Regular Contest 105 C - Camels and Bridge
  19. 架构师如何设计架构,架构师的架构思路
  20. 大数据测试指标(二)

热门文章

  1. PIPI的逃跑路线Ⅳ
  2. JavaEE框架中间件
  3. HICA:数通/网络域
  4. 环境噪声监测环保数采仪
  5. Python基础学习-1-廖雪峰课程-Python的数据类型和面向对象
  6. 药名上如有这10个字请慎用!用错了会对身体带来更大伤害
  7. 警惕,11月这6本期刊已被剔除SCI/SSCI
  8. 【解决方案】TSINGSEE青犀视频EasyCVR智慧药品监管系统构建及实现
  9. 跑分软件测试原理,专业跑分软件测试2/总结_TCL Y710_手机Android频道-中关村在线...
  10. 五款电视软件全面横评