STM32寄存器开发全面指南

STM32寄存器开发全面指南

1. 寄存器开发基础

1.1 寄存器与HAL库的关系

STM32微控制器可以通过直接操作寄存器或使用HAL库进行开发。寄存器操作是最底层的编程方式,而HAL库则是ST公司提供的硬件抽象层,封装了寄存器操作,提供了更友好的API。

1.2 寄存器操作基本原理

STM32的寄存器通常是32位的,每个位或一组位控制特定功能。寄存器操作主要包括:

  • 读取寄存器值
  • 设置特定位(置1)
  • 清除特定位(置0)
  • 修改特定位(先清除再设置)

位操作基础
在STM32中,大多数寄存器配置都是通过位操作来完成的。主要有以下几种操作:

1. 清除位 : &= ~(value)
2. 设置位 : |= (value)
3. 翻转位 : ^= (value)

位操作解析
&= ~(3 << (0 * 2))
这行代码用于 清除 特定位置的位。

3 在二进制中是 11 ,表示要清除两个连续的位
 << (0 * 2) 表示左移0位,即从第0位开始

0 * 2 是在计算位的起始位置
- 在STM32的GPIO MODER寄存器中,每个引脚占用2个位
- 第0个引脚(PA0)对应的位是第0位和第1位
- 第1个引脚(PA1)对应的位是第2位和第3位
- 所以对于引脚n,其在MODER寄存器中的起始位置是 n * 2


 ~ 是按位取反操作
 &= 是按位与赋值操作
所以 &= ~(3 << (0 * 2)) 的作用是将第0位和第1位清零,而保持其他位不变。

基本操作示例:

// 读取寄存器
uint32_t value = REGISTER;

// 设置位
REGISTER |= (1 << BIT_POSITION);

// 清除位
REGISTER &= ~(1 << BIT_POSITION);

// 修改位
REGISTER = (REGISTER & ~(MASK << BIT_POSITION)) | (VALUE << BIT_POSITION);

2. 常用外设寄存器详解

2.1 GPIO寄存器详解

寄存器名称功能描述对应的 HAL 库函数详细配置说明
GPIOx_MODER配置 IO 口模式HAL_GPIO_Init()每个引脚占 2 位,00 = 输入,01 = 输出,10 = 复用,11 = 模拟
GPIOx_OTYPER配置输出类型HAL_GPIO_Init()0 = 推挽输出,1 = 开漏输出
GPIOx_OSPEEDR配置输出速度HAL_GPIO_Init()00 = 低速,01 = 中速,10 = 高速,11 = 超高速
GPIOx_PUPDR配置上拉 / 下拉HAL_GPIO_Init()00 = 无上下拉,01 = 上拉,10 = 下拉
GPIOx_IDR读取输入数据HAL_GPIO_ReadPin()只读寄存器,读取引脚电平状态
GPIOx_ODR写输出数据HAL_GPIO_WritePin()控制引脚输出高低电平
GPIOx_BSRR原子位设置 / 复位HAL_GPIO_WritePin()低 16 位置 1 设置对应引脚,高 16 位置 1 复位对应引脚
GPIOx_LCKR锁定配置HAL_GPIO_LockPin()锁定 GPIO 配置,防止意外修改
GPIOx_AFRL/AFRH复用功能配置HAL_GPIO_Init()选择引脚的复用功能,每个引脚占 4 位
GPIO寄存器操作详细示例:
// 初始化GPIO配置
void GPIO_Init(void) {
    // 使能GPIOA时钟
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    
    // 配置PA0为输出模式
    GPIOA->MODER &= ~(3 << (0 * 2));  // 清除原有设置
    GPIOA->MODER |= (1 << (0 * 2));   // 设置为输出模式(01)
    
    // 配置为推挽输出
    GPIOA->OTYPER &= ~(1 << 0);
    
    // 配置为高速输出
    GPIOA->OSPEEDR &= ~(3 << (0 * 2));
    GPIOA->OSPEEDR |= (2 << (0 * 2));
    
    // 配置无上下拉
    GPIOA->PUPDR &= ~(3 << (0 * 2));
    
    // 配置PA1为输入模式(带上拉)
    GPIOA->MODER &= ~(3 << (1 * 2));  // 输入模式(00)
    GPIOA->PUPDR &= ~(3 << (1 * 2));
    GPIOA->PUPDR |= (1 << (1 * 2));   // 上拉(01)
    
    // 配置PA2为复用功能(USART2_TX)
    GPIOA->MODER &= ~(3 << (2 * 2));
    GPIOA->MODER |= (2 << (2 * 2));   // 复用功能(10)
    GPIOA->AFR[0] &= ~(0xF << (2 * 4));
    GPIOA->AFR[0] |= (7 << (2 * 4));  // AF7(USART2)
}

// GPIO输出操作
void GPIO_Output_Example(void) {
    // 方法1:使用ODR寄存器
    GPIOA->ODR |= (1 << 0);   // PA0输出高电平
    GPIOA->ODR &= ~(1 << 0);  // PA0输出低电平
    
    // 方法2:使用BSRR寄存器(原子操作,更安全)
    GPIOA->BSRR = (1 << 0);       // PA0输出高电平
    GPIOA->BSRR = (1 << (0 + 16)); // PA0输出低电平
    
    // 翻转引脚状态
    GPIOA->ODR ^= (1 << 0);
}

// GPIO输入操作
uint8_t GPIO_Input_Example(void) {
    // 读取PA1输入状态
    if(GPIOA->IDR & (1 << 1)) {
        return 1;  // 高电平
    } else {
        return 0;  // 低电平
    }
}

2.2 RCC寄存器详解

寄存器名称功能描述对应的 HAL 库函数详细配置说明
RCC_CR时钟控制HAL_RCC_OscConfig()控制各种时钟源的开关和状态
RCC_PLLCFGRPLL 配置HAL_RCC_OscConfig()配置 PLL 的倍频、分频参数
RCC_CFGR时钟配置HAL_RCC_ClockConfig()选择系统时钟源和配置各总线分频
RCC_CIR时钟中断HAL_RCC_NMI_IRQHandler()时钟就绪中断和标志
RCC_AHB1ENRAHB1 外设时钟使能__HAL_RCC_GPIOx_CLK_ENABLE()控制 AHB1 总线上外设的时钟
RCC_AHB2ENRAHB2 外设时钟使能__HAL_RCC_xxx_CLK_ENABLE()控制 AHB2 总线上外设的时钟
RCC_APB1ENRAPB1 外设时钟使能__HAL_RCC_xxx_CLK_ENABLE()控制 APB1 总线上外设的时钟
RCC_APB2ENRAPB2 外设时钟使能__HAL_RCC_xxx_CLK_ENABLE()控制 APB2 总线上外设的时钟
RCC_AHB1RSTRAHB1 外设复位__HAL_RCC_xxx_FORCE_RESET()复位 AHB1 总线上的外设
RCC_APB1RSTRAPB1 外设复位__HAL_RCC_xxx_FORCE_RESET()复位 APB1 总线上的外设
RCC_APB2RSTRAPB2 外设复位__HAL_RCC_xxx_FORCE_RESET()复位 APB2 总线上的外设
寄存器功能HAL库对应函数详细说明
RCC寄存器操作详细示例:
// 系统时钟配置(以STM32F4为例,配置为168MHz)
void SystemClock_Config(void) {
    // 使能HSE
    RCC->CR |= RCC_CR_HSEON;
    while(!(RCC->CR & RCC_CR_HSERDY)); // 等待HSE稳定
    
    // 配置PLL
    RCC->PLLCFGR = 0;  // 复位PLL配置
    RCC->PLLCFGR |= (8 << 0);       // PLLM=8
    RCC->PLLCFGR |= (336 << 6);     // PLLN=336
    RCC->PLLCFGR |= (0 << 16);      // PLLP=2(00)
    RCC->PLLCFGR |= (7 << 24);      // PLLQ=7
    RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE; // PLL源为HSE
    
    // 使能PLL
    RCC->CR |= RCC_CR_PLLON;
    while(!(RCC->CR & RCC_CR_PLLRDY)); // 等待PLL锁定
    
    // 配置Flash等待周期和ART加速器
    FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_5WS;
    
    // 配置总线分频
    RCC->CFGR &= ~RCC_CFGR_HPRE;    // AHB不分频
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; // APB1 4分频(42MHz)
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; // APB2 2分频(84MHz)
    
    // 选择PLL作为系统时钟
    RCC->CFGR &= ~RCC_CFGR_SW;
    RCC->CFGR |= RCC_CFGR_SW_PLL;
    while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // 等待PLL成为系统时钟
}

// 外设时钟使能
void Peripherals_Clock_Enable(void) {
    // 使能GPIO时钟
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;  // GPIOA
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;  // GPIOB
    
    // 使能USART1时钟(APB2外设)
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
    
    // 使能SPI1时钟(APB2外设)
    RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
    
    // 使能I2C1时钟(APB1外设)
    RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
    
    // 使能TIM2时钟(APB1外设)
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
    
    // 使能DMA1时钟
    RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
}

// 外设复位
void Peripheral_Reset(void) {
    // 复位USART1
    RCC->APB2RSTR |= RCC_APB2RSTR_USART1RST;
    RCC->APB2RSTR &= ~RCC_APB2RSTR_USART1RST;
}
 

2.3 USART寄存器详解

寄存器名称功能描述对应的 HAL 库函数详细配置说明
USART_CR1控制寄存器 1HAL_UART_Init()控制 USART 的基本功能,如使能、字长、奇偶校验等
USART_CR2控制寄存器 2HAL_UART_Init()控制停止位、时钟等
USART_CR3控制寄存器 3HAL_UART_Init()控制硬件流控、DMA 等
USART_BRR波特率寄存器HAL_UART_Init()设置波特率
USART_GTPR保护时间和预分频HAL_UART_Init()用于智能卡模式
USART_RTOR接收超时HAL_UART_Init()设置接收超时时间
USART_RQR请求寄存器HAL_UART_AbortReceive()请求特定操作
USART_ISR中断和状态HAL_UART_GetState()状态标志 (F7/L4 系列)
USART_ICR中断清除HAL_UART_IRQHandler()清除中断标志 (F7/L4 系列)
USART_RDR接收数据HAL_UART_Receive()接收数据寄存器 (F7/L4 系列)
USART_TDR发送数据HAL_UART_Transmit()发送数据寄存器 (F7/L4 系列)
USART_SR状态寄存器HAL_UART_GetState()状态标志 (F4/F1 系列)
USART_DR数据寄存器HAL_UART_Transmit()/HAL_UART_Receive()收发数据 (F4/F1 系列)
寄存器功能HAL库对应函数详细说明
USART寄存器操作详细示例:
// USART初始化(以STM32F4为例,USART1配置为115200 8N1)
void USART1_Init(void) {
    // 使能USART1和GPIOA时钟
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    
    // 配置PA9(TX)和PA10(RX)为复用功能
    GPIOA->MODER &= ~(GPIO_MODER_MODER9_Msk | GPIO_MODER_MODER10_Msk);
    GPIOA->MODER |= (GPIO_MODER_MODER9_1 | GPIO_MODER_MODER10_1); // 复用功能
    
    // 配置为AF7(USART1)
    GPIOA->AFR[1] &= ~(0xF << ((9-8)*4) | 0xF << ((10-8)*4));
    GPIOA->AFR[1] |= (7 << ((9-8)*4)) | (7 << ((10-8)*4));
    
    // 配置为高速
    GPIOA->OSPEEDR |= (GPIO_OSPEEDER_OSPEEDR9_1 | GPIO_OSPEEDER_OSPEEDR10_1);
    
    // 配置为推挽输出和上拉输入
    GPIOA->OTYPER &= ~(GPIO_OTYPER_OT_9);
    GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPDR9_Msk | GPIO_PUPDR_PUPDR10_Msk);
    GPIOA->PUPDR |= (GPIO_PUPDR_PUPDR9_0 | GPIO_PUPDR_PUPDR10_0); // 上拉
    
    // 复位USART1
    RCC->APB2RSTR |= RCC_APB2RSTR_USART1RST;
    RCC->APB2RSTR &= ~RCC_APB2RSTR_USART1RST;
    
    // 配置波特率(假设APB2时钟为84MHz)
    // BRR = fCK / baud rate = 84000000/115200 = 729.16
    USART1->BRR = 729;
    
    // 配置USART1参数:8位数据,1位停止位,无校验,使能发送和接收
    USART1->CR1 = 0;  // 复位CR1
    USART1->CR1 |= USART_CR1_TE | USART_CR1_RE;  // 使能发送和接收
    
    // 配置CR2和CR3
    USART1->CR2 = 0;  // 1个停止位
    USART1->CR3 = 0;  // 无硬件流控
    
    // 使能USART1
    USART1->CR1 |= USART_CR1_UE;
    
    // 可选:使能接收中断
    // USART1->CR1 |= USART_CR1_RXNEIE;
    // NVIC_EnableIRQ(USART1_IRQn);
}

// USART发送一个字节
void USART1_SendByte(uint8_t data) {
    // 等待发送缓冲区为空
    while(!(USART1->SR & USART_SR_TXE));
    
    // 发送数据
    USART1->DR = data;
    
    // 等待发送完成
    while(!(USART1->SR & USART_SR_TC));
}

// USART发送字符串
void USART1_SendString(char *str) {
    while(*str) {
        USART1_SendByte(*str++);
    }
}

// USART接收一个字节(阻塞方式)
uint8_t USART1_ReceiveByte(void) {
    // 等待接收到数据
    while(!(USART1->SR & USART_SR_RXNE));
    
    // 读取并返回数据
    return (uint8_t)USART1->DR;
}

// USART中断处理函数
void USART1_IRQHandler(void) {
    if(USART1->SR & USART_SR_RXNE) {
        // 接收到数据
        uint8_t data = (uint8_t)USART1->DR;
        // 处理接收到的数据...
        
        // 回显
        USART1_SendByte(data);
    }
}
 

2.4 TIM定时器寄存器详解

寄存器名称功能描述对应的 HAL 库函数详细配置说明
TIM_CR1控制寄存器 1HAL_TIM_Base_Init()控制计数器的基本功能
TIM_CR2控制寄存器 2HAL_TIM_Base_Init()控制主从模式等
TIM_SMCR从模式控制HAL_TIM_SlaveConfigSynchro()配置从模式和触发源
TIM_DIER中断使能HAL_TIM_Base_Start_IT()使能各种中断和 DMA 请求
TIM_SR状态寄存器HAL_TIM_IRQHandler()各种状态标志
TIM_EGR事件生成HAL_TIM_GenerateEvent()软件生成事件
TIM_CCMR1捕获 / 比较模式 1HAL_TIM_PWM_Init()配置通道 1 和 2 的模式
TIM_CCMR2捕获 / 比较模式 2HAL_TIM_PWM_Init()配置通道 3 和 4 的模式
TIM_CCER捕获 / 比较使能HAL_TIM_PWM_Start()使能捕获 / 比较通道
TIM_CNT计数器__HAL_TIM_GET_COUNTER()当前计数值
TIM_PSC预分频器HAL_TIM_Base_Init()设置预分频值
TIM_ARR自动重装载HAL_TIM_Base_Init()设置计数器周期
TIM_CCR1 - 4捕获 / 比较寄存器__HAL_TIM_SET_COMPARE()设置比较值或捕获值
TIM_DCRDMA 控制HAL_TIM_DMABurst_WriteStart()配置 DMA 突发传输
TIM_DMARDMA 地址HAL_TIM_DMABurst_WriteStart()DMA 访问的寄存器地址
寄存器功能HAL库对应函数详细说明
TIM定时器寄存器操作详细示例:
// 基本定时器初始化(以TIM2为例,配置为1ms中断)
void TIM2_Init(void) {
    // 使能TIM2时钟
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
    
    // 配置TIM2
    TIM2->PSC = 84-1;        // 预分频,84MHz/84=1MHz
    TIM2->ARR = 1000-1;      // 自动重装载值,1MHz/1000=1KHz(1ms)
    TIM2->CR1 = 0;           // 复位CR1
    TIM2->CR1 |= TIM_CR1_ARPE; // 使能ARR预装载
    
    // 使能更新中断
    TIM2->DIER |= TIM_DIER_UIE;
    
    // 清除更新中断标志
    TIM2->SR &= ~TIM_SR_UIF;
    
    // 使能TIM2
    TIM2->CR1 |= TIM_CR1_CEN;
    
    // 使能TIM2中断
    NVIC_EnableIRQ(TIM2_IRQn);
    NVIC_SetPriority(TIM2_IRQn, 1);
}

// PWM输出初始化(以TIM3通道1为例)
void TIM3_PWM_Init(void) {
    // 使能TIM3和GPIOA时钟
    RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    
    // 配置PA6为TIM3_CH1
    GPIOA->MODER &= ~GPIO_MODER_MODER6_Msk;
    GPIOA->MODER |= GPIO_MODER_MODER6_1;  // 复用功能
    GPIOA->AFR[0] &= ~(0xF << (6 * 4));
    GPIOA->AFR[0] |= (2 << (6 * 4));      // AF2(TIM3)
    
    // 配置TIM3基本参数
    TIM3->PSC = 84-1;        // 预分频,84MHz/84=1MHz
    TIM3->ARR = 1000-1;      // PWM周期1ms
    
    // 配置通道1为PWM模式1
    TIM3->CCMR1 &= ~TIM_CCMR1_OC1M_Msk;
    TIM3->CCMR1 |= (6 << TIM_CCMR1_OC1M_Pos);  // PWM模式1
    TIM3->CCMR1 |= TIM_CCMR1_OC1PE;            // 使能预装载
    
    // 设置PWM占空比(50%)
    TIM3->CCR1 = 500;
    
    // 使能通道1输出
    TIM3->CCER |= TIM_CCER_CC1E;
    
    // 使能TIM3
    TIM3->CR1 |= TIM_CR1_ARPE;  // 使能ARR预装载
    TIM3->CR1 |= TIM_CR1_CEN;   // 使能计数器
}

// 输入捕获初始化(以TIM4通道1为例)
void TIM4_Capture_Init(void) {
    // 使能TIM4和GPIOB时钟
    RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
    
    // 配置PB6为TIM4_CH1
    GPIOB->MODER &= ~GPIO_MODER_MODER6_Msk;
    GPIOB->MODER |= GPIO_MODER_MODER6_1;  // 复用功能
    GPIOB->AFR[0] &= ~(0xF << (6 * 4));
    GPIOB->AFR[0] |= (2 << (6 * 4));      // AF2(TIM4)
    
    // 配置TIM4基本参数
    TIM4->PSC = 84-1;        // 预分频,84MHz/84=1MHz
    TIM4->ARR = 0xFFFF;      // 最大计数值
    
    // 配置通道1为输入捕获
    TIM4->CCMR1 &= ~TIM_CCMR1_CC1S_Msk;
    TIM4->CCMR1 |= (1 << TIM_CCMR1_CC1S_Pos);  // 输入捕获,映射到IC1
    
    // 配置输入滤波和分频
    TIM4->CCMR1 &= ~(TIM_CCMR1_IC1F_Msk | TIM_CCMR1_IC1PSC_Msk);
    TIM4->CCMR1 |= (3 << TIM_CCMR1_IC1F_Pos);  // 8个采样点滤波
    
    // 配置上升沿触发
    TIM4->CCER &= ~(TIM_CCER_CC1P | TIM_CCER_CC1NP);
    
    // 使能通道1捕获
    TIM4->CCER |= TIM_CCER_CC1E;
    
    // 使能捕获中断
    TIM4->DIER |= TIM_DIER_CC1IE;
    
    // 使能TIM4
    TIM4->CR1 |= TIM_CR1_CEN;
    
    // 使能TIM4中断
    NVIC_EnableIRQ(TIM4_IRQn);
    NVIC_SetPriority(TIM4_IRQn, 2);
}

// TIM2中断处理函数
void TIM2_IRQHandler(void) {
    if(TIM2->SR & TIM_SR_UIF) {
        // 清除更新中断标志
        TIM2->SR &= ~TIM_SR_UIF;
        
        // 1ms定时中断处理...
        // 例如:LED翻转
        GPIOA->ODR ^= (1 << 0);
    }
}

// TIM4中断处理函数(输入捕获)
void TIM4_IRQHandler(void) {
    static uint16_t rising_edge = 0;
    static uint16_t falling_edge = 0;
    
    if(TIM4->SR & TIM_SR_CC1IF) {
        if(TIM4->CCER & TIM_CCER_CC1P) {
            // 下降沿捕获
            falling_edge = TIM4->CCR1;
            
            // 计算脉宽
            uint16_t pulse_width;
            if(falling_edge >= rising_edge) {
                pulse_width = falling_edge - rising_edge;
            } else {
                pulse_width = (0xFFFF - rising_edge) + falling_edge + 1;
            }
            
            // 处理捕获结果...
            
            // 切换为上升沿捕获
            TIM4->CCER &= ~(TIM_CCER_CC1P | TIM_CCER_CC1NP);
        } else {
            // 上升沿捕获
            rising_edge = TIM4->CCR1;
            
            // 切换为下降沿捕获
            TIM4->CCER |= TIM_CCER_CC1P;
            TIM4->CCER &= ~TIM_CCER_CC1NP;
        }
    }
}

2.5 ADC寄存器详解

寄存器名称功能HAL 库对应函数详细说明
ADC_SR状态寄存器HAL_ADC_PollForConversion()转换状态标志
ADC_CR1控制寄存器 1HAL_ADC_Init()配置 ADC 工作模式、分辨率等
ADC_CR2控制寄存器 2HAL_ADC_Init()配置触发源、数据对齐等
ADC_SMPR1/2采样时间HAL_ADC_ConfigChannel()配置各通道采样时间
ADC_SQR1/2/3规则序列HAL_ADC_ConfigChannel()配置规则转换序列
ADC_JSQR注入序列HAL_ADCEx_InjectedConfigChannel()配置注入转换序列
ADC_JDR1 - 4注入数据HAL_ADCEx_InjectedGetValue()注入通道转换结果
ADC_DR数据寄存器HAL_ADC_GetValue()规则通道转换结果

ADC寄存器操作详细示例:

// ADC初始化(以ADC1为例)
void ADC1_Init(void) {
    // 使能ADC1和GPIOA时钟
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    
    // 配置PA0为模拟输入
    GPIOA->MODER |= GPIO_MODER_MODER0;  // 模拟模式(11)
    
    // 复位ADC1
    RCC->APB2RSTR |= RCC_APB2RSTR_ADCRST;
    RCC->APB2RSTR &= ~RCC_APB2RSTR_ADCRST;
    
    // 配置ADC1
    ADC1->CR1 = 0;  // 复位CR1
    ADC1->CR2 = 0;  // 复位CR2
    
    // 配置分辨率、扫描模式等
    ADC1->CR1 &= ~ADC_CR1_RES;  // 12位分辨率
    ADC1->CR1 &= ~ADC_CR1_SCAN; // 禁用扫描模式
    
    // 配置数据对齐、EOC等
    ADC1->CR2 &= ~ADC_CR2_ALIGN; // 右对齐
    ADC1->CR2 |= ADC_CR2_EOCS;   // 每次转换结束产生EOC
    
    // 配置通道0采样时间(通道0对应PA0)
    ADC1->SMPR2 &= ~ADC_SMPR2_SMP0;
    ADC1->SMPR2 |= (7 << ADC_SMPR2_SMP0_Pos);  // 480周期(最长采样时间)
    
    // 配置规则序列
    ADC1->SQR1 &= ~ADC_SQR1_L;  // 1个转换
    ADC1->SQR3 &= ~ADC_SQR3_SQ1;
    ADC1->SQR3 |= (0 << ADC_SQR3_SQ1_Pos);  // 第1个转换为通道0
    
    // 使能ADC1
    ADC1->CR2 |= ADC_CR2_ADON;
    
    // 等待ADC稳定
    for(volatile uint32_t i = 0; i < 10000; i++);
}

// 单次ADC转换
uint16_t ADC1_GetValue(void) {
    // 启动转换
    ADC1->CR2 |= ADC_CR2_SWSTART;
    
    // 等待转换完成
    while(!(ADC1->SR & ADC_SR_EOC));
    
    // 读取转换结果
    return (uint16_t)ADC1->DR;
}

// 多通道ADC扫描模式示例
void ADC1_MultiChannel_Init(void) {
    // 使能ADC1和GPIOA/GPIOB时钟
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN;
    
    // 配置PA0, PA1, PB0为模拟输入
    GPIOA->MODER |= GPIO_MODER_MODER0 | GPIO_MODER_MODER1;
    GPIOB->MODER |= GPIO_MODER_MODER0;
    
    // 配置ADC1
    ADC1->CR1 = 0;
    ADC1->CR2 = 0;
    
    // 使能扫描模式
    ADC1->CR1 |= ADC_CR1_SCAN;
    
    // 配置DMA传输
    ADC1->CR2 |= ADC_CR2_DMA;
    ADC1->CR2 |= ADC_CR2_DDS;  // DMA请求直到DMA禁用
    
    // 配置通道采样时间
    // 通道0(PA0)
    ADC1->SMPR2 &= ~ADC_SMPR2_SMP0;
    ADC1->SMPR2 |= (7 << ADC_SMPR2_SMP0_Pos);
    
    // 通道1(PA1)
    ADC1->SMPR2 &= ~ADC_SMPR2_SMP1;
    ADC1->SMPR2 |= (7 << ADC_SMPR2_SMP1_Pos);
    
    // 通道8(PB0)
    ADC1->SMPR2 &= ~ADC_SMPR2_SMP8;
    ADC1->SMPR2 |= (7 << ADC_SMPR2_SMP8_Pos);
    
    // 配置规则序列(3个转换)
    ADC1->SQR1 &= ~ADC_SQR1_L;
    ADC1->SQR1 |= (2 << ADC_SQR1_L_Pos);  // 3个转换(值为转换数-1)
    
    // 配置转换顺序
    ADC1->SQR3 &= ~(ADC_SQR3_SQ1 | ADC_SQR3_SQ2 | ADC_SQR3_SQ3);
    ADC1->SQR3 |= (0 << ADC_SQR3_SQ1_Pos);  // 第1个转换为通道0
    ADC1->SQR3 |= (1 << ADC_SQR3_SQ2_Pos);  // 第2个转换为通道1
    ADC1->SQR3 |= (8 << ADC_SQR3_SQ3_Pos);  // 第3个转换为通道8
    
    // 使能ADC1
    ADC1->CR2 |= ADC_CR2_ADON;
    
    // 等待ADC稳定
    for(volatile uint32_t i = 0; i < 10000; i++);
}

// 配置DMA传输ADC结果
void ADC1_DMA_Config(uint16_t *buffer, uint32_t length) {
    // 使能DMA2时钟(ADC1使用DMA2 Stream0 Channel0)
    RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
    
    // 确保DMA2 Stream0禁用
    DMA2_Stream0->CR &= ~DMA_SxCR_EN;
    while(DMA2_Stream0->CR & DMA_SxCR_EN);
    
    // 清除所有标志
    DMA2->LIFCR = 0x0F7D0F7D;
    
    // 配置DMA2 Stream0
    DMA2_Stream0->PAR = (uint32_t)&ADC1->DR;  // 外设地址
    DMA2_Stream0->M0AR = (uint32_t)buffer;    // 存储器地址
    DMA2_Stream0->NDTR = length;              // 数据项数
    
    // 配置DMA传输参数
    DMA2_Stream0->CR = 0;
    DMA2_Stream0->CR |= (0 << DMA_SxCR_CHSEL_Pos);  // 通道0
    DMA2_Stream0->CR |= DMA_SxCR_PL_1;              // 高优先级
    DMA2_Stream0->CR |= DMA_SxCR_MSIZE_0;           // 存储器数据宽度16位
    DMA2_Stream0->CR |= DMA_SxCR_PSIZE_0;           // 外设数据宽度16位
    DMA2_Stream0->CR |= DMA_SxCR_MINC;              // 存储器地址递增
    DMA2_Stream0->CR |= DMA_SxCR_CIRC;              // 循环模式
    
    // 使能DMA2 Stream0
    DMA2_Stream0->CR |= DMA_SxCR_EN;
}

// 启动ADC连续转换
void ADC1_Start_Conversion(void) {
    // 配置为连续转换模式
    ADC1->CR2 |= ADC_CR2_CONT;
    
    // 启动转换
    ADC1->CR2 |= ADC_CR2_SWSTART;
}


2.6 I2C寄存器详解

寄存器名称功能HAL 库对应函数详细说明
I2C_CR1控制寄存器 1HAL_I2C_Init()控制 I2C 的基本功能
I2C_CR2控制寄存器 2HAL_I2C_Init()配置时钟和中断
I2C_OAR1自身地址 1HAL_I2C_Init()设置自身地址 (7 位或 10 位)
I2C_OAR2自身地址 2HAL_I2C_Init()设置第二个地址 (7 位)
I2C_DR数据寄存器HAL_I2C_Master_Transmit()/HAL_I2C_Master_Receive()发送 / 接收数据
I2C_SR1状态寄存器 1HAL_I2C_GetState()状态标志
I2C_SR2状态寄存器 2HAL_I2C_GetState()附加状态标志
I2C_CCR时钟控制HAL_I2C_Init()配置 SCL 时钟频率
I2C_TRISE上升时间HAL_I2C_Init()配置 SCL 上升时间

I2C寄存器操作详细示例:

// I2C初始化(以I2C1为例,配置为主模式,100KHz)
void I2C1_Init(void) {
    // 使能I2C1和GPIOB时钟
    RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
    
    // 配置PB6(SCL)和PB7(SDA)为复用功能
    GPIOB->MODER &= ~(GPIO_MODER_MODER6_Msk | GPIO_MODER_MODER7_Msk);
    GPIOB->MODER |= (GPIO_MODER_MODER6_1 | GPIO_MODER_MODER7_1);  // 复用功能
    
    // 配置为开漏输出
    GPIOB->OTYPER |= (GPIO_OTYPER_OT_6 | GPIO_OTYPER_OT_7);
    
    // 配置为上拉
    GPIOB->PUPDR &= ~(GPIO_PUPDR_PUPDR6_Msk | GPIO_PUPDR_PUPDR7_Msk);
    GPIOB->PUPDR |= (GPIO_PUPDR_PUPDR6_0 | GPIO_PUPDR_PUPDR7_0);
    
    // 配置为高速
    GPIOB->OSPEEDR |= (GPIO_OSPEEDER_OSPEEDR6_1 | GPIO_OSPEEDER_OSPEEDR7_1);
    
    // 配置为AF4(I2C1)
    GPIOB->AFR[0] &= ~(0xF << (6 * 4) | 0xF << (7 * 4));
    GPIOB->AFR[0] |= (4 << (6 * 4)) | (4 << (7 * 4));
    
    // 复位I2C1
    RCC->APB1RSTR |= RCC_APB1RSTR_I2C1RST;
    RCC->APB1RSTR &= ~RCC_APB1RSTR_I2C1RST;
    
    // 禁用I2C1
    I2C1->CR1 &= ~I2C_CR1_PE;
    
    // 配置I2C时钟
    I2C1->CR2 &= ~I2C_CR2_FREQ;
    I2C1->CR2 |= 42;  // APB1时钟频率(MHz),假设为42MHz
    
    // 配置CCR(时钟控制)
    I2C1->CCR = 0;
    I2C1->CCR &= ~I2C_CCR_FS;  // 标准模式(100KHz)
    // CCR = Fpclk / (2 * Fi2c) = 42MHz / (2 * 100KHz) = 210
    I2C1->CCR |= 210;
    
    // 配置TRISE(上升时间)
    // TRISE = (Tr / Tpclk) + 1 = (1000ns / (1/42MHz)) + 1 = 43
    I2C1->TRISE = 43;
    
    // 使能I2C1
    I2C1->CR1 |= I2C_CR1_PE;
}

// I2C发送数据(主模式)
uint8_t I2C1_Write(uint8_t device_addr, uint8_t reg_addr, uint8_t *data, uint16_t len) {
    // 等待I2C空闲
    uint32_t timeout = 10000;
    while(I2C1->SR2 & I2C_SR2_BUSY) {
        if(--timeout == 0) return 1;  // 超时错误
    }
    
    // 发送起始位
    I2C1->CR1 |= I2C_CR1_START;
    
    // 等待起始位发送完成
    timeout = 10000;
    while(!(I2C1->SR1 & I2C_SR1_SB)) {
        if(--timeout == 0) return 2;  // 超时错误
    }
    
    // 发送设备地址(写)
    I2C1->DR = device_addr << 1;
    
    // 等待地址发送完成
    timeout = 10000;
    while(!(I2C1->SR1 & I2C_SR1_ADDR)) {
        if(--timeout == 0) return 3;  // 超时错误
    }
    
    // 清除ADDR标志
    uint32_t temp = I2C1->SR1;
    temp = I2C1->SR2;
    (void)temp;
    
    // 发送寄存器地址
    I2C1->DR = reg_addr;
    
    // 等待数据发送完成
    timeout = 10000;
    while(!(I2C1->SR1 & I2C_SR1_TXE)) {
        if(--timeout == 0) return 4;  // 超时错误
    }
    
    // 发送数据
    for(uint16_t i = 0; i < len; i++) {
        I2C1->DR = data[i];
        
        // 等待数据发送完成
        timeout = 10000;
        while(!(I2C1->SR1 & I2C_SR1_TXE)) {
            if(--timeout == 0) return 5;  // 超时错误
        }
    }
    
    // 发送停止位
    I2C1->CR1 |= I2C_CR1_STOP;
    
    return 0;  // 成功
}

// I2C接收数据(主模式)
uint8_t I2C1_Read(uint8_t device_addr, uint8_t reg_addr, uint8_t *data, uint16_t len) {
    // 等待I2C空闲
    uint32_t timeout = 10000;
    while(I2C1->SR2 & I2C_SR2_BUSY) {
        if(--timeout == 0) return 1;  // 超时错误
    }
    
    // 发送起始位
    I2C1->CR1 |= I2C_CR1_START;
    
    // 等待起始位发送完成
    timeout = 10000;
    while(!(I2C1->SR1 & I2C_SR1_SB)) {
        if(--timeout == 0) return 2;  // 超时错误
    }
    
    // 发送设备地址(写)
    I2C1->DR = device_addr << 1;
    
    // 等待地址发送完成
    timeout = 10000;
    while(!(I2C1->SR1 & I2C_SR1_ADDR)) {
        if(--timeout == 0) return 3;  // 超时错误
    }
    
    // 清除ADDR标志
    uint32_t temp = I2C1->SR1;
    temp = I2C1->SR2;
    (void)temp;
    
    // 发送寄存器地址
    I2C1->DR = reg_addr;
    
    // 等待数据发送完成
    timeout = 10000;
    while(!(I2C1->SR1 & I2C_SR1_TXE)) {
        if(--timeout == 0) return 4;  // 超时错误
    }
    
    // 发送重复起始位
    I2C1->CR1 |= I2C_CR1_START;
    
    // 等待起始位发送完成
    timeout = 10000;
    while(!(I2C1->SR1 & I2C_SR1_SB)) {
        if(--timeout == 0) return 5;  // 超时错误
    }
    
    // 发送设备地址(读)
    I2C1->DR = (device_addr << 1) | 0x01;
    
    // 等待地址发送完成
    timeout = 10000;
    while(!(I2C1->SR1 & I2C_SR1_ADDR)) {
        if(--timeout == 0) return 6;  // 超时错误
    }
    
    // 根据接收数据长度配置ACK
    if(len == 1) {
        // 单字节接收,禁用ACK
        I2C1->CR1 &= ~I2C_CR1_ACK;
    } else {
        // 多字节接收,使能ACK
        I2C1->CR1 |= I2C_CR1_ACK;
    }
    
    // 清除ADDR标志
    temp = I2C1->SR1;
    temp = I2C1->SR2;
    (void)temp;
    
    // 接收数据
    for(uint16_t i = 0; i < len; i++) {
        if(i == len - 1) {
            // 最后一个字节,禁用ACK
            I2C1->CR1 &= ~I2C_CR1_ACK;
        }
        
        // 等待接收数据
        timeout = 10000;
        while(!(I2C1->SR1 & I2C_SR1_RXNE)) {
            if(--timeout == 0) return 7;  // 超时错误
        }
        
        // 读取数据
        data[i] = I2C1->DR;
    }
    
    // 发送停止位
    I2C1->CR1 |= I2C_CR1_STOP;
    
    // 重新使能ACK
    I2C1->CR1 |= I2C_CR1_ACK;
    
    return 0;  // 成功
}


 2.7 SPI寄存器详解

寄存器功能HAL 库对应函数详细说明
SPI_CR1控制寄存器 1HAL_SPI_Init()控制 SPI 的基本功能
SPI_CR2控制寄存器 2HAL_SPI_Init()控制中断和 DMA
SPI_SR状态寄存器HAL_SPI_GetState()状态标志
SPI_DR数据寄存器HAL_SPI_Transmit()/HAL_SPI_Receive()发送 / 接收数据
SPI_CRCPRCRC 多项式HAL_SPI_Init()设置 CRC 多项式
SPI_RXCRCR接收 CRCHAL_SPI_GetCRCState()接收 CRC 值
SPI_TXCRCR发送 CRCHAL_SPI_GetCRCState()发送 CRC 值
SPI_I2SCFGRI2S 配置HAL_I2S_Init()配置 I2S 模式
SPI_I2SPRI2S 预分频HAL_I2S_Init()配置 I2S 时钟

SPI寄存器操作详细示例:

// SPI初始化(以SPI1为例,配置为主模式)
void SPI1_Init(void) {
    // 使能SPI1和GPIOA时钟
    RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    
    // 配置PA5(SCK), PA6(MISO), PA7(MOSI)为复用功能
    GPIOA->MODER &= ~(GPIO_MODER_MODER5_Msk | GPIO_MODER_MODER6_Msk | GPIO_MODER_MODER7_Msk);
    GPIOA->MODER |= (GPIO_MODER_MODER5_1 | GPIO_MODER_MODER6_1 | GPIO_MODER_MODER7_1);
    
    // 配置为AF5(SPI1)
    GPIOA->AFR[0] &= ~(0xF << (5 * 4) | 0xF << (6 * 4) | 0xF << (7 * 4));
    GPIOA->AFR[0] |= (5 << (5 * 4)) | (5 << (6 * 4)) | (5 << (7 * 4));
    
    // 配置为高速
    GPIOA->OSPEEDR |= (GPIO_OSPEEDER_OSPEEDR5_1 | GPIO_OSPEEDER_OSPEEDR6_1 | GPIO_OSPEEDER_OSPEEDR7_1);
    
    // 配置PA5(SCK)和PA7(MOSI)为推挽输出,PA6(MISO)为上拉输入
    GPIOA->OTYPER &= ~(GPIO_OTYPER_OT_5 | GPIO_OTYPER_OT_7);
    GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPDR5_Msk | GPIO_PUPDR_PUPDR6_Msk | GPIO_PUPDR_PUPDR7_Msk);
    GPIOA->PUPDR |= GPIO_PUPDR_PUPDR6_0;  // PA6上拉
    
    // 配置PA4为推挽输出(软件NSS)
    GPIOA->MODER &= ~GPIO_MODER_MODER4_Msk;
    GPIOA->MODER |= GPIO_MODER_MODER4_0;  // 输出模式
    GPIOA->OTYPER &= ~GPIO_OTYPER_OT_4;   // 推挽输出
    GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR4_1;  // 高速
    GPIOA->BSRR = GPIO_BSRR_BS_4;  // NSS高电平(不选中)
    
    // 复位SPI1
    RCC->APB2RSTR |= RCC_APB2RSTR_SPI1RST;
    RCC->APB2RSTR &= ~RCC_APB2RSTR_SPI1RST;
    
    // 配置SPI1
    SPI1->CR1 = 0;  // 复位CR1
    
    // 配置为主模式,时钟极性和相位,波特率等
    SPI1->CR1 |= SPI_CR1_MSTR;  // 主模式
    SPI1->CR1 &= ~SPI_CR1_CPOL; // 时钟极性CPOL=0
    SPI1->CR1 &= ~SPI_CR1_CPHA; // 时钟相位CPHA=0
    
    // 配置波特率为fPCLK/16 (84MHz/16=5.25MHz)
    SPI1->CR1 |= (3 << SPI_CR1_BR_Pos);
    
    // 配置为软件NSS管理
    SPI1->CR1 |= SPI_CR1_SSM | SPI_CR1_SSI;
    
    // 配置数据格式
    SPI1->CR1 &= ~SPI_CR1_DFF;  // 8位数据格式
    SPI1->CR1 &= ~SPI_CR1_LSBFIRST; // MSB先传
    
    // 使能SPI1
    SPI1->CR1 |= SPI_CR1_SPE;
}

// SPI发送接收一个字节
uint8_t SPI1_TransmitReceive(uint8_t data) {
    // 等待发送缓冲区为空
    while(!(SPI1->SR & SPI_SR_TXE));
    
    // 发送数据
    SPI1->DR = data;
    
    // 等待接收缓冲区非空
    while(!(SPI1->SR & SPI_SR_RXNE));
    
    // 读取接收到的数据
    return SPI1->DR;
}

// SPI发送数据
void SPI1_Transmit(uint8_t *data, uint16_t size) {
    // 选中从设备(NSS低电平)
    GPIOA->BSRR = GPIO_BSRR_BR_4;
    
    for(uint16_t i = 0; i < size; i++) {
        // 等待发送缓冲区为空
        while(!(SPI1->SR & SPI_SR_TXE));
        
        // 发送数据
        SPI1->DR = data[i];
        
        // 等待传输完成
        while(!(SPI1->SR & SPI_SR_RXNE));
        
        // 读取接收到的数据(清除RXNE标志)
        uint8_t temp = SPI1->DR;
        (void)temp;
    }
    
    // 等待SPI不忙
    while(SPI1->SR & SPI_SR_BSY);
    
    // 取消选中从设备(NSS高电平)
    GPIOA->BSRR = GPIO_BSRR_BS_4;
}

// SPI接收数据
void SPI1_Receive(uint8_t *data, uint16_t size) {
    // 选中从设备(NSS低电平)
    GPIOA->BSRR = GPIO_BSRR_BR_4;
    
    for(uint16_t i = 0; i < size; i++) {
        // 发送一个哑数据以产生时钟
        SPI1->DR = 0xFF;
        
        // 等待接收缓冲区非空
        while(!(SPI1->SR & SPI_SR_RXNE));
        
        // 读取接收到的数据
        data[i] = SPI1->DR;
    }
    
    // 等待SPI不忙
    while(SPI1->SR & SPI_SR_BSY);
    
    // 取消选中从设备(NSS高电平)
    GPIOA->BSRR = GPIO_BSRR_BS_4;
}

// SPI DMA传输配置
void SPI1_DMA_Config(void) {
    // 使能DMA2时钟(SPI1使用DMA2)
    RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
    
    // 配置DMA2 Stream3(SPI1_TX)
    DMA2_Stream3->CR = 0;  // 复位CR
    while(DMA2_Stream3->CR & DMA_SxCR_EN);  // 等待DMA禁用
    
    DMA2_Stream3->CR |= (3 << DMA_SxCR_CHSEL_Pos);  // 通道3
    DMA2_Stream3->CR |= DMA_SxCR_PL_1;              // 高优先级
    DMA2_Stream3->CR |= DMA_SxCR_MINC;              // 存储器地址递增
    DMA2_Stream3->CR &= ~DMA_SxCR_PSIZE;            // 外设数据宽度8位
    DMA2_Stream3->CR &= ~DMA_SxCR_MSIZE;            // 存储器数据宽度8位
    DMA2_Stream3->CR |= DMA_SxCR_DIR_0;             // 存储器到外设
    
    // 配置DMA2 Stream2(SPI1_RX)
    DMA2_Stream2->CR = 0;  // 复位CR
    while(DMA2_Stream2->CR & DMA_SxCR_EN);  // 等待DMA禁用
    
    DMA2_Stream2->CR |= (3 << DMA_SxCR_CHSEL_Pos);  // 通道3
    DMA2_Stream2->CR |= DMA_SxCR_PL_0;              // 中优先级
    DMA2_Stream2->CR |= DMA_SxCR_MINC;              // 存储器地址递增
    DMA2_Stream2->CR &= ~DMA_SxCR_PSIZE;            // 外设数据宽度8位
    DMA2_Stream2->CR &= ~DMA_SxCR_MSIZE;            // 存储器数据宽度8位
    DMA2_Stream2->CR &= ~DMA_SxCR_DIR;              // 外设到存储器
    
    // 设置外设地址
    DMA2_Stream3->PAR = (uint32_t)&SPI1->DR;
    DMA2_Stream2->PAR = (uint32_t)&SPI1->DR;
    
    // 使能SPI1的DMA请求
    SPI1->CR2 |= SPI_CR2_TXDMAEN | SPI_CR2_RXDMAEN;
}

// SPI DMA传输
void SPI1_TransmitReceive_DMA(uint8_t *tx_data, uint8_t *rx_data, uint16_t size) {
    // 设置DMA存储器地址和数据量
    DMA2_Stream3->M0AR = (uint32_t)tx_data;
    DMA2_Stream3->NDTR = size;
    
    DMA2_Stream2->M0AR = (uint32_t)rx_data;
    DMA2_Stream2->NDTR = size;
    
    // 清除DMA标志
    DMA2->LIFCR = DMA_LIFCR_CTCIF2 | DMA_LIFCR_CHTIF2 | DMA_LIFCR_CTEIF2 | DMA_LIFCR_CDMEIF2 | DMA_LIFCR_CFEIF2;
    DMA2->LIFCR = DMA_LIFCR_CTCIF3 | DMA_LIFCR_CHTIF3 | DMA_LIFCR_CTEIF3 | DMA_LIFCR_CDMEIF3 | DMA_LIFCR_CFEIF3;
    
    // 选中从设备(NSS低电平)
    GPIOA->BSRR = GPIO_BSRR_BR_4;
    
    // 使能DMA
    DMA2_Stream2->CR |= DMA_SxCR_EN;  // 先使能RX
    DMA2_Stream3->CR |= DMA_SxCR_EN;  // 再使能TX
    
    // 等待传输完成
    while(!(DMA2->LISR & DMA_LISR_TCIF2));
    
    // 等待SPI不忙
    while(SPI1->SR & SPI_SR_BSY);
    
    // 取消选中从设备(NSS高电平)
    GPIOA->BSRR = GPIO_BSRR_BS_4;
    
    // 禁用DMA
    DMA2_Stream3->CR &= ~DMA_SxCR_EN;
    DMA2_Stream2->CR &= ~DMA_SxCR_EN;
}

2.8 DMA寄存器详解

寄存器功能HAL 库对应函数详细说明
DMA_SxCR配置寄存器HAL_DMA_Init()配置 DMA 传输参数
DMA_SxNDTR数据量寄存器HAL_DMA_Start()设置传输数据项数
DMA_SxPAR外设地址HAL_DMA_Init()设置外设地址
DMA_SxM0AR存储器 0 地址HAL_DMA_Init()设置存储器地址
DMA_SxM1AR存储器 1 地址HAL_DMAEx_MultiBufferStart()双缓冲模式的第二个地址
DMA_SxFCRFIFO 控制HAL_DMA_Init()配置 FIFO 阈值和直接模式
DMA_LISR/HISR低 / 高中断状态HAL_DMA_IRQHandler()中断状态标志
DMA_LIFCR/HIFCR低 / 高中断标志清除HAL_DMA_IRQHandler()清除中断标志

DMA寄存器操作详细示例:

// DMA内存到内存传输示例
void DMA_MemToMem_Transfer(uint32_t *src, uint32_t *dst, uint32_t size) {
    // 使能DMA2时钟
    RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
    
    // 配置DMA2 Stream0
    DMA2_Stream0->CR = 0;  // 复位CR
    while(DMA2_Stream0->CR & DMA_SxCR_EN);  // 等待DMA禁用
    
    // 配置为内存到内存模式
    DMA2_Stream0->CR |= DMA_SxCR_DIR_0;     // 方向:存储器到存储器
    DMA2_Stream0->CR |= DMA_SxCR_MINC;      // 存储器地址递增
    DMA2_Stream0->CR |= DMA_SxCR_PINC;      // 外设(这里是源内存)地址递增
    DMA2_Stream0->CR |= DMA_SxCR_MSIZE_1;   // 存储器数据宽度32位
    DMA2_Stream0->CR |= DMA_SxCR_PSIZE_1;   // 外设数据宽度32位
    DMA2_Stream0->CR |= DMA_SxCR_PL_1;      // 高优先级
    DMA2_Stream0->CR |= DMA_SxCR_MEM2MEM;   // 内存到内存模式
    
    // 设置源地址、目标地址和数据量
    DMA2_Stream0->PAR = (uint32_t)src;      // 源地址
    DMA2_Stream0->M0AR = (uint32_t)dst;     // 目标地址
    DMA2_Stream0->NDTR = size;              // 数据项数
    
    // 禁用FIFO模式(使用直接模式)
    DMA2_Stream0->FCR &= ~DMA_SxFCR_DMDIS;
    
    // 清除所有中断标志
    DMA2->LIFCR = 0x0F7D0F7D;
    
    // 使能DMA
    DMA2_Stream0->CR |= DMA_SxCR_EN;
    
    // 等待传输完成
    while(!(DMA2->LISR & DMA_LISR_TCIF0));
    
    // 清除传输完成标志
    DMA2->LIFCR = DMA_LIFCR_CTCIF0;
    
    // 禁用DMA
    DMA2_Stream0->CR &= ~DMA_SxCR_EN;
}

// DMA双缓冲模式示例(以ADC1为例)
void DMA_DoubleBuffer_Config(uint16_t *buffer0, uint16_t *buffer1, uint16_t size) {
    // 使能DMA2时钟
    RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
    
    // 配置DMA2 Stream0(ADC1)
    DMA2_Stream0->CR = 0;  // 复位CR
    while(DMA2_Stream0->CR & DMA_SxCR_EN);  // 等待DMA禁用
    
    // 配置DMA参数
    DMA2_Stream0->CR |= (0 << DMA_SxCR_CHSEL_Pos);  // 通道0
    DMA2_Stream0->CR &= ~DMA_SxCR_DIR;              // 外设到存储器
    DMA2_Stream0->CR |= DMA_SxCR_MINC;              // 存储器地址递增
    DMA2_Stream0->CR |= DMA_SxCR_PSIZE_0;           // 外设数据宽度16位
    DMA2_Stream0->CR |= DMA_SxCR_MSIZE_0;           // 存储器数据宽度16位
    DMA2_Stream0->CR |= DMA_SxCR_CIRC;              // 循环模式
    DMA2_Stream0->CR |= DMA_SxCR_PL_1;              // 高优先级
    
    // 使能双缓冲模式
    DMA2_Stream0->CR |= DMA_SxCR_DBM;
    
    // 设置外设地址和数据量
    DMA2_Stream0->PAR = (uint32_t)&ADC1->DR;
    DMA2_Stream0->NDTR = size;
    
    // 设置两个存储器地址
    DMA2_Stream0->M0AR = (uint32_t)buffer0;
    DMA2_Stream0->M1AR = (uint32_t)buffer1;
    
    // 清除所有中断标志
    DMA2->LIFCR = 0x0F7D0F7D;
    
    // 使能DMA传输完成中断
    DMA2_Stream0->CR |= DMA_SxCR_TCIE;
    
    // 使能DMA中断
    NVIC_EnableIRQ(DMA2_Stream0_IRQn);
    NVIC_SetPriority(DMA2_Stream0_IRQn, 2);
    
    // 使能DMA
    DMA2_Stream0->CR |= DMA_SxCR_EN;
}

// DMA中断处理函数
void DMA2_Stream0_IRQHandler(void) {
    if(DMA2->LISR & DMA_LISR_TCIF0) {
        // 清除传输完成标志
        DMA2->LIFCR = DMA_LIFCR_CTCIF0;
        
        // 检查当前使用的缓冲区
        if(DMA2_Stream0->CR & DMA_SxCR_CT) {
            // 当前使用的是存储器1,可以处理存储器0的数据
            // 处理buffer0的数据...
        } else {
            // 当前使用的是存储器0,可以处理存储器1的数据
            // 处理buffer1的数据...
        }
    }
}


2.9 EXTI寄存器详解

寄存器功能HAL 库对应函数详细说明
EXTI_IMR中断屏蔽HAL_GPIO_Init()使能中断线
EXTI_EMR事件屏蔽HAL_GPIO_Init()使能事件线
EXTI_RTSR上升沿触发HAL_GPIO_Init()配置上升沿触发
EXTI_FTSR下降沿触发HAL_GPIO_Init()配置下降沿触发
EXTI_SWIER软件中断HAL_GPIO_EXTI_IRQHandler()软件触发中断
EXTI_PR挂起寄存器HAL_GPIO_EXTI_IRQHandler()中断挂起标志

EXTI寄存器操作详细示例:

// 外部中断初始化(以PA0为例,配置为下降沿触发)
void EXTI0_Init(void) {
    // 使能GPIOA和SYSCFG时钟
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
    
    // 配置PA0为输入模式
    GPIOA->MODER &= ~GPIO_MODER_MODER0;
    
    // 配置为上拉输入
    GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR0;
    GPIOA->PUPDR |= GPIO_PUPDR_PUPDR0_0;
    
    // 配置EXTI线路映射
    SYSCFG->EXTICR[0] &= ~SYSCFG_EXTICR1_EXTI0;  // 清除EXTI0设置
    SYSCFG->EXTICR[0] |= SYSCFG_EXTICR1_EXTI0_PA;  // 选择PA0作为EXTI0
    
    // 配置EXTI线0
    EXTI->IMR |= EXTI_IMR_MR0;    // 使能中断
    EXTI->EMR &= ~EXTI_EMR_MR0;   // 禁用事件
    EXTI->RTSR &= ~EXTI_RTSR_TR0; // 禁用上升沿触发
    EXTI->FTSR |= EXTI_FTSR_TR0;  // 使能下降沿触发
    
    // 清除挂起标志
    EXTI->PR = EXTI_PR_PR0;
    
    // 配置NVIC
    NVIC_SetPriority(EXTI0_IRQn, 3);
    NVIC_EnableIRQ(EXTI0_IRQn);
}

// 外部中断处理函数
void EXTI0_IRQHandler(void) {
    // 检查是否是EXTI0中断
    if(EXTI->PR & EXTI_PR_PR0) {
        // 清除挂起标志
        EXTI->PR = EXTI_PR_PR0;
        
        // 中断处理...
        // 例如:LED翻转
        GPIOC->ODR ^= GPIO_ODR_OD13;
        
        // 延时消抖
        for(volatile uint32_t i = 0; i < 100000; i++);
    }
}

// 配置多个外部中断
void EXTI_MultiLine_Init(void) {
    // 使能GPIO和SYSCFG时钟
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN;
    RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
    
    // 配置PA1和PB2为输入模式
    GPIOA->MODER &= ~GPIO_MODER_MODER1;
    GPIOB->MODER &= ~GPIO_MODER_MODER2;
    
    // 配置为上拉输入
    GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR1;
    GPIOA->PUPDR |= GPIO_PUPDR_PUPDR1_0;
    GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR2;
    GPIOB->PUPDR |= GPIO_PUPDR_PUPDR2_0;
    
    // 配置EXTI线路映射
    // PA1 -> EXTI1
    SYSCFG->EXTICR[0] &= ~SYSCFG_EXTICR1_EXTI1;
    SYSCFG->EXTICR[0] |= SYSCFG_EXTICR1_EXTI1_PA;
    
    // PB2 -> EXTI2
    SYSCFG->EXTICR[0] &= ~SYSCFG_EXTICR1_EXTI2;
    SYSCFG->EXTICR[0] |= SYSCFG_EXTICR1_EXTI2_PB;
    
    // 配置EXTI线1和线2
    // EXTI1: 上升沿触发
    EXTI->IMR |= EXTI_IMR_MR1;
    EXTI->RTSR |= EXTI_RTSR_TR1;
    EXTI->FTSR &= ~EXTI_FTSR_TR1;
    
    // EXTI2: 下降沿触发
    EXTI->IMR |= EXTI_IMR_MR2;
    EXTI->RTSR &= ~EXTI_RTSR_TR2;
    EXTI->FTSR |= EXTI_FTSR_TR2;
    
    // 清除挂起标志
    EXTI->PR = EXTI_PR_PR1 | EXTI_PR_PR2;
    
    // 配置NVIC
    NVIC_SetPriority(EXTI1_IRQn, 3);
    NVIC_EnableIRQ(EXTI1_IRQn);
    
    NVIC_SetPriority(EXTI2_IRQn, 3);
    NVIC_EnableIRQ(EXTI2_IRQn);
}

// EXTI1中断处理函数
void EXTI1_IRQHandler(void) {
    if(EXTI->PR & EXTI_PR_PR1) {
        EXTI->PR = EXTI_PR_PR1;  // 清除挂起标志
        
        // 处理EXTI1中断...
    }
}

// EXTI2中断处理函数
void EXTI2_IRQHandler(void) {
    if(EXTI->PR & EXTI_PR_PR2) {
        EXTI->PR = EXTI_PR_PR2;  // 清除挂起标志
        
        // 处理EXTI2中断...
    }
}


3. 寄存器开发进阶技巧


3.1 位带操作


STM32的位带操作是一种高效的位操作技术,可以将位操作转换为地址操作,提高效率。

// 位带操作宏定义
#define PERIPH_BASE         0x40000000
#define PERIPH_BB_BASE      0x42000000
#define SRAM_BASE           0x20000000
#define SRAM_BB_BASE        0x22000000

// 外设位带操作
#define PERIPH_BB(addr, bit) \
    (*(volatile uint32_t *)(PERIPH_BB_BASE + (((uint32_t)(addr) - PERIPH_BASE) * 32) + ((bit) * 4)))

// SRAM位带操作
#define SRAM_BB(addr, bit) \
    (*(volatile uint32_t *)(SRAM_BB_BASE + (((uint32_t)(addr) - SRAM_BASE) * 32) + ((bit) * 4)))

// 使用示例
void BitBand_Example(void) {
    // 使用位带操作设置GPIOC的第13位(PC13)
    PERIPH_BB(&GPIOC->ODR, 13) = 1;  // 等价于 GPIOC->ODR |= (1 << 13);
    PERIPH_BB(&GPIOC->ODR, 13) = 0;  // 等价于 GPIOC->ODR &= ~(1 << 13);
    
    // 使用位带操作读取GPIOA的第0位(PA0)
    uint32_t pin_state = PERIPH_BB(&GPIOA->IDR, 0);  // 等价于 (GPIOA->IDR & (1 << 0)) ? 1 : 0;
    
    // SRAM位带操作示例
    uint32_t flag = 0;
    SRAM_BB(&flag, 0) = 1;  // 设置flag的第0位
    SRAM_BB(&flag, 1) = 1;  // 设置flag的第1位
}

3.2 原子操作


在多线程或中断环境中,原子操作可以避免竞态条件。

// 使用LDREX和STREX指令实现原子操作
uint32_t Atomic_Increment(uint32_t *addr) {
    uint32_t tmp;
    uint32_t status;
    
    do {
        // 加载独占
        tmp = __LDREXW(addr);
        // 尝试存储独占
        status = __STREXW(tmp + 1, addr);
    } while(status != 0);  // 如果存储失败,重试
    
    return tmp + 1;
}

// 使用位带实现原子位操作
void Atomic_BitSet(volatile uint32_t *addr, uint32_t bit) {
    PERIPH_BB(addr, bit) = 1;
}

void Atomic_BitClear(volatile uint32_t *addr, uint32_t bit) {
    PERIPH_BB(addr, bit) = 0;
}

3.3 寄存器操作优化技巧

// 使用临时变量减少寄存器访问次数
void Register_Optimization(void) {
    // 不优化的方式
    GPIOA->MODER &= ~(3 << (0 * 2));
    GPIOA->MODER |= (1 << (0 * 2));
    GPIOA->OTYPER &= ~(1 << 0);
    GPIOA->OSPEEDR &= ~(3 << (0 * 2));
    GPIOA->OSPEEDR |= (2 << (0 * 2));
    
    // 优化后的方式
    uint32_t temp_moder = GPIOA->MODER;
    uint32_t temp_ospeedr = GPIOA->OSPEEDR;
    
    temp_moder &= ~(3 << (0 * 2));
    temp_moder |= (1 << (0 * 2));
    
    temp_ospeedr &= ~(3 << (0 * 2));
    temp_ospeedr |= (2 << (0 * 2));
    
    GPIOA->MODER = temp_moder;
    GPIOA->OTYPER &= ~(1 << 0);
    GPIOA->OSPEEDR = temp_ospeedr;
}

// 使用位掩码简化位操作
#define GPIO_PIN_0              ((uint16_t)0x0001)
#define GPIO_PIN_SET            1
#define GPIO_PIN_RESET          0

void GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, uint8_t PinState) {
    if(PinState != GPIO_PIN_RESET) {
        GPIOx->BSRR = GPIO_Pin;
    } else {
        GPIOx->BSRR = (uint32_t)GPIO_Pin << 16;
    }
}



 5. 实用寄存器开发模板

5.1 系统初始化模板

// 系统时钟配置(以STM32F4为例,配置为168MHz)
void SystemClock_Config(void) {
    // 使能HSE
    RCC->CR |= RCC_CR_HSEON;
    while(!(RCC->CR & RCC_CR_HSERDY));
    
    // 配置电源控制
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
    PWR->CR |= PWR_CR_VOS;  // 电压调节器输出选择Scale1模式
    
    // 配置Flash预取、指令缓存、数据缓存和等待周期
    FLASH->ACR = FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_PRFTEN | FLASH_ACR_LATENCY_5WS;
    
    // 配置PLL
    RCC->PLLCFGR = RCC_PLLCFGR_PLLSRC_HSE |  // PLL源为HSE
                  (8 << RCC_PLLCFGR_PLLM_Pos) |  // PLLM=8
                  (336 << RCC_PLLCFGR_PLLN_Pos) |  // PLLN=336
                  (0 << RCC_PLLCFGR_PLLP_Pos) |  // PLLP=2
                  (7 << RCC_PLLCFGR_PLLQ_Pos);   // PLLQ=7
    
    // 使能PLL
    RCC->CR |= RCC_CR_PLLON;
    while(!(RCC->CR & RCC_CR_PLLRDY));
    
    // 配置总线分频
    RCC->CFGR = (0 << RCC_CFGR_HPRE_Pos) |   // AHB不分频
               (5 << RCC_CFGR_PPRE1_Pos) |  // APB1 4分频(42MHz)
               (4 << RCC_CFGR_PPRE2_Pos);   // APB2 2分频(84MHz)
    
    // 选择PLL作为系统时钟
    RCC->CFGR |= RCC_CFGR_SW_PLL;
    while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
    
    // 更新系统时钟变量
    SystemCoreClock = 168000000;
}

// 系统初始化
void System_Init(void) {
    // 配置系统时钟
    SystemClock_Config();
    
    // 使能所需外设时钟
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | 
                    RCC_AHB1ENR_GPIOCEN | RCC_AHB1ENR_GPIODEN;
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN | RCC_APB1ENR_TIM3EN | 
                    RCC_APB1ENR_I2C1EN;
    RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN | RCC_APB2ENR_SPI1EN | 
                    RCC_APB2ENR_USART1EN | RCC_APB2ENR_ADC1EN;
    
    // 配置SysTick
    SysTick_Config(SystemCoreClock / 1000);  // 1ms中断
    
    // 配置NVIC优先级分组
    NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
}


5.2 GPIO通用模板

// GPIO初始化
void GPIO_Init(void) {
    // LED初始化(PC13)
    GPIOC->MODER &= ~GPIO_MODER_MODER13_Msk;
    GPIOC->MODER |= GPIO_MODER_MODER13_0;  // 输出模式
    GPIOC->OTYPER &= ~GPIO_OTYPER_OT_13;   // 推挽输出
    GPIOC->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR13_Msk;
    GPIOC->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR13_0;  // 中速
    
    // 按键初始化(PA0)
    GPIOA->MODER &= ~GPIO_MODER_MODER0_Msk;  // 输入模式
    GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR0_Msk;
    GPIOA->PUPDR |= GPIO_PUPDR_PUPDR0_1;     // 下拉
    
    // USART1引脚初始化(PA9/PA10)
    GPIOA->MODER &= ~(GPIO_MODER_MODER9_Msk | GPIO_MODER_MODER10_Msk);
    GPIOA->MODER |= (GPIO_MODER_MODER9_1 | GPIO_MODER_MODER10_1);  // 复用功能
    GPIOA->AFR[1] &= ~(0xF << ((9-8)*4) | 0xF << ((10-8)*4));
    GPIOA->AFR[1] |= (7 << ((9-8)*4)) | (7 << ((10-8)*4));  // AF7(USART1)
    GPIOA->OSPEEDR |= (GPIO_OSPEEDER_OSPEEDR9_1 | GPIO_OSPEEDER_OSPEEDR10_1);  // 高速
    GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPDR9_Msk | GPIO_PUPDR_PUPDR10_Msk);
    GPIOA->PUPDR |= (GPIO_PUPDR_PUPDR9_0 | GPIO_PUPDR_PUPDR10_0);  // 上拉
}

// GPIO操作宏定义
#define LED_ON()        GPIOC->BSRR = GPIO_BSRR_BR_13
#define LED_OFF()       GPIOC->BSRR = GPIO_BSRR_BS_13
#define LED_TOGGLE()    GPIOC->ODR ^= GPIO_ODR_OD13
#define KEY_STATE()     ((GPIOA->IDR & GPIO_IDR_ID0) ? 1 : 0)


5.3 USART通用模板

// USART1初始化(115200 8N1)
void USART1_Init(void) {
    // 使能USART1时钟
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
    
    // 配置波特率(假设APB2时钟为84MHz)
    // BRR = fCK / baud rate = 84000000/115200 = 729.16
    USART1->BRR = 729;
    
    // 配置USART1参数:8位数据,1位停止位,无校验,使能发送和接收
    USART1->CR1 = 0;  // 复位CR1
    USART1->CR1 |= USART_CR1_TE | USART_CR1_RE;  // 使能发送和接收
    
    // 配置CR2和CR3
    USART1->CR2 = 0;  // 1个停止位
    USART1->CR3 = 0;  // 无硬件流控
    
    // 使能USART1
    USART1->CR1 |= USART_CR1_UE;
    
    // 可选:使能接收中断
    // USART1->CR1 |= USART_CR1_RXNEIE;
    // NVIC_EnableIRQ(USART1_IRQn);
}

// USART1发送一个字节
void USART1_SendByte(uint8_t data) {
    // 等待发送缓冲区为空
    while(!(USART1->SR & USART_SR_TXE));
    
    // 发送数据
    USART1->DR = data;
}

// USART1发送字符串
void USART1_SendString(char *str) {
    while(*str) {
        USART1_SendByte(*str++);
    }
}

// USART1发送数据(带长度)
void USART1_SendData(uint8_t *data, uint16_t len) {
    for(uint16_t i = 0; i < len; i++) {
        USART1_SendByte(data[i]);
    }
}

// USART1接收一个字节(阻塞方式)
uint8_t USART1_ReceiveByte(void) {
    // 等待接收到数据
    while(!(USART1->SR & USART_SR_RXNE));
    
    // 读取并返回数据
    return (uint8_t)USART1->DR;
}

// USART1中断处理函数
void USART1_IRQHandler(void) {
    if(USART1->SR & USART_SR_RXNE) {
        // 接收到数据
        uint8_t data = (uint8_t)USART1->DR;
        
        // 处理接收到的数据...
        
        // 回显
        USART1_SendByte(data);
    }
}


5.4 定时器通用模板

// 基本定时器初始化(TIM2, 1ms中断)
void TIM2_Init(void) {
    // 使能TIM2时钟
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
    
    // 配置TIM2
    TIM2->PSC = 84-1;        // 预分频,84MHz/84=1MHz
    TIM2->ARR = 1000-1;      // 自动重装载值,1MHz/1000=1KHz(1ms)
    TIM2->CR1 = 0;           // 复位CR1
    TIM2->CR1 |= TIM_CR1_ARPE; // 使能ARR预装载
    
    // 使能更新中断
    TIM2->DIER |= TIM_DIER_UIE;
    
    // 清除更新中断标志
    TIM2->SR &= ~TIM_SR_UIF;
    
    // 使能TIM2
    TIM2->CR1 |= TIM_CR1_CEN;
    
    // 使能TIM2中断
    NVIC_EnableIRQ(TIM2_IRQn);
    NVIC_SetPriority(TIM2_IRQn, 1);
}

// PWM输出初始化(TIM3通道1, PA6)
void TIM3_PWM_Init(void) {
    // 使能TIM3和GPIOA时钟
    RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    
    // 配置PA6为TIM3_CH1
    GPIOA->MODER &= ~GPIO_MODER_MODER6_Msk;
    GPIOA->MODER |= GPIO_MODER_MODER6_1;  // 复用功能
    GPIOA->AFR[0] &= ~(0xF << (6 * 4));
    GPIOA->AFR[0] |= (2 << (6 * 4));      // AF2(TIM3)
    
    // 配置TIM3基本参数
    TIM3->PSC = 84-1;        // 预分频,84MHz/84=1MHz
    TIM3->ARR = 1000-1;      // PWM周期1ms
    
    // 配置通道1为PWM模式1
    TIM3->CCMR1 &= ~TIM_CCMR1_OC1M_Msk;
    TIM3->CCMR1 |= (6 << TIM_CCMR1_OC1M_Pos);  // PWM模式1
    TIM3->CCMR1 |= TIM_CCMR1_OC1PE;            // 使能预装载
    
    // 设置PWM占空比(50%)
    TIM3->CCR1 = 500;
    
    // 使能通道1输出
    TIM3->CCER |= TIM_CCER_CC1E;
    
    // 使能TIM3
    TIM3->CR1 |= TIM_CR1_ARPE;  // 使能ARR预装载
    TIM3->CR1 |= TIM_CR1_CEN;   // 使能计数器
}

// 设置PWM占空比(0-100%)
void TIM3_SetPWM_DutyCycle(uint8_t channel, uint8_t duty) {
    uint32_t pulse = ((uint32_t)duty * (TIM3->ARR + 1)) / 100;
    
    switch(channel) {
        case 1:
            TIM3->CCR1 = pulse;
            break;
        case 2:
            TIM3->CCR2 = pulse;
            break;
        case 3:
            TIM3->CCR3 = pulse;
            break;
        case 4:
            TIM3->CCR4 = pulse;
            break;
    }
}

// TIM2中断处理函数
void TIM2_IRQHandler(void) {
    if(TIM2->SR & TIM_SR_UIF) {
        // 清除更新中断标志
        TIM2->SR &= ~TIM_SR_UIF;
        
        // 1ms定时中断处理...
        // 例如:LED翻转
        static uint16_t counter = 0;
        
        counter++;
        if(counter >= 500) {  // 500ms
            counter = 0;
            LED_TOGGLE();
        }
    }
}


5.5 ADC通用模板

// ADC1初始化(单通道模式)
void ADC1_Init(void) {
    // 使能ADC1和GPIOA时钟
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    
    // 配置PA1为模拟输入(ADC1_IN1)
    GPIOA->MODER |= GPIO_MODER_MODER1;  // 模拟模式(11)
    
    // 复位ADC1
    RCC->APB2RSTR |= RCC_APB2RSTR_ADCRST;
    RCC->APB2RSTR &= ~RCC_APB2RSTR_ADCRST;
    
    // 配置ADC1
    ADC1->CR1 = 0;  // 复位CR1
    ADC1->CR2 = 0;  // 复位CR2
    
    // 配置分辨率、扫描模式等
    ADC1->CR1 &= ~ADC_CR1_RES;  // 12位分辨率
    ADC1->CR1 &= ~ADC_CR1_SCAN; // 禁用扫描模式
    
    // 配置数据对齐、EOC等
    ADC1->CR2 &= ~ADC_CR2_ALIGN; // 右对齐
    ADC1->CR2 |= ADC_CR2_EOCS;   // 每次转换结束产生EOC
    
    // 配置通道1采样时间(通道1对应PA1)
    ADC1->SMPR2 &= ~ADC_SMPR2_SMP1;
    ADC1->SMPR2 |= (7 << ADC_SMPR2_SMP1_Pos);  // 480周期(最长采样时间)
    
    // 配置规则序列
    ADC1->SQR1 &= ~ADC_SQR1_L;  // 1个转换
    ADC1->SQR3 &= ~ADC_SQR3_SQ1;
    ADC1->SQR3 |= (1 << ADC_SQR3_SQ1_Pos);  // 第1个转换为通道1
    
    // 使能ADC1
    ADC1->CR2 |= ADC_CR2_ADON;
    
    // 等待ADC稳定
    for(volatile uint32_t i = 0; i < 10000; i++);
}

// 获取ADC转换结果
uint16_t ADC1_GetValue(void) {
    // 启动转换
    ADC1->CR2 |= ADC_CR2_SWSTART;
    
    // 等待转换完成
    while(!(ADC1->SR & ADC_SR_EOC));
    
    // 读取转换结果
    return (uint16_t)ADC1->DR;
}

// 获取多次ADC采样的平均值
uint16_t ADC1_GetAverageValue(uint8_t times) {
    uint32_t sum = 0;
    
    for(uint8_t i = 0; i < times; i++) {
        sum += ADC1_GetValue();
    }
    
    return sum / times;
}


5.6 I2C通用模板

// I2C1初始化(主模式,100KHz)
void I2C1_Init(void) {
    // 使能I2C1和GPIOB时钟
    RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
    
    // 配置PB6(SCL)和PB7(SDA)为复用功能
    GPIOB->MODER &= ~(GPIO_MODER_MODER6_Msk | GPIO_MODER_MODER7_Msk);
    GPIOB->MODER |= (GPIO_MODER_MODER6_1 | GPIO_MODER_MODER7_1);  // 复用功能
    
    // 配置为开漏输出
    GPIOB->OTYPER |= (GPIO_OTYPER_OT_6 | GPIO_OTYPER_OT_7);
    
    // 配置为上拉
    GPIOB->PUPDR &= ~(GPIO_PUPDR_PUPDR6_Msk | GPIO_PUPDR_PUPDR7_Msk);
    GPIOB->PUPDR |= (GPIO_PUPDR_PUPDR6_0 | GPIO_PUPDR_PUPDR7_0);
    
    // 配置为高速
    GPIOB->OSPEEDR |= (GPIO_OSPEEDER_OSPEEDR6_1 | GPIO_OSPEEDER_OSPEEDR7_1);
    
    // 配置为AF4(I2C1)
    GPIOB->AFR[0] &= ~(0xF << (6 * 4) | 0xF << (7 * 4));
    GPIOB->AFR[0] |= (4 << (6 * 4)) | (4 << (7 * 4));
    
    // 复位I2C1
    RCC->APB1RSTR |= RCC_APB1RSTR_I2C1RST;
    RCC->APB1RSTR &= ~RCC_APB1RSTR_I2C1RST;
    
    // 禁用I2C1
    I2C1->CR1 &= ~I2C_CR1_PE;
    
    // 配置I2C时钟
    I2C1->CR2 &= ~I2C_CR2_FREQ;
    I2C1->CR2 |= 42;  // APB1时钟频率(MHz),假设为42MHz
    
    // 配置CCR(时钟控制)
    I2C1->CCR = 0;
    I2C1->CCR &= ~I2C_CCR_FS;  // 标准模式(100KHz)
    // CCR = Fpclk / (2 * Fi2c) = 42MHz / (2 * 100KHz) = 210
    I2C1->CCR |= 210;
    
    // 配置TRISE(上升时间)
    // TRISE = (Tr / Tpclk) + 1 = (1000ns / (1/42MHz)) + 1 = 43
    I2C1->TRISE = 43;
    
    // 使能I2C1
    I2C1->CR1 |= I2C_CR1_PE;
}

// I2C写入数据
uint8_t I2C1_Write(uint8_t device_addr, uint8_t reg_addr, uint8_t *data, uint16_t len) {
    // 等待I2C空闲
    uint32_t timeout = 10000;
    while(I2C1->SR2 & I2C_SR2_BUSY) {
        if(--timeout == 0) return 1;  // 超时错误
    }
    
    // 发送起始位
    I2C1->CR1 |= I2C_CR1_START;
    
    // 等待起始位发送完成
    timeout = 10000;
    while(!(I2C1->SR1 & I2C_SR1_SB)) {
        if(--timeout == 0) return 2;  // 超时错误
    }
    
    // 发送设备地址(写)
    I2C1->DR = device_addr << 1;
    
    // 等待地址发送完成
    timeout = 10000;
    while(!(I2C1->SR1 & I2C_SR1_ADDR)) {
        if(--timeout == 0) return 3;  // 超时错误
    }
    
    // 清除ADDR标志
    uint32_t temp = I2C1->SR1;
    temp = I2C1->SR2;
    (void)temp;
    
    // 发送寄存器地址
    I2C1->DR = reg_addr;
    
    // 等待数据发送完成
    timeout = 10000;
    while(!(I2C1->SR1 & I2C_SR1_TXE)) {
        if(--timeout == 0) return 4;  // 超时错误
    }
    
    // 发送数据
    for(uint16_t i = 0; i < len; i++) {
        I2C1->DR = data[i];
        
        // 等待数据发送完成
        timeout = 10000;
        while(!(I2C1->SR1 & I2C_SR1_TXE)) {
            if(--timeout == 0) return 5;  // 超时错误
        }
    }
    
    // 发送停止位
    I2C1->CR1 |= I2C_CR1_STOP;
    
    return 0;  // 成功
}

// I2C读取数据
uint8_t I2C1_Read(uint8_t device_addr, uint8_t reg_addr, uint8_t *data, uint16_t len) {
    // 等待I2C空闲
    uint32_t timeout = 10000;
    while(I2C1->SR2 & I2C_SR2_BUSY) {
        if(--timeout == 0) return 1;  // 超时错误
    }
    
    // 发送起始位
    I2C1->CR1 |= I2C_CR1_START;
    
    // 等待起始位发送完成
    timeout = 10000;
    while(!(I2C1->SR1 & I2C_SR1_SB)) {
        if(--timeout == 0) return 2;  // 超时错误
    }
    
    // 发送设备地址(写)
    I2C1->DR = device_addr << 1;
    
    // 等待地址发送完成
    timeout = 10000;
    while(!(I2C1->SR1 & I2C_SR1_ADDR)) {
        if(--timeout == 0) return 3;  // 超时错误
    }
    
    // 清除ADDR标志
    uint32_t temp = I2C1->SR1;
    temp = I2C1->SR2;
    (void)temp;
    
    // 发送寄存器地址
    I2C1->DR = reg_addr;
    
    // 等待数据发送完成
    timeout = 10000;
    while(!(I2C1->SR1 & I2C_SR1_TXE)) {
        if(--timeout == 0) return 4;  // 超时错误
    }
    
    // 发送重复起始位
    I2C1->CR1 |= I2C_CR1_START;
    
    // 等待起始位发送完成
    timeout = 10000;
    while(!(I2C1->SR1 & I2C_SR1_SB)) {
        if(--timeout == 0) return 5;  // 超时错误
    }
    
    // 发送设备地址(读)
    I2C1->DR = (device_addr << 1) | 0x01;
    
    // 等待地址发送完成
    timeout = 10000;
    while(!(I2C1->SR1 & I2C_SR1_ADDR)) {
        if(--timeout == 0) return 6;  // 超时错误
    }
    
    // 根据接收数据长度配置ACK
    if(len == 1) {
        // 单字节接收,禁用ACK
        I2C1->CR1 &= ~I2C_CR1_ACK;
    } else {
        // 多字节接收,使能ACK
        I2C1->CR1 |= I2C_CR1_ACK;
    }
    
    // 清除ADDR标志
    temp = I2C1->SR1;
    temp = I2C1->SR2;
    (void)temp;
    
    // 接收数据
    for(uint16_t i = 0; i < len; i++) {
        if(i == len - 1) {
            // 最后一个字节,禁用ACK
            I2C1->CR1 &= ~I2C_CR1_ACK;
        }
        
        // 等待接收数据
        timeout = 10000;
        while(!(I2C1->SR1 & I2C_SR1_RXNE)) {
            if(--timeout == 0) return 7;  // 超时错误
        }
        
        // 读取数据
        data[i] = I2C1->DR;
    }
    
    // 发送停止位
    I2C1->CR1 |= I2C_CR1_STOP;
    
    // 重新使能ACK
    I2C1->CR1 |= I2C_CR1_ACK;
    
    return 0;  // 成功
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值