软件中断笔记小结

1. 软件串口的基本原理

什么是串口通信?
  • 串口(UART):异步串行通信协议,数据以固定格式传输:
    • 帧格式:1 个起始位(0)、8 个数据位、1 个结束位(1),共 10 位。
    • 波特率:每秒传输的位数,例如 115200bps 表示每秒 115200 位。
    • 位时长:1 / 波特率,例如 115200bps 的位时长 ≈ 8.68µs。
硬件 UART vs 软件串口
  • 硬件 UART
    • 使用 STM32 的内置 USART 外设,波特率由时钟分频器控制。
    • 优点:高效、可靠。
    • 缺点:波特率固定,无法动态调整单个位的时长。
  • 软件串口
    • 用定时器控制时间,用 GPIO 输出/读取电平。
    • 优点:灵活,可实现波特率拉偏。
    • 缺点:CPU 占用高,依赖定时器精度。
软件串口的关键
  • 发送:定时器触发中断,逐位输出到 GPIO。
  • 接收:外部中断检测起始位,定时器采样数据位。

2. 波特率与时钟的关系

波特率计算
  • 波特率 = 时钟频率 / (每位所需的计数值)。
  • 位时长 = 1 / 波特率。
  • 示例:
    • 目标波特率 = 115200bps。
    • 位时长 = 1 / 115200 ≈ 8.68µs。
时钟配置

假设使用 STM32F103:

  • 系统时钟:72MHz(典型配置)。
  • 定时器时钟
    • 通过预分频器(Prescaler)调整,例如设为 72,则定时器时钟 = 72MHz / 72 = 1MHz(周期 1µs)。
  • 定时器计数
    • 每个计数 = 1 / 1MHz = 1µs。
    • 115200bps 的位时长 8.68µs ≈ 8.68 个计数。
    • 设置定时器重装值(ARR)为 8(计数 0 到 8,共 9 次),周期 ≈ 9µs,波特率 ≈ 111111bps(接近 115200bps)。
调整精度
  • 如果需要更精确:
    • 降低预分频器,例如设为 36,则定时器时钟 = 2MHz(周期 0.5µs)。
    • 位时长 8.68µs ≈ 17.36 个计数,ARR = 16(计数 17 次),周期 8.5µs,波特率 ≈ 117647bps。

3. 软件串口代码示例

硬件假设
  • MCU:STM32F103。
  • TX 引脚:GPIOA Pin 0。
  • RX 引脚:GPIOA Pin 1。
  • 定时器:TIM2(发送),TIM3(接收)。
  • 目标波特率:115200bps

发送代码

#include "stm32f1xx_hal.h"

TIM_HandleTypeDef htim2, htim3;
GPIO_InitTypeDef GPIO_InitStruct = {0};

// 全局变量
uint8_t tx_buffer[] = {0xAA}; // 发送数据:0xAA (10101010)
uint8_t tx_bit_index = 0;
uint8_t rx_buffer = 0;
uint8_t rx_bit_index = 0;

void SystemClock_Config(void); // 默认 72MHz 配置

// 初始化 GPIO 和定时器
void SoftwareUART_Init(void) {
    // GPIO 配置
    __HAL_RCC_GPIOA_CLK_ENABLE();
    GPIO_InitStruct.Pin = GPIO_PIN_0; // TX
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_1; // RX
    GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; // 外部中断检测起始位
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    // TIM2 配置 (发送)
    __HAL_RCC_TIM2_CLK_ENABLE();
    htim2.Instance = TIM2;
    htim2.Init.Prescaler = 71;        // 72MHz / 72 = 1MHz
    htim2.Init.Period = 8;            // 9µs ≈ 111111bps
    htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
    HAL_TIM_Base_Init(&htim2);
    HAL_TIM_Base_Start_IT(&htim2);    // 中断模式

    // TIM3 配置 (接收)
    __HAL_RCC_TIM3_CLK_ENABLE();
    htim3.Instance = TIM3;
    htim3.Init.Prescaler = 71;
    htim3.Init.Period = 4;            // 半个位时长 4.5µs
    htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
    HAL_TIM_Base_Init(&htim3);

    // NVIC 配置
    HAL_NVIC_SetPriority(EXTI1_IRQn, 1, 0);
    HAL_NVIC_EnableIRQ(EXTI1_IRQn);
    HAL_NVIC_SetPriority(TIM2_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(TIM2_IRQn);
    HAL_NVIC_SetPriority(TIM3_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(TIM3_IRQn);
}

接受代码
 

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    if (GPIO_Pin == GPIO_PIN_1) { // 起始位下降沿
        rx_buffer = 0;
        rx_bit_index = 0;
        HAL_TIM_Base_Start_IT(&htim3); // 启动采样
    }
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
    if (htim->Instance == TIM3) { // 接收定时器
        if (rx_bit_index < 9) {   // 采样 9 次(起始 + 8 数据)
            if (rx_bit_index > 0) { // 跳过起始位
                uint8_t bit = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1);
                rx_buffer |= (bit << (rx_bit_index - 1));
            }
            rx_bit_index++;
        } else {
            HAL_TIM_Base_Stop(&htim3); // 接收完成
            printf("Received: 0x%02X\n", rx_buffer);
        }
    }
}
  • 流程
    1. 外部中断检测起始位下降沿。
    2. 定时器以半个位时长(4.5µs)采样,确保落在每位中间。
    3. 读取 8 个数据位,重建字节。
  • 波形解析
    • 输入 0xAA:
      • 采样点:0 (起始), 0, 1, 0, 1, 0, 1, 0, 1。
      • 结果:rx_buffer = 0xAA。

4. 波特率与时钟的详细计算

发送时钟
  • 系统时钟:72MHz。
  • 预分频器:72 → 定时器时钟 = 1MHz(1µs)。
  • ARR:8 → 周期 = 9µs。
  • 波特率:1 / 9µs = 111111bps。
  • 误差:(115200 - 111111) / 115200 ≈ 3.5%,可接受。
接收时钟
  • 预分频器:72 → 1MHz。
  • ARR:4 → 周期 = 5µs(半个位时长)。
  • 采样时机:起始位后 4.5µs 开始,每 9µs 采样一次。
拉偏示例
  • 拉偏 0xAA 的第 3 位
    • 标准:每位 9µs。
    • 拉偏 +10%:第 3 位变为 9.9µs(ARR = 9 → 10)。
    • 中断中动态调整:

5. 软件串口原理总结

  • 发送
    • 定时器控制每位时长,GPIO 输出电平。
    • 波特率由 ARR 和时钟频率决定。
  • 接收
    • 外部中断触发,定时器采样。
    • 半个位时长采样确保精度。
  • 时钟与速率
    • 定时器时钟 = 系统时钟 / 预分频器。
    • 位时长 = (ARR + 1) * 时钟周期。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值