极客秀
搜索

FireBLE裸奔之二UART

关于UART/USART对于调试的重要性,在我的另一篇文章中已经详细谈及!也详细谈到了UART和USART之间的区别和联系。
所以在操作了MCU的I/O口之后的第一件事就是调通UART的目的就很明显了!没错!我就是为了将它作为调试代码的工具使用。
所以对于FireBLE来说,调通UART有如下作用:

  • 作为调试工具,调试代码。
  • 为蓝牙模块做透传或者作数据通信端口做准备。
    众所周知,无论是蓝牙模块还是WIFI模块,当其被作为与MCU通信作数据透传时,通常提供给MCU的接口就是UART/USART和SPI通信接口。
    那么也就是做,模块在这种模式下工作时,UART/USART就是作为与外部MCU通信和大量数据传输的方式之一,所以调通UART也是为了与外部进行大量数据通信做准备。

OK!基本的就解释清楚了!那就开始码代码:
1、确定UART硬件端口
看硬件原理图:

可以看到,FireBLE底板采用了PL2303作为COMS电平和TTL电平的转换芯片,进行与PC机通信,这是专门为了与PC机进行通信的,比如说,串口助手或者ISP程序烧录等。
在图中也能找到我们想要的信息:

  • UART_RXD对应的GPIO口为P17口
  • UART_TXD对应的GPIO口为P00口
    再查看QN9021的Datasheet,如下图:

    嘿嘿!一眼看出,咱需要调试的是UART0哦!总算是找到目标了!
    2、初始化UART



还是老一套,打开UART时钟,初始化GPIO口并复用GPIO口,初始化UART(时钟、波特率等等的设置),使能UART的Rx和Tx。这就完事了!
但是呢!作为学习新的东西,并且以后还要跑协议栈,或者说为了以后查找BUG方便,咱还是浏览一下一些函数原型,在这里值得浏览的就是

uart_init(QN_UART0, __USART_CLK, UART_115200)了
因为在这里配置的是UART0,所以只跟踪与UART0相关的部分;如下图:



从上面三个图中可以看到,

  • 在uart_init()函数中已经调用了uart_clock_on(UART);函数,开启UART的时钟。
  • 默认情况下,是不会打开Rx和Tx中断的,也不会打开DMA,最后就是关于UART的配置了!
    还记得前面我们自己的关于UART的配置吗?
    自己调用uart_clock_on(UART);函数函数打开了UART的时钟,既然提供的官方Demo已经在内部干了这事,所以其实代码就可以简化成如下了:

    3、移植复用printf和scanf
    作为调试,我们难免会对一些字符串进行标识打印,同时也会打印一些数值,或者字符串和数值混合打印;在官方库给我们提供的打印函数基本上就分为了两种:
  • 字符串打印
  • 数据传输函数
    呵呵!我们知道,要打印数值并且打印的数组能在串口助手打印出我们想要的(比如数组45要打印出来,就必须做数值和字符串的转换,将字符串“45”进行打印。
    这才是我们想要在串口助手上想看到的,否则直接打印数值45,将会在串口上打印出值为45的ASCII码),多了一步转换,而官方是不会放我们干这事的,官方的提供就只是将UART正确通信,所以,我们就有两种方案选择了:
  • 手动的将数值转换为字符串
  • 移植printf函数
    所以为了方便,作为调试,果断的就选择了移植标准C的printf和scanf这两个函数。如下图:

    因为标准C为不同的硬件提供了printf和scanf的输入输出接口,所以我们只需要在这个接口上添加我们的输入输出设备即可,在这里就是UART的Tx和Rx啦!
    值得声明的一点是,移植printf和scanf仅仅只是为了调试,如果需要作设备之间的大量数据传输,那么printf和scanf是不合适的,因为它是标准字符设备。具体的玩过就知道了!哈哈!
    最后声明的是:

在使用printf和scanf的时候,最后用宏做一个开关,因为它只用来做调试,当项目完成的时候,它就没有必要存在了!所以用宏来控制是最好
的方法,否则一个一个的删除也不见得是键容易的事。这纯属事调试经验哈!
OK!Printf和scanf的移植就算是完事了!继续往下!
4、函数调用
关于函数的调用,那就是在主函数的调用喽!如下图:

过多的就不写了!就简单的几句打印!也不再解释!编译Rebuild无错误无警告,烧录程序看现象!有这么以下3个现象:

  • LED只有蓝灯亮(怎么和在点灯的时候的缺省情况一样呢?),也不会闪烁。
  • UART有打印,并且打印正常
  • UART能接收数据,当时总感觉不太正常
    呵呵!没有想象中的结果!这是啥情况呢?这其中肯定有鬼!我通过LED的现象进行查找,最终发现原因出在了如下图:

    按道理来说,这是官方提供的东西,应该没错!但是为毛我就说这里有问题呢?其实,这个函数本身是没有问题的,只是我们使用出了问题。现在看看它的原型:

    可以看出来了:

syscon_SetPMCR0(QN_SYSCON_TypeDef *SYSCON, uint32_t
value)函数和syscon_SetPMCR0WithMask(QN_SYSCON_TypeDef *SYSCON,uint32_t mask,
uint32_t value)函数都是作为设置I/O口功能的函数,但是他们的内容是不同的。
从原型可以看到,他们操作的寄存器是相同的,但是他们操作的方式不同,通过了解得知,wr_reg((uint32_t)&SYSCON->CRSS,
value);操作寄存器只是简单的对寄存器进行赋值,每次赋值都会对寄存器的所有位造成影响。
而函数__wr_reg_with_msk((uint32_t)&SYSCON->PMCR0, mask,
value);对寄存器是按位操作,只操作寄存器的某一位,而不影响寄存器的其他不需要操作的位;

那么意思很明显了,也就是说syscon_SetPMCR0()函数对寄存器的操作只是简单的赋值,而syscon_SetPMCR0WithMask()函数对寄存器只操作需要操作的位。
所以对于以上遇到问题的原因就很明了了!因为我使用的隔阂LED点灯是同一个工程,LED的IO口功能是使用syscon_SetPMCR0()函数配置的,而UART的初始化使用的也是syscon_SetPMCR0()函数。
再加上在main函数中调用时,先初始化LED,再初始化UART,所以就相当于前面的LED被后面的UART初始化操作覆盖了,相当于没初始化,所以,所以就没所以了!
所以就出问题了!那么为了修正程序,将代码改如下:



为了以后不会再出任何错误,所以同时也将LED的初始化为如下图:

OK!再Rebuild,烧录程序!一切按预想效果运行!OK!这就解决了!
总结: 经常看到好的童鞋直接将官方例程编译烧录看效果,随便看看代码,就算是完事了!这样的效果是不好的!
因为单个的使用,官方可能就使用了不同的方法,当真正的做工程的时候,这是不兼容的,就比如上面遇到的问题,做单个实验,基本上很少有人会去注意这个,因为它是正确的。
所以,在学习时,从一个工程开始是各种设备的Demo、驱动等兼容在一个工程里!这对开发和学习都是有非常大的好处的!

1.转载请保留原文链接谢谢!
2.本站所有资源文章出自互联网收集整理,本站不参与制作,如果侵犯了您的合法权益,请联系本站我们会及时删除。
3.本站发布资源来源于互联网,可能存在水印或者引流等信息,请用户擦亮眼睛自行鉴别,做一个有主见和判断力的用户。
4.本站资源仅供研究、学习交流之用,若使用商业用途,请购买正版授权,否则产生的一切后果将由下载用户自行承担。
5.联系方式(#替换成@):pm#vimge.com

  相关内容