
前言
串口是一个非常重要的工具,用这个可以非常简单的收发一些数据,串口的操作还是比其他协议简单很多,因此在工控芯片上串口非常普遍。本节就详细记录dsPIC33E芯片的串口寄存器配置以及使用过程。
首先需要把串口的硬件环境搭建好,我这里使用了一对蓝牙主从机,蓝牙主机连接USB转TTL模块连接电脑,蓝牙从机连接单片机芯片,蓝牙的无线传输距离还是比较短的,有效距离只有10几米,不过无所谓了,重点并不是距离,只需要它能够正常收发即可,连接图如下:

我将蓝牙主从机包括密码,名字,波特率等配置好后,测试他们两个之间的数据交互是没问题的,也就是确保了蓝牙是正常的。之后便可以开始UART的配置过程。
1.第一步:配置引脚复用
第一步配置肯定就是引脚配置了,需要把指定的引脚配置为UART模式,包括TX,RX引脚,查看手册中的IO端口章节:

这句话也就是说,如果该端口由AD/DA功能,那么如果要使用这个端口的数字功能,就要将ANSELx寄存器相应的位置0。

也就是说,如果使用UART功能,相关引脚不能使用PORT和TRIS寄存器来进行读写操作。这个关系不大,应该不会有这样的操作。
输入映射与输出映射:
需要知道可用的引脚以及引脚对应的外设:

这里就需要我们在查看芯片手册中,我们具体的芯片型号对应的电路中需要的引脚是什么编号,也就是说查找RPn。


由上可知,TX是RP40,RX是RP41。
如果使用RX接收引脚,需要配置输入映射:(RPINRx寄存器)


如果使用TX发送引脚,需要配置输出映射:(RPORx寄存器)

配置TX:
配置引脚为TX首先需要禁用引脚的模拟模式,将引脚配置为IO,之后配置为输出:
ANSELBbits.ANSB8 = 0; //关闭模拟模式,设置B9为数字IO
TRISBbits.TRISB8 = 0; //设置B8为输出
之后,便将此引脚配置为UART1_TX功能:

也即,需要将RPOR寄存器的RP40位中写入二进制的000001,也就是0b000001:
RPOR3bits.RP40R = 0b000001; //选择为TX模式
配置RX:

也就是说要把RPINR18寄存器的U1RXR位接入需要接入的引脚,电路上是PB9,也就是PR41,就配置位了RX模式,在此之前要把此引脚设置为输入。

2.第二步:配置波特率

波特率就很好配置了,按照这个图,配置相应的BRGH位,然后根据BRGH位计算,这里我使用的BRGH就为0,由于我配置的时钟频率为60M,(关于时钟频率配置,请看此dsPIC系列的振荡器相关文章)按照这个公式计算即可,波特率宏定义:
#define FCY 60000000 //工作频率
#define BAUDRATE 9600 //设定波特率
#define BRGVAL ((FCY/BAUDRATE)/16)-1
3.第三步:发送功能程序
完成TX配置之后,就需要编写发送函数,从主要的还是发送一个字节函数,只要可以发送一个字节,那么发送字符串函数什么的,就可以迎刃而解。发送功能函数的最主要的就是数据缓冲区以及发送完成标志:

则,发送函数编写就可以这样:
void uart_SendByte (uint8_t val)
{
U1TXREG = val; //将数据写入发送缓冲区
/@@*等待发送完成*/
while(!U1STAbits.TRMT); //等待发送移位寄存器为空 即上次发送已完成
}
完整TX配置以及发送函数如下:
/@@****************发送配置***************/
ANSELBbits.ANSB8 = 0; //关闭模拟模式,设置B9为数字IO
TRISBbits.TRISB8 = 0; //设置B8为输出
RPOR3bits.RP40R = 0b000001; //选择为TX模式
U1MODEbits.STSEL = 0;//一个停止位
U1MODEbits.PDSEL = 0;//8数据位 无奇偶校验
U1MODEbits.ABAUD = 0;//禁止波特率测量或测量已完成
U1MODEbits.BRGH = 0;//BRG 在每个位周期内产生 16 个时钟信号 ( 16 倍频波特率时钟,标准模式)
U1BRG = BRGVAL; //60MHz波特率9600(按照计算公式)
U1STAbits.UTXISEL0 = 0;
U1STAbits.URXISEL1 = 0;//当接收到一个字符且该字符从 UxRSR 传输给接收缓冲区,
//使接收缓冲区有一个或多个字符时,中断标志位置 1。
IEC0bits.U1TXIE = 0; //不使能发送中断
U1MODEbits.UARTEN = 1;//使能UART
U1STAbits.UTXEN = 1; //使能发送
void uart_SendByte (uint8_t val)
{
U1TXREG = val; //将数据写入发送缓冲区
/@@*等待发送完成*/
while(!U1STAbits.TRMT); //等待发送移位寄存器为空 即上次发送已完成
}
我使用了此函数在main函数中一直发送 ff,测试效果如图:

4.第四步:接收功能程序
接收部分就使用中断会更好一些,因此,需要配置RX的中断:

配置代码:
TRISBbits.TRISB9 = 1; //设置B9为输入
RPINR18bits.U1RXR = 41; //设置B9为RX模式
U1MODEbits.STSEL = 0;//一个停止位
U1MODEbits.PDSEL = 0; // 8数据位 无奇偶校验
U1MODEbits.ABAUD = 0; // 禁止波特率测量或测量已完成
U1MODEbits.BRGH = 0; // BRG 在每个位周期内产生 16 个时钟信号 ( 16 倍频波特率时钟,标准模式)
U1BRG = BRGVAL;//80MHz波特率9600(按照计算公式)
U1STAbits.URXISEL = 0b00;//当接收到一个字符且该字符从 UxRSR 传输给接收缓冲区,
//使接收缓冲区有一个或多个字符时,中断标志位置 1。
U1STAbits.ADDEN = 1;
IEC0bits.U1RXIE = 1;//使能接收中断
IPC2bits.U1RXIP = 0b100; //接收中断优先级设置为4
U1MODEbits.UARTEN = 1;//使能UART
void __attribute__((__interrupt__, auto_psv)) _U1RXInterrupt(void)//接收中断
{
uint8_t data = 0;
IFS0bits.U1RXIF = 0;
data = U1RXREG;
if(data == 0xff)
{
PORTBbits.RB4 = 0; //点亮LED
}
else
{
PORTBbits.RB4 = 1; //熄灭LED
}
}
上面接收部分的功能就是电脑发送 ff ,则点亮LED1,发送其他数据则熄灭LED1,效果如下:
如上所示:当发送 ff 时,丝印编号为D1的LED点亮,发送其他时,D1熄灭(可能由于GIF转换的原因有些模糊)。

当然,也可以做一个更有趣的实验,既然用电脑的串口软件可以控制了,那么用手机蓝牙自然也是可以控制的,因为手机自带蓝牙,因此就不需要哪个USB转TTL以及蓝牙主机了,之间下载一个蓝牙app即可,我下载了一个蓝牙串口app,可以自定义按键发送的内容,那么就可以近距离无线控制我的板子上的led灯了,效果:
