📄 uart.c
字号:
//8.3 程序清单
//方法一: 使用回归计算的源代码
#include "p30F6014.h" // 标准头文件
#include "math.h" // 数学库
#define Fcy 29491200 // 允许波特率计算用于显示
// 函数原型
void SetupAutoBaud(void); // 该函数用于设置UART1、IC1 和TMR3
void CalculateBaud(void); // 该函数用于计算U1BRG 值
// 变量
unsigned int ICCount = 0; // 对捕捉事件次数进行计数
unsigned int T3Count = 0; // 对Timer 3 中断数进行计数
unsigned int CurrentCapture; // 记录UART 边沿的时间
unsigned int PreviousCapture; // 存储上一次边沿时间测量值
unsigned int CaptureDifference; // UART 边沿时间差值
long SumXY, SumY; // 用于回归计算的立即数
long RegressionData[10]; // 用于线性回归的数据
unsigned long BaudRate; // 计算波特率
// 主程序
// 无限循环检测UART 输入数据0x55 的波特率
// 并每次在波特率计算完后输出一条报文。
int main(void)
{
while(1) // 无限循环
{
SetupAutoBaud(); // 为自动波特率检测设置UART1、IC1 及TMR3
while(U1BRG == 0) {} // 等待自动波特率检测结束
BaudRate = (Fcy / 16) / (U1BRG + 1); // 查看当前使用的波特率
printf("Baud rate: %ld\r", BaudRate);// 输出带有波特率的文本
while(U1STAbits.TRMT == 0) {} // 等待发送结束
}
}
// 设置外设和中断以进行波特率检测
void SetupAutoBaud(void)
{
U1BRG = 0; // U1BRG 初始值未知
U1MODE = 0x8020; // 使能UART 中的自动波特率检测功能
U1STA = 0x0000; // 将UART 的其他功能设定为缺省状态
ICCount = 0; // 对捕捉事件的次数进行初始化
IC1CON = 0x0000; // 复位输入捕捉1 模块
IC1CON = 0x0001; // 使能输入捕捉1 模块
IFS0bits.IC1IF = 0; // 清除捕捉模块1 中断标志
IEC0bits.IC1IE = 1; // 使能捕捉1 中断
T3CON = 0x0000; //Timer 3 关闭
IEC0bits.T3IE = 0; // 清除Timer 3 中断使能
T3Count = 0; // 对Timer 3 的中断数进行初始化
PR3 = 0xffff; //Timer 3 周期为最大值
T3CON = 0x8000; //Timer 3 启用且设置为1:1 预分频比以及内部时钟
}
// 计算U1BRG 波特率发生器的值
void CalculateBaud(void)
{
int i; // 用于进行误差求和的循环变量
long Slope; // 回归计算的斜率(位时间)
long Yintercept; // 第一个边沿的期望的(计算的)时间
long PlotLine; // 每一个边沿的期望的(计算的)时间
long SumOfErrors = 0; // 所有误差的和| 测量值- 期望值|
Slope = (2 * SumXY - 9 * SumY) / 165; // 计算斜率 = 一个位时间
Yintercept = (SumY - Slope * 45) / 10; // 计算第一个边沿的期望时间
PlotLine = Yintercept; // 对每一个边沿的期望时间进行初始化
for(i=0; i<10; i++) // 循环以对所有绝对误差进行累加
{
SumOfErrors += abs(RegressionData[i] - PlotLine);// 计算并加上下一个误差
PlotLine += Slope; // 计算下一个边沿的期望时间
}
if((SumOfErrors * 2) < Slope) // 检查是否平均绝对误差<5%
{ // (10 个误差值累加和的两倍是否< 一个位时间?)
U1BRG = ((Slope + 8) >> 4) - 1; // 计算UxBRG (通过加半位进行舍入)
U1MODE = 0x8000; // 使能UART 并禁止自动波特率检测
U1STA = 0x0400; // 使能发送
}
else
{
SetupAutoBaud(); // 误差太大因此重新开始
}
}
// 输入捕捉1中断服务程序(ISR)
// 获得时间测量值并加到回归计算所需的累加和中
void _ISR _IC1Interrupt(void)
{
IFS0bits.IC1IF = 0; // 清除捕捉1 中断标志
PreviousCapture = CurrentCapture; // 存储前一次时间测量值
CurrentCapture = IC1BUF; // 获取新的时间测量值
T3Count = 0; // 复位超时计数器
if(ICCount == 0) // 检查是否为第一个边沿
{
IFS0bits.T3IF = 0; // 清除Timer 3 中断标志
IEC0bits.T3IE = 1; // 使能Timer 3 中断用于超时检查
RegressionData[0] = 0; // 第一个边沿时间的初始值
SumY = 0; // 时间测量值和的初始值
SumXY = 0; // 位数x 时间和的初始值
}
Else //检查是否非第一个边沿
{
CaptureDifference = CurrentCapture - PreviousCapture; // 获得时间差
RegressionData[ICCount]=RegressionData[ICCount-1] + CaptureDifference;
// 将时间差加到前一次时间测量值中
SumY += RegressionData[ICCount]; // 对时间测量值进行求和
SumXY += RegressionData[ICCount] * ICCount;// 对位数 x 时间测量值进行求和
}
ICCount++; // 对边沿递增计数
if(ICCount == 10) // 检查是否最后一个边沿
{
IEC0bits.IC1IE = 0; // 清除捕捉1 中断使能位
IEC0bits.T3IE = 0; // 清除Timer 3 中断使能位
CalculateBaud(); // 计算U1BRG 值并使能UART
}
}
//Timer 3 ISR
//检查是否超时,即自前一次输入捕捉是否有两次溢出
void _ISR _T3Interrupt(void)
{
IFS0bits.T3IF = 0; // 清除Timer 3 中断标志
T3Count++; // 自上一次捕捉起的中断递增计数
if(T3Count == 2) // 检查自上一次捕捉起是否发生太多定时器溢出
{
SetupAutoBaud(); // 超时因此重新开始
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -