中颖51芯片学习6. EUART接口通讯
一、资源介绍
SH79F9476 有3个自带波特率发生器的EUART0/1/2(波特率发生器是一个15位向上计数器)。
SH79F9476的EUART有四种工作方式,3个EUART功能基本一样。下面主要介绍 EUART0。
二、工作方式介绍
1. 四种通讯方式
(1)方式0:同步通信
波特率由系统时钟频率决定,帧长度为8位,无起始位、停止位和第9位。在这种模式下,通信开始时等待RIx标志位(接收中断标志)为0并且RENx标志位(接收使能标志)为1,然后发送一个时钟信号到TXDx引脚,并在RXDx引脚上接收8位数据。
(2)方式1:异步通信
波特率由EUART自带的波特率发生器控制,波特率为溢出率除以16,帧长度为10位(1个起始位、8位数据、1个停止位),没有第9位。在这种模式下,通信开始时从输入的起始位初始化接收,并且都设置为1。
(3)方式2:异步通信
,波特率由系统时钟频率决定,帧长度为11位(1个起始位、8位数据、1个停止位、1个第9位),第9位可设置为0或1。波特率由系统时钟频率除以32或64决定。
(4)方式3:异步通信
波特率由EUART自带的波特率发生器控制,波特率为溢出率除以16,帧长度为11位(1个起始位、8位数据、1个停止位、1个第9位),第9位可设置为0或1。在这种模式下,通信开始时从输入的起始位初始化接收,并且都设置为1。
通过配置SCON寄存器的SM0、SM1用来配置运行方式,如下图所示:
SM0 | SM1 | 方式 | 类型 | 波特率 | 帧长度 | 起始位 | 停止位 | 第 9 位 |
---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 同步 | fSYS/(4 或 12) | 8 位 | 无 | 无 | 无 |
0 | 1 | 1 | 异步 | 自带波特率发生器的溢出率/16 | 10 位 | 1 | 1 | 无 |
1 | 0 | 2 | 异步 | fSYS/(32 或 64) | 11 位 | 1 | 1 | 0,1 |
1 | 1 | 3 | 异步 | 自带波特率发生器的溢出率/16 | 11 位 | 1 | 1 | 0,1 |
2. 波特率计算公式
(1)方式0
波特率可编程为系统时钟的1/12或1/4,由SMx2位决定。
- 当SMx2为0时,串行端口在系统时钟的1/12下运行。
- 当SMx2为1时,串行端口在系统时钟的1/4下运行。
(2)方式1和方式3中,波特率可微调
精度为一个系统时钟,公式 如下:
B
a
u
d
R
a
t
e
=
F
s
y
s
16
∗
(
32768
−
S
B
R
T
)
+
S
F
I
N
E
BaudRate = \frac{F_{sys}}{16*(32768-SBRT)+SFINE}
BaudRate=16∗(32768−SBRT)+SFINEFsys
以系统时钟24M为例,要得到9600Hz波特率:
(int)X=FSY/(16波特率) ;
SBRT=32768-X ;
SFINE=(FSY/波特率)-16X
FSY=8M
为便于参数计算,在开源仓库的docs文件件放了波特率计算的Excel公式 。
使用时填写波特率、系统频率,自动计算的 合并一栏,即要给 SBRTH、SBRTL赋的值,SFINE在下面对应单元格。
波特率9600计算结果示例:
即
SBRTH=0xff;
SBRTL=0x64;
SFINE=0x04;
(3)方式2
波特率固定为系统时钟的1/32或1/64,由SMOD位(PCON.7)决定 。
三、寄存器
1. EUART0控制及状态寄存器
2. EUART0 数据缓冲寄存器
3. 波特率发生器寄存器
4. 波特率发生器微调寄存器
开启中断
EUART0的中断允许位在IEN0, 位置如图所示:
通过下面代码 开启EUART0的中断:
IEN0 |= 0x10;
四、实验代码
1. 硬件连接
- TX:引脚 P3.3
- RX:引脚 P3.4
通过USB转串口工具,将串口连接到电脑,并打开串口助手进行调试。
2. 简单的收发代码
下面代码实现的功能是:
- mcu启动时,向串口发送字符0x32。
- 串口收到字符时,将数值+1后,通过串口再发出去。
要注意的是,代码选项:“内部 128kHz RC 振荡器作为振荡器 1,24MHz 内部 RC 作为振荡器 2”,如下图所示:
(1)euart_utils.h
#include "intrins.h"
#include "euart_utils.h"
#include "api_ext.h"
#include "SH79F9476.h"
#include "cpu.h"
/**
* @brief 初始化串口
*/
void uart0_init(){
//=====TX 建议配置为输出H====
P3CR = 0x08;
P3 = 0x08;
// 配置Uart工作在模式1
select_bank1();
// 0110 0111 Tx:P3.3 Rx:P3.4
UART0CR = 0x67;
select_bank0();
SCON=0x50;
/*配置波特率参数,波特率9600*/
/* 计算公式:(int)X=FSY/(16*波特率) ; SBRT=32768-X ; SFINE=(FSY/波特率)-16*X FSY=8M*/
// 波特率发生器高位
SBRTH = 0xFF;
// 波特率发生器低位
SBRTL = 0x64;
// 波特率发生器微调
SFINE = 0x04;
}
/**
* @brief 发送一个字节
*/
void uart0_send_byte(u8 byte){
SBUF = byte;
while(!TI);
TI = 0;
}
/**
* @brief UART0中断
**/
void INT_EUART0(void) interrupt 4{
u8 dat;
_push_(INSCON);
// 收的数值+1 再返回去
while(!RI);
dat = SBUF;
RI = 0;
_pop_(INSCON);
dat ++;
uart0_send_byte(dat);
}
(2)main.c
#include "SH79F9476.h"
#include "cpu.h"
#include "intrins.h"
#include "api_ext.h"
#include "clk_util.h"
#include "euart_utils.h"
#include "irq_util.h"
void main()
{
// 选择高速时钟
highFrequenceClk();
// 串口初始化
uart0_init();
// 开启中断
enableAllIrq();
enableEUart0();
uart0_send_byte(0x32);
while (1);
}
3. 通过发送缓冲区发送数据
上面的发送串口数据的方法,由于在发送的时候要等待 TI 标志位完成,在发送时会造成程序阻塞,影响其它程序的运行。
为了避免这种影响,可以将发送改在中断中来完成,实现方法是:
- 将要发送的数据放到发送缓冲区
- 把发送缓冲区当前指针位置赋值给SBUF,并将指针移到下一个元素
- 发送完成时,触发中断
- 如果未发送完,回到第2步发送
代码示例
#include "intrins.h"
#include "euart_utils.h"
#include "api_ext.h"
#include "SH79F9476.h"
#include "cpu.h"
#include "string.h"
// 发送缓冲区
U8 gUart0DataTxD[UART0_DATA_BUF_SIZE];
// 未发送数据长度
U8 gUart0Len_TxD = 0;
// 内部变量,发送指针
volatile U8 *ptr_tx0;
/**
* @brief 初始化串口
*/
void Uart0_Init() {
//=====TX 建议配置为输出H====
P3CR = 0x08;
P3 = 0x08;
// 配置Uart工作在模式1
select_bank1();
// 0110 0111 Tx:P3.3 Rx:P3.4
UART0CR = 0x67;
select_bank0();
SCON = 0x50;
/*配置波特率参数,波特率9600*/
/* 计算公式:(int)X=FSY/(16*波特率) ; SBRT=32768-X ; SFINE=(FSY/波特率)-16*X FSY=8M*/
// 波特率发生器高位
SBRTH = 0xFF;
// 波特率发生器低位
SBRTL = 0x64;
// 波特率发生器微调
SFINE = 0x04;
// 使能串口中断
IEN0 |= 0x10;
}
/**
* @brief 发送缓冲区数据
*/
static void Uart0_Transmit(void) {
ptr_tx0 = &gUart0DataTxD[0];
SBUF = *ptr_tx0;
if (gUart0Len_TxD > 0)
gUart0Len_TxD--;
ptr_tx0++;
}
/**
* @brief 发送一个字节
*/
void Uart0_Send_Byte(U8 byte) {
Uart0_Send_Bytes(&byte, 1);
}
/**
* @brief 发送数组
* @param bytes
* @param len
*/
void Uart0_Send_Bytes(const U8 *bytes, U8 len) {
memcpy(gUart0DataTxD, bytes, len);
gUart0Len_TxD = len;
Uart0_Transmit();
}
/**
* @brief 发送字符串
* @param str
* @param len
*/
void Uart0_Send_String(const char *str) {
U8 len = strlen(str);
memcpy(gUart0DataTxD, str, len);
gUart0Len_TxD = len;
Uart0_Transmit();
}
/**
* @brief UART0中断
**/
void INT_EUART0(void) interrupt 4{
if(TI){
TI = 0;
if(gUart0Len_TxD >0){
SBUF = *ptr_tx0;
gUart0Len_TxD --;
ptr_tx0 ++;
}
}
}
本文学习资源参考中颖官方文档
本文代码开源地址: https://gitee.com/xundh/learn-sinowealth-51