stm8 串口中断接收数据 (完全中断执行)
最近写modbus ,接收的时候各种问题,放在主函数,执行子函数的时候收不到数据,不放在主函数,似乎是一个不错的想法。
第一步:串口初始化
哪位大神指导,复制过来的中文注释都是乱码的问题,感激不尽
void UART2_Config(void)
{
UART2_DeInit(); /* ½«¼Ä´æÆ÷µÄÖµ¸´Î» */
// 初始化 UART2
// 参数说明:
// - 9600: 波特率设置为9600 bps
// - UART2_WORDLENGTH_8D: 数据位长度设置为8位
// - UART2_STOPBITS_1: 停止位设置为1位
// - UART2_PARITY_NO: 不使用奇偶校验
// - UART2_SYNCMODE_CLOCK_DISABLE: 禁用同步模式时钟
// - UART2_MODE_TXRX_ENABLE: 使能发送和接收模式
UART2_Init((u32)9600, UART2_WORDLENGTH_8D, UART2_STOPBITS_1, UART2_PARITY_NO, UART2_SYNCMODE_CLOCK_DISABLE, UART2_MODE_TXRX_ENABLE);
// 配置 UART2 接收中断
// UART2_IT_RXNE_OR: 使能接收数据寄存器非空 (RXNE) 或接收超时 (OR) 中断
// ENABLE: 使能该中断
UART2_ITConfig(UART2_IT_RXNE_OR, ENABLE);
// 使能 UART2 外设
// 这一步将 UART2 外设的功能打开,使其开始工作
UART2_Cmd(ENABLE);
}
第二步:初始化 定时器
这里可能有人好奇为啥要定时器,我是用来判断超时,防止错位的,可能有更好的办法。
// 定义全局变量,用于存储计时器的分钟、秒和毫秒
int minutes = 0; // 存储分钟数
int seconds = 0; // 存储秒数
int milliseconds = 0; // 存储毫秒数
// 初始化 TIM3 定时器
void Tim3_Init(void)
{
// 禁用 TIM3 定时器
TIM3_Cmd(DISABLE);
// 将 TIM3 定时器重置为默认状态
TIM3_DeInit();
// 配置 TIM3 的时间基准
// 参数说明:
// - TIM3_PRESCALER_16: 预分频器设置为 16,意味着定时器计数频率为系统时钟频率 / 16
// - 999: 定时器计数器的自动重装载值设置为 999,意味着计数到 999 时会产生一个更新事件
// 这样配置后,TIM3 每当计数到 1000(999 + 1)个时钟周期时,更新事件会被触发
TIM3_TimeBaseInit(TIM3_PRESCALER_16, 999);
// 启动 TIM3 定时器
TIM3_Cmd(ENABLE);
// 使能 TIM3 的更新中断
// TIM3_IT_UPDATE: 指定要使能的中断类型,这里是更新中断
// ENABLE: 使能更新中断,使得每次定时器计数到 1000 时会触发中断
TIM3_ITConfig(TIM3_IT_UPDATE, ENABLE);
// 初始化计时器的分钟、秒和毫秒变量
minutes = 0; // 初始化分钟数为 0
seconds = 0; // 初始化秒数为 0
milliseconds = 0; // 初始化毫秒数为 0
}
-
全局变量定义:
int minutes = 0;
: 用于存储计时器的分钟数,初始值为 0。int seconds = 0;
: 用于存储计时器的秒数,初始值为 0。int milliseconds = 0;
: 用于存储计时器的毫秒数,初始值为 0。-
Tim3_Init
函数: - 该函数用于初始化 TIM3 定时器并配置其相关参数。
-
TIM3_Cmd(DISABLE);
: - 禁用 TIM3 定时器,确保在进行初始化时定时器处于关闭状态。
-
TIM3_DeInit();
: - 将 TIM3 定时器重置为默认状态,清除之前的配置和状态,以确保新的配置不会受到旧设置的影响。
-
TIM3_TimeBaseInit(TIM3_PRESCALER_16, 999);
: - 配置 TIM3 的时间基准。
TIM3_PRESCALER_16
: 设置定时器的预分频器为 16,这表示定时器计数频率为系统时钟频率除以 16。999
: 设置自动重装载值为 999,意味着定时器计数到 999 后会产生一个更新事件。这样配置后,TIM3 每 1000 个计数周期(即每 1000 / (系统时钟频率 / 16) 秒)将触发一次更新事件。-
TIM3_Cmd(ENABLE);
: - 启动 TIM3 定时器,使其开始计数。
-
TIM3_ITConfig(TIM3_IT_UPDATE, ENABLE);
: - 使能 TIM3 的更新中断。
TIM3_IT_UPDATE
: 指定要使能的中断类型,这里是更新中断。ENABLE
: 使能更新中断,使得每次定时器计数到 1000 时会触发中断,从而可以在中断服务例程中处理计时逻辑。-
初始化计时器变量:
minutes = 0;
: 将分钟数初始化为 0。seconds = 0;
: 将秒数初始化为 0。milliseconds = 0;
: 将毫秒数初始化为 0。
time3 中断内容
// 定义定时器3的更新、溢出和断点中断处理程序
INTERRUPT_HANDLER(TIM3_UPD_OVF_BRK_IRQHandler, 15)
{
/* 在开发过程中,为了检测意外事件,建议在以下指令上设置一个断点。
这可以帮助开发者在调试时观察中断处理的执行情况。
*/
milliseconds += 1; // 每次中断触发时,将毫秒计数增加10毫秒
TIM3->SR1 &= ~TIM3_SR1_UIF; // 清除更新中断标志位,表示已经处理了这次中断
// TIM3_ClearITPendingBit(TIM3_IT_UPDATE); // 这行代码被注释掉了,功能与上行相同
// 检查累计的毫秒数是否达到1000毫秒(即1秒)
if (milliseconds >= 1000)
{
milliseconds -= 1000; // 从毫秒计数中减去1000,准备下一秒的计数
seconds++; // 秒数加1
// 检查秒数是否达到60秒
if (seconds >= 60)
{
seconds = 0; // 如果达到60秒,重置秒数为0
minutes++; // 分钟数加1
// 检查分钟数是否超过1000分钟
if (minutes > 1000)
{
minutes = 0; // 如果分钟数超过1000,重置为0
}
}
}
}
时钟计算函数 用来计算距离上次调用,过了多久
// 记录上次时间的变量
unsigned int last_minutes = 0; // 存储上一次的分钟数
unsigned int last_seconds = 0; // 存储上一次的秒数
unsigned int last_milliseconds = 0; // 存储上一次的毫秒数
u32 interval; // 用于存储当前时间与上次时间的间隔
// 函数:计算当前时间与上次记录时间之间的间隔
u32 check_time_interval()
{
// 计算当前时间(以毫秒为单位)
u32 current_time_in_ms = (minutes * 60 * 1000) + (seconds * 1000) + milliseconds;
// 计算上次记录的时间(以毫秒为单位)
u32 last_time_in_ms = (last_minutes * 60 * 1000) + (last_seconds * 1000) + last_milliseconds;
// 计算当前时间与上次时间的间隔(单位:毫秒)
interval = current_time_in_ms - last_time_in_ms;
// 更新上次记录的时间为当前时间
last_minutes = minutes; // 更新上次分钟数
last_seconds = seconds; // 更新上次秒数
last_milliseconds = milliseconds; // 更新上次毫秒数
// 返回当前时间与上次时间的间隔
return interval;
}
第三步: 设置中断内容
我这里是如果接收到的内容,正好是从机编号,则送入解析内容。不过貌似解析时间不能太久,后面容易丢包。我的处理办法是,如果解析时间太长,就先做标记,等数据接收完成后。再去处理函数。
// UART2 接收中断处理程序
INTERRUPT_HANDLER(UART2_RX_IRQHandler, 21)
{
// 清除 UART2 接收中断挂起位
// 这里清除的是 RXNE 中断,而不是 RXNE_OR 中断
UART2_ClearITPendingBit(UART2_IT_RXNE);
// 检查时间间隔是否大于 10 毫秒
if (check_time_interval() > 10)
{
read_idx = 0; // 重置读取索引
(void) UART2->SR; // 读取 UART 状态寄存器以清除潜在的错误标志
(void) UART2->DR; // 读取 UART 数据寄存器以清除潜在的错误标志
}
// 从 UART2 接收数据并存入缓冲区
read_buffer[read_idx++] = UART2_ReceiveData8();
// 检查是否达到了预设的读取长度
if (read_idx >= read_len) // 如果读取的字节数大于等于预设的长度
{
UART2_ITConfig(UART2_IT_RXNE_OR, DISABLE); // 禁用接收中断
read_ok = 1; // 标记读取完成
read_idx = 0; // 重置读取索引
(void) UART2->SR; // 读取 UART 状态寄存器以清除潜在的错误标志
(void) UART2->DR; // 读取 UART 数据寄存器以清除潜在的错误标志
}
// 如果读取完成标志为 1
if (read_ok == 1)
{
// 如果接收到的数据的第一个字节是 SlaveID
if (read_buffer[0] == SlaveID)
{
DisposeReceive(); // 处理接收到的数据
}
// 重新使能 UART2 接收中断
UART2_ITConfig(UART2_IT_RXNE_OR, ENABLE);
read_ok = 0; // 重置读取完成标志
}
}
作者:sunwei24680