请选择 进入手机版 | 继续访问电脑版
搜索
查看: 1011|回复: 7

[原创] 【板卡活动】+PIC32MZEF+笔记5:鱼翔浅底(PIC32MZ的UART2)

[复制链接]
发表于 2017-10-20 02:26:11 | 显示全部楼层 |阅读模式
本帖最后由 南山九叔 于 2017-10-20 02:35 编辑

作者:南山九叔
邮箱:jiushu(at)tsinghua.info
微博:http://weibo.com/smartmcu

  今天主要写写UART,单片机调试什么都能没有,但是一盏LED和一个UART是必须的。今天就来看看PIC32MZ的UART2。

一、PIC32MZ串口通讯

  PIC32MZ单片机与UART相关的几个寄存器UxMODE, UxSTA, UxBRG, UxTXREG, and UxRXREG,其中x代表是第x个UART接口,如我们要使用的UART2,x=2。串口通信有几个步骤:
  1)设置波特率;
  2)配置串口数据类型;
  3)发送数据
  4)接收数据
  5)相关中断
  在Curiosity PIC32 MZ EF开发板上,J14引脚上引出了UART2,我们就用UART2来实验。


1、设置波特率
  如图1所示,PBCLK2输入到波特率发生器UxBRG,经过UxBRG分频后产生UART的采样时钟。所以UxBRG中的值决定串口通信的波特率。
同时,需要注意的是,串口通信由于是异步通信,所以需要多次采样才能确定传输的正确性。当UxMODE寄存器中的BRGH位=0时(默认),时钟进行16次采样确定1位值;BRGH位=1时,4次采样确定1位值。
  什么意思呢?说白了,就是要对RX或者TX中的电平连续读取多次,确定是高电平才认为是1,确定是低电平才认为是0,如果BRGH = 1那就确认4次,BRGH = 0就确认16次,当然确认16次更可靠。事有两面性,16次虽然可靠但是同样的时钟源,4次确认的波特率要比16次确认快4倍。所以在波特率能够满足需求的前提下,尽量使用16次采样(即BRGH = 0)

UART时钟源.png

图1、UART模块(主要看波特率发生器)


  搞清楚这个,就不难理解波特率的计算公式。式中FPB是外设总线时钟(PBCLK2)。其实,我们更关心的是根据波特率计算UxBRG寄存器的值:

BRG值计算公式

BRG值计算公式

图2、UxBRG值计算公式


  下面以配置波特率为115200 bps为例计算UxBRG的值,以UART2为例说明:
  (1)我们板子的PBCLK2时钟速度很快(系统时钟我配置的是200M,则PBCLK2 = 100MHz),足以产生115200 bps,所以我们不使用高波特率。配置RBGH=0(U2MODEbits.BRGH = 0)。
  (2)计算U2BRG的值:
  U2BRG = 100×10^6 / 16 / 115200 -1 =  53.2535
  显然不能给U2BRG赋值小数(UxBRG只能是16位整数),所以四舍五入取53。代入计算实际的波特率,保留一位小数:
  Baud Rate = 200×10^6 / (108+1) / 16 = 115740.7 bps。
  波特率误差率 error = (Baud Rate - 115200) / 115200 × 100% = 0.4694%。
  RS232规定波特率允许±2%的误差,error = 0.4694%在允许的误差范围内。所以U2BRG = 53 符合条件。
  经过上面探讨,具体代码如下:
  1. U2MODEbits.BRGH = 0;    // 使用16次采样模式
  2. U2BRG = 53;    //  波特率计算值(U2BRG = PBCLK2 / 16 / 波特率 -1)
复制代码
  (3)配置串口数据类型——模式寄存器UxMODE配置
  UARTx的参数主要在模式寄存器UxMODE中配置。本例程我们配置UART2:8位数据位,无校验位,1位停止位,不使用硬件流控制。由于这个配置在串口通信中最通用,所以默认就是了!全是0就行了。
  另外,其中ON位是使能位,这个需要打开,所以U2MODE配置为:
  1. U2MODE = 0x8000;   // 使能UART2:8-bit data, no parity, 1 stop bit
复制代码
  (4)使能收发——状态寄存器UxSTA配置
  主要涉及URXEN和UTXEN两位,其中URXEN = 1是UART接收使能,UTXEN = 1是UART发送使能。我们收发都要,所以都开,U2STA配置:
  1. U2STA = 0x1400;
复制代码
  (5)发送和接收
  UxTXREG是发送寄存器,UxRXREG是接收寄存器。
  写数据到UxTXREG寄存器将通过UARTx的TX引脚发送出去;读UxRXREG寄存器接收来自UARTx的RX引脚的数据。
  真开心!就这点需要配置的,整合一下上面的代码了!
  1. U2BRG = 53;
  2. U2MODE = 0x8000;         // Enable UART2 for 8-bit data, no parity, 1 stop bit
  3. U2STA = 0x1400;                 // Enable UART2 receive & transmit
复制代码
  这就是我两年前写的代码,就这么直白,但是真心简洁利索!
  我们尝试发送两个字符:OK,于是再加两句:
  1. while (U2STAbits.UTXBF);
  2. U2TXREG = ‘O’;
  3. while (U2STAbits.UTXBF);
  4. U2TXREG = ‘K’;
复制代码

  上面加的while (U2STAbits.UTXBF)是判断可不可以往发送寄存器中放数据。UTXBF是发送缓冲区满状态位,发送区慢的时候它为1,该位只读。


  我们编译一下程序,哦!有童鞋说你的程序是啥?那我就把它写完整,就是上一节Hello,world!的程序,核心代码修改为上面这些,如果不记得了,看这里:http://microchip.eefocus.com/module/forum/thread-57129-1-2.html

  使用USB转串口线,连接上J14上的引脚上,打开串口调试助手,你会怀着惊喜的心情发现串口调试助手上什么都没有!对!什么都没有……如图3所示。

UART板子连线及调试

UART板子连线及调试

图3、Curiosity PIC32 MZ EF与电脑通过UART线连接及调试


  不过不必惊慌,这里我挖下这么一个坑是想说明一个问题,PIC单片机的引脚重映射功能,这在很多书上都没有写,包括官方的应用笔记上也没有明确说明,所以很多对PIC单片机不太熟悉的用户很疑惑,总以为自己是不是代码配置错了。PIC单片机从PIC24开始就不再把某一个引脚固定给某一个外设用了,而是采用动态分配,在初始化的时候由用户来分配。例如UART2的TX和RX引脚,并没有真实连接到某一个引脚上,我们要使用TX和RX的话,就需要手工把他们映射到某两个引脚上,然后才能用。这么一说大家就明白了,那我们赶紧翻Datasheet,看看引脚重映射。在官方Datasheet中文版的“12.4 外设引脚选择(PPS)”节有详细介绍,具体我就不说了,大家可以自己看。我摘抄几句重要的结论:
  1)PPS管理的外设都是仅数字外设,由用户选择连接到哪些引脚上。
  2)某些数字外设、所有的模拟外设均不能重映射,也就是说引脚固定好了。
  3)可重映射的外设必须始终将外设分配给特定的I/O引脚,然后才能使用外设。
  4)可重映射外设的优先级永远不会高于与该引脚相关的任何模拟功能。
  5)作为输入引脚重映射时,PPS功能不会优先于TRISx设置。因此,配置RPn引脚为输入引脚时,TRISx 寄存器中的对应位也必须配置为输入(设置为1)。
  6)可重映射的引脚在Datasheet设备引脚介绍中会标上RPn。可重映射不是随意映射,也是有规矩的,具体在Datasheet中会给出

  总结以上6点,就是PIC32单片机上的可重映射外设必须进行引脚重映射才能用。映射规则见Datasheet。输入引脚映射要把TRIS寄存器设置为输入。有模拟功能的引脚必须配置为数字引脚,才可以进行映射。
经查,UART2是可重映射外设,所以:
  1)必须先把UART2的TX和RX映射到引脚上,才能用。
  2)对于RX引脚,要在TRISA中把方向设置为输入。
  3)引脚如果有模拟功能,先关闭与模拟外设的连接。
  PIC32MZ单片机UART2端口重映射关系见图4(摘自Datasheet的表12-2)

引脚重映射关系.png

图4、引脚重映射关系

  于是就有了下面这几行代码:
  1. //1. 配置RPn引脚为输入引脚时,TRISx寄存器中的对应位必须配置为输入
  2. TRISCbits.TRISC3 = 1;
  3. //2. 可重映射外设的优先级永远不会高于与该引脚相关的任何模拟功能
  4. //   所以我们需要将该引脚配置为数字功能
  5. ANSELCbits.ANSC3 = 0;
  6. //3. 引脚重映射
  7. U2RXRbits.U2RXR = 0b01100;  // UART2 Rx引脚映射到RPC3引脚
  8. RPC2Rbits.RPC2R = 2;        // UART2 Tx引脚映射到RPC2引脚
复制代码
然后,我们进行重新测试!我们惊喜地发现,收到了OK两个字符。如图5所示,这TMD才是惊喜,真是热泪盈眶~
串口调试助手收到OK.png
图5、在串口调试助手上收到OK两个字符

IMG_3374.JPG

  已经可以发送数据了,我们还想接收数据!好,我们在哪儿接收呢?为了减轻负担,我们先不着急在中断中接收,我们就在while(1)超级循环中接收。代码如下:
  1. while(1)
  2. {
  3. while(!U2STAbits.URXDA);                //  如果接收缓冲区为空,则一直等
  4.     U2TXREG = U2RXREG;                        //  接收到数据,把它原封不动发送出来
  5. }
复制代码
  赶紧测试,惊喜的发现,成功了! 如图6所示。

发送和接收数据

发送和接收数据

图6、发送和接收数据测试

  以上实验说明我们的UART2收发没有正常了。我们的完整实验代码如下:
  1. /*
  2. * File:   main.c
  3. * Author: jiushu
  4. * Email: jiushu@tsinghua.info
  5. *
  6. * Created on 2017年10月17日, 上午0:22
  7. */

  8. // PIC32MZ2048EFM100 Configuration Bit Settings ////////////////////////////////
  9. // DEVCFG3
  10. // USERID = No Setting
  11. #pragma config FMIIEN = ON              // Ethernet RMII/MII Enable (MII Enabled)
  12. #pragma config FETHIO = ON              // Ethernet I/O Pin Select (Default Ethernet I/O)
  13. #pragma config PGL1WAY = ON             // Permission Group Lock One Way Configuration (Allow only one reconfiguration)
  14. #pragma config PMDL1WAY = ON            // Peripheral Module Disable Configuration (Allow only one reconfiguration)
  15. #pragma config IOL1WAY = OFF            // Peripheral Pin Select Configuration (Allow multiple reconfigurations)
  16. #pragma config FUSBIDIO = ON            // USB USBID Selection (Controlled by the USB Module)

  17. // DEVCFG2
  18. // 这里将PLL的时钟源配置为外部振荡器,24MHz外部时钟经过PLL后时钟输出200MHz
  19. // PLL的输出时钟=FPLLICLK÷FPLLIDIV×FPLLMULT÷FPLLODIV
  20. // FPLLRNG的范围需要与PLL的输入匹配
  21. #pragma config FPLLIDIV = DIV_3         // System PLL Input Divider (3x Divider)
  22. #pragma config FPLLRNG = RANGE_5_10_MHZ // System PLL Input Range (5-10 MHz Input)
  23. #pragma config FPLLICLK = PLL_POSC      // System PLL Input Clock Selection (POSC is input to the System PLL)
  24. #pragma config FPLLMULT = MUL_50        // System PLL Multiplier (PLL Multiply by 50)
  25. #pragma config FPLLODIV = DIV_2         // System PLL Output Clock Divider (2x Divider)
  26. #pragma config UPLLFSEL = FREQ_24MHZ    // USB PLL Input Frequency Selection (USB PLL input is 24 MHz)

  27. // DEVCFG1
  28. // 选择系统时钟为PLL(200MHz)
  29. // 关闭了辅助时钟(就是那个X3的低速时钟,板子上没有焊)
  30. // 关闭了看门狗,调试阶段不需要这东西
  31. #pragma config FNOSC = SPLL             // Oscillator Selection Bits (System PLL)
  32. #pragma config DMTINTV = WIN_127_128    // DMT Count Window Interval (Window/Interval value is 127/128 counter value)
  33. #pragma config FSOSCEN = OFF            // Secondary Oscillator Enable (Disable SOSC)
  34. #pragma config IESO = OFF               // Internal/External Switch Over (Disabled)
  35. #pragma config POSCMOD = EC             // Primary Oscillator Configuration (External clock mode)
  36. #pragma config OSCIOFNC = OFF           // CLKO Output Signal Active on the OSCO Pin (Disabled)
  37. #pragma config FCKSM = CSDCMD           // Clock Switching and Monitor Selection (Clock Switch Disabled, FSCM Disabled)
  38. #pragma config WDTPS = PS1048576        // Watchdog Timer Postscaler (1:1048576)
  39. #pragma config WDTSPGM = STOP           // Watchdog Timer Stop During Flash Programming (WDT stops during Flash programming)
  40. #pragma config WINDIS = NORMAL          // Watchdog Timer Window Mode (Watchdog Timer is in non-Window mode)
  41. #pragma config FWDTEN = OFF             // Watchdog Timer Enable (WDT Disabled)
  42. #pragma config FWDTWINSZ = WINSZ_25     // Watchdog Timer Window Size (Window size is 25%)
  43. #pragma config DMTCNT = DMT31           // Deadman Timer Count Selection (2^31 (2147483648))
  44. #pragma config FDMTEN = OFF             // Deadman Timer Enable (Deadman Timer is disabled)

  45. // DEVCFG0
  46. // 关闭了JTAG调试功能,我们没用JTAG
  47. // 使用ICS_PGx2作为调试接口(上一节我们已经讨论过)
  48. #pragma config DEBUG = OFF              // Background Debugger Enable (Debugger is disabled)
  49. #pragma config JTAGEN = OFF             // JTAG Enable (JTAG Disabled)
  50. #pragma config ICESEL = ICS_PGx2        // ICE/ICD Comm Channel Select (Communicate on PGEC2/PGED2)
  51. #pragma config TRCEN = ON               // Trace Enable (Trace features in the CPU are enabled)
  52. #pragma config BOOTISA = MIPS32         // Boot ISA Selection (Boot code and Exception code is MIPS32)
  53. #pragma config FECCCON = OFF_UNLOCKED   // Dynamic Flash ECC Configuration (ECC and Dynamic ECC are disabled (ECCCON bits are writable))
  54. #pragma config FSLEEP = OFF             // Flash Sleep Mode (Flash is powered down when the device is in Sleep mode)
  55. #pragma config DBGPER = PG_ALL          // Debug Mode CPU Access Permission (Allow CPU access to all permission regions)
  56. #pragma config SMCLR = MCLR_NORM        // Soft Master Clear Enable bit (MCLR pin generates a normal system Reset)
  57. #pragma config SOSCGAIN = GAIN_2X       // Secondary Oscillator Gain Control bits (2x gain setting)
  58. #pragma config SOSCBOOST = ON           // Secondary Oscillator Boost Kick Start Enable bit (Boost the kick start of the oscillator)
  59. #pragma config POSCGAIN = GAIN_2X       // Primary Oscillator Gain Control bits (2x gain setting)
  60. #pragma config POSCBOOST = ON           // Primary Oscillator Boost Kick Start Enable bit (Boost the kick start of the oscillator)
  61. #pragma config EJTAGBEN = NORMAL        // EJTAG Boot (Normal EJTAG functionality)

  62. // DEVCP0
  63. #pragma config CP = OFF                 // Code Protect (Protection Disabled)

  64. // 注意:编程配置位语句应该在项目文件include之前

  65. #include <xc.h>
  66. // 这是PIC32MZ2048EFM100单片机的头文件,位于xc32安装目录\v1.44\pic32mx\include\proc
  67. // 注意:如果在新建工程的时候选择了单片机型号,下面这句可以省略,编译器会自动选择
  68. // #include <proc/p32mz2048efm100.h>

  69. /*
  70. * 入口函数
  71. */
  72. void main(void)   
  73. {
  74.         // 1. 配置UART2的引脚重映射 //////////////////////////////////////////////////
  75.         // UART2_TX <--> RPC2
  76.         // UART2_RX <--> RPC3
  77.         // 1.1. 配置RPn引脚为输入引脚时,TRISx寄存器中的对应位必须配置为输入
  78.         TRISCbits.TRISC3 = 1;
  79.         // 1.2. 可重映射外设的优先级永远不会高于与该引脚相关的任何模拟功能
  80.         //   所以我们需要将该引脚配置为数字功能
  81.     ANSELCbits.ANSC3 = 0;
  82.         // 1.3. 引脚重映射
  83.     U2RXRbits.U2RXR = 0b01100;  // UART2 Rx引脚映射到RPC3引脚
  84.     RPC2Rbits.RPC2R = 2;        // UART2 Tx引脚映射到RPC2引脚
  85.         
  86.         // 2. UART2配置 /////////////////////////////////////////////////////////////
  87.     U2BRG = 53;
  88.         U2MODE = 0x8000;         // 使能UART2:8-bit data, no parity, 1 stop bit
  89.     U2STA = 0x1400;         // 使能UART2收发功能
  90.         
  91.         // 3. 超级循环
  92.         while(1)
  93.         {        
  94.             while(!U2STAbits.URXDA);
  95.         U2TXREG = U2RXREG;
  96.         } // 超级循环结束
  97. }// main函数结束
复制代码

二、关于PIC32MZ串口及XC32的一些事情


1、关于printf的使用
  printf作为串口调试利器,实在是个宝贝,Microchip的工程师良苦用心,在XC32中默认把printf映射到了UART2上,所以用户可以非常方便的使用printf来输出格式化字符串。在上面我们的例子中,增加下面面的代码:
  1. apple = 5;
  2. price = 3.2;
  3. printf("I have %d apples! %.2f yuan for each.\r\n", apple,price);
复制代码

  注意:要使用printf函数需要包含stdio头文件,即#include <stdio.h>
  测试printf输出如图7所示:
4.png

图7、测试printf


2、外设引脚重映射
  上面已经说了PIC32单片机外设引脚重映射功能,这个值得大家注意,这真是一个好东西,绝对让你如获珍宝。单片机里面有那么多外设,而引脚就这么些(即便是144引脚也不够把PIC32MZ中的所有外设全部引出),把哪个外设连到哪个引脚都不能实现独享,这必然带来引脚复用。一复用这就麻烦了,经常造成你想用的两个功能在一个引脚上复用。。。只能舍弃其一,这显然是不明智的。所以PIC单片机的引脚重映射解决了这个问题:外设与引脚的关系不是一一对应,而是用户可以灵活定义的,这就很好地解决了外设引脚冲突问题。
  用户可能发现像STM32等基于ARM内核的单片机也有外设引脚重映射功能,不过相对于PIC单片机这相对还是晚的,早在PIC24开始就已经有引脚重映射了,而且PIC单片机的引脚重映射更开放灵活(一对多模式,大大降低冲突)。以前使用PIC24FJ128GA702的时候,物尽所用,我把引脚几乎用光了,非常合理的按照我的需求把引脚重新映射到合适的位置,既避免了冲突,又避免了浪费。
  这里重点推荐这个精巧的功能,在复杂应用中很值得玩味~
  另外很多新手由于忘了引脚映射及其规矩,会带来很多麻烦,比如本文中提到的外设无反应情况。新手如果遇到类似问题,想想引脚映射的规矩(规矩已总结,请在本文中找)。

3、笨拙的接收数据
  本文使用“死等方式”在while(1)大循环里面等“接收缓冲区中是否有数据标志位”,以确认是否有数据发过来,这是一种很笨的方法,几乎没有实用性。最佳的解决办法是使用分身术——中断接收,但是本文已经很长了,我就打算放在下一节介绍UART中断。同时下一节我把这个长长的单文件main.c进行一些代码规范化处理,提高代码可读性和复用性。




回复

使用道具 举报

 楼主| 发表于 2017-10-20 02:37:43 | 显示全部楼层
占楼备用
回复

使用道具 举报

 楼主| 发表于 2017-10-20 02:39:26 | 显示全部楼层
占楼备用
回复

使用道具 举报

发表于 2017-10-20 09:41:06 | 显示全部楼层

占楼备用
回复

使用道具 举报

发表于 2017-10-20 09:44:23 | 显示全部楼层
  九叔的帖子真是超级详细, 简直可以当教材用啦
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-10-20 12:08:54 | 显示全部楼层
糖水荷包蛋 发表于 2017-10-20 09:44
九叔的帖子真是超级详细, 简直可以当教材用啦

蛋总,社区要有意编书么。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-10-20 12:10:17 | 显示全部楼层
回复 支持 反对

使用道具 举报

发表于 2017-12-15 14:42:42 | 显示全部楼层
风格很风趣   用心了            
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /5 下一条

facebook google plus twitter linkedin youku weibo rss
©2019 Microchip Corporation

小黑屋|手机版|Archiver|Microchip技术社区

GMT+8, 2019-9-20 00:45 , Processed in 0.111189 second(s), 8 queries , MemCache On.

快速回复 返回顶部 返回列表