单片机的外设是指与单片机核心处理部分相连的附加硬件模块,它们能够扩展单片机的功能和能力。外设指的是单片机外部的外围功能模块,比如键盘控制芯片,液晶,A/D转换芯片,等等。外设可通过单片机的I/O,SPI,I2C等总线控制。
1. GPIO(通用输入输出接口)
1.1 GPIO 原理
GPIO 是 STM32 中最基础的外设,用于数字信号的输入和输出。每个 GPIO 引脚可以配置为不同的工作模式,根据应用需求来实现不同的功能。GPIO 可以设置为以下几种模式:
- 输入模式:用于读取外部设备的信号。例如,按键、传感器输出等。
- 输出模式:用于驱动外部设备,如点亮 LED、控制电机。
- 复用模式:当 GPIO 引脚作为其他外设的功能引脚(如 UART、SPI、I2C)的时,需将其配置为复用模式。
- 模拟模式:此模式下 GPIO 用于处理模拟信号输入/输出,比如 ADC 或 DAC 信号。
GPIO 的输入/输出电平可以是高电平或低电平,对应逻辑“1”和逻辑“0”。输入时,外部设备提供电平,MCU 读取这个电平。输出时,MCU 根据设置控制 GPIO 引脚电平的高低。
1.2 GPIO 配置步骤
主要步骤:
- 启用 GPIO 端口时钟:外设时钟必须被开启,才能访问外设寄存器。
- 配置 GPIO 工作模式:根据使用场景,配置为输入、输出、复用、模拟模式。
- 配置 GPIO 拉高/拉低电阻:根据输入设备信号特性选择是否启用内部上拉或下拉电阻。
- 配置 GPIO 引脚速度:根据外设需求设置引脚速度(低、中、高速)。
- 读取或写入 GPIO 引脚:在程序中读取输入状态或输出控制信号。
示例代码: 下面展示了一个通过 GPIO 控制 LED 的程序,LED 连接在 GPIO_PIN_5
上。
c复制代码GPIO_InitTypeDef GPIO_InitStruct = {0};// 1. 启用 GPIO 时钟
__HAL_RCC_GPIOA_CLK_ENABLE();// 2. 配置 GPIO 引脚 PA5 为推挽输出模式
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上拉或下拉电阻
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 设置速度
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);// 3. 点亮 LED (输出高电平)
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);// 4. 熄灭 LED (输出低电平)
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
1.3 GPIO 中断配置
GPIO 引脚可以配置为触发中断,常用于处理外部事件(如按键触发、外部传感器信号变化)。例如,配置 GPIO_PIN_0
为上升沿触发中断。
c复制代码// 配置 PA0 为输入中断模式
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; // 上升沿触发中断
GPIO_InitStruct.Pull = GPIO_PULLDOWN; // 下拉电阻
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);// 使能 EXTI0 的中断
HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);// 中断回调函数
void EXTI0_IRQHandler(void)
{// 检查是否为 EXTI0 触发HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{if (GPIO_Pin == GPIO_PIN_0){// 中断处理逻辑,例如切换 LED}
}
2. USART/UART(通用同步/异步收发器)
2.1 USART 原理
USART 提供了串行通信功能,可以支持全双工通信。常用于与传感器、电脑、调试工具等设备进行通信。在 UART 模式下,数据以一定波特率传输,通过开始位、数据位、校验位和停止位来完成通信过程。
USART 的主要配置参数包括:
- 波特率:决定了数据传输的速度,常见波特率有 9600, 115200。
- 数据位长度:通常为 8 位,也支持 9 位数据帧。
- 校验:支持奇偶校验,也可以不使用校验。
- 停止位:1 位或 2 位,用于标识帧的结束。
2.2 USART 配置步骤
- 启用时钟:启用 USART 外设和相关 GPIO 引脚的时钟。
- 配置 USART 参数:设置波特率、数据长度、停止位、校验等。
- 配置 GPIO 引脚:配置 TX(发送)和 RX(接收)引脚为复用模式。
- 使能 USART:启用 USART 外设。
- 发送/接收数据:使用 HAL 库函数进行数据的发送和接收。
示例代码: 配置 USART2 进行 115200 波特率的通信。
c复制代码UART_HandleTypeDef huart2;// 1. 启用 USART2 时钟
__HAL_RCC_USART2_CLK_ENABLE();// 2. 配置 USART 参数
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200; // 设置波特率
huart2.Init.WordLength = UART_WORDLENGTH_8B; // 8 位数据长度
huart2.Init.StopBits = UART_STOPBITS_1; // 1 位停止位
huart2.Init.Parity = UART_PARITY_NONE; // 无校验
huart2.Init.Mode = UART_MODE_TX_RX; // 使能发送和接收
HAL_UART_Init(&huart2);
2.3 USART 数据发送与接收
USART 通信中,常使用阻塞模式、非阻塞模式(中断)或 DMA 模式。以下展示如何发送和接收数据:
c复制代码// 发送数据
char msg[] = "Hello, USART!";
HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);// 接收数据
uint8_t buffer[10];
HAL_UART_Receive(&huart2, buffer, sizeof(buffer), HAL_MAX_DELAY);
2.4 USART 中断模式
为了避免占用 CPU 进行数据轮询,可以使用中断方式来接收数据。当 USART 接收到数据时,会触发中断,并调用中断处理程序。
c复制代码// 启用中断接收
HAL_UART_Receive_IT(&huart2, buffer, 1);// 中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if (huart->Instance == USART2){// 处理接收到的数据}
}
3. SPI(串行外设接口)
3.1 SPI 原理
SPI 是一种同步通信协议,数据在时钟的同步下在主从设备之间传输。SPI 通常用于传感器、显示器、存储设备等外设的通信,能够实现高速通信。SPI 包括四条主要信号线:
- SCLK(时钟线):由主设备产生,控制数据传输速率。
- MOSI(主输出从输入):主设备发送数据给从设备。
- MISO(从输出主输入):从设备发送数据给主设备。
- SS(片选信号):选择要通信的从设备。
3.2 SPI 工作模式
SPI 有四种模式,通过时钟极性(CPOL)和时钟相位(CPHA)配置。根据从设备的要求,配置不同的工作模式。
- CPOL = 0, CPHA = 0:数据在时钟的第一个上升沿采样,时钟空闲时为低电平。
- CPOL = 0, CPHA = 1:数据在时钟的第二个上升沿采样,时钟空闲时为低电平。
- CPOL = 1, CPHA = 0:数据在时钟的第一个下降沿采样,时钟空闲时为高电平。
- CPOL = 1, CPHA = 1:数据在时钟的第二个下降沿采样,时钟空闲时为高电平。
3.3 SPI 配置步骤
- 启用时钟:为 SPI 外设和相关 GPIO 引脚启用时钟。
- 配置 SPI 工作模式:设置 SPI 主从模式、时钟极性、时钟相位、数据大小等。
- 配置 GPIO:将相关引脚设置为复用模式。
- 使能 SPI 外设:启动 SPI 外设。
- 收发数据:使用 HAL 函数传输数据。
示例代码: 配置 SPI1 进行主设备通信,CPOL = 0,CPHA = 0。
c复制代码SPI_HandleTypeDef hspi1;// 1. 启用 SPI 时钟
__HAL_RCC_SPI1_CLK_ENABLE();// 2. 配置 SPI
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER; // 配置为主设备
hspi1.Init.Direction = SPI_DIRECTION_2LINES; // 全双工通信
hspi1.Init.DataSize = SPI_DATASIZE_8BIT; // 8 位数据帧
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; // 时钟空闲时为低电平
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; // 在第一个上升沿采样
hspi1.Init.NSS = SPI_NSS_SOFT; // 软件控制片选信号
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
HAL_SPI_Init(&hspi1);
3.4 SPI 数据传输
SPI 支持阻塞、非阻塞(中断)和 DMA 模式的传输。以下展示如何使用 SPI 发送和接收数据:
c复制代码uint8_t txData[] = {0x01, 0x02, 0x03};
uint8_t rxData[3];// 发送数据
HAL_SPI_Transmit(&hspi1, txData, sizeof(txData), HAL_MAX_DELAY);// 接收数据
HAL_SPI_Receive(&hspi1, rxData, sizeof(rxData), HAL_MAX_DELAY);
3.5 SPI 中断模式
使用中断模式可以更高效地传输数据。配置 SPI 中断接收:
c复制代码// 启用中断接收
HAL_SPI_Receive_IT(&hspi1, rxData, sizeof(rxData));// 中断回调函数
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{if (hspi->Instance == SPI1){// 处理接收到的数据}
}
4. I2C(集成电路间通信)
4.1 I2C 原理
I2C 是一种常用的两线式通信协议,通常用于低速设备之间的通信。I2C 使用两根信号线:
- SCL(时钟线):控制数据传输速率。
- SDA(数据线):用于传输数据。
I2C 的特点是所有设备共享一条总线,主设备通过唯一的 7 位地址来选择要通信的从设备。I2C 支持半双工通信,数据由 SCL 同步进行传输。
4.2 I2C 配置步骤
- 启用时钟:为 I2C 外设和相关 GPIO 引脚启用时钟。
- 配置 I2C 参数:设置 I2C 工作频率、地址模式、从设备地址等。
- 配置 GPIO:将相关引脚设置为复用模式,并启用上拉电阻。
- 使能 I2C 外设:启动 I2C 外设。
- 发送/接收数据:使用 HAL 库函数进行数据传输。
示例代码: 配置 I2C1 进行主设备通信,地址模式为 7 位。
c复制代码I2C_HandleTypeDef hi2c1;// 1. 启用 I2C 时钟
__HAL_RCC_I2C1_CLK_ENABLE();// 2. 配置 I2C
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000; // 设置时钟频率为 100kHz
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; // 50% 占空比
hi2c1.Init.OwnAddress1 = 0x32; // 设置主设备地址
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; // 7 位地址模式
HAL_I2C_Init(&hi2c1);
4.3 I2C 数据传输
I2C 支持主从模式的数据传输,以下展示如何发送和接收数据:
c复制代码uint8_t txData[] = {0x01, 0x02, 0x03};
uint8_t rxData[3];
uint16_t deviceAddress = 0x50; // 从设备地址// 发送数据到从设备
HAL_I2C_Master_Transmit(&hi2c1, deviceAddress, txData, sizeof(txData), HAL_MAX_DELAY);// 从从设备接收数据
HAL_I2C_Master_Receive(&hi2c1, deviceAddress, rxData, sizeof(rxData), HAL_MAX_DELAY);
4.4 I2C 中断模式
可以使用中断模式来高效处理 I2C 数据通信:
c复制代码// 启用中断接收
HAL_I2C_Master_Receive_IT(&hi2c1, deviceAddress, rxData, sizeof(rxData));// 中断回调函数
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{if (hi2c->Instance == I2C1){// 处理接收到的数据}
}
5. ADC(模数转换器)
5.1 ADC 原理
STM32 的 ADC(Analog-to-Digital Converter)用于将模拟信号(如传感器信号)转换为数字信号。ADC 的主要参数包括:
- 分辨率:STM32 的 ADC 支持 12 位分辨率,也可以配置为 8 位、10 位。
- 参考电压:ADC 的转换结果是相对于参考电压的,典型的参考电压为 3.3V。
- 采样时间:ADC 在转换前需要对信号进行采样,不同的采样时间可以影响转换的速度和精度。
5.2 ADC 配置步骤
- 启用时钟:启用 ADC 时钟和相关 GPIO 时钟。
- 配置 ADC:设置 ADC 的分辨率、采样时间、数据对齐等参数。
- 配置 GPIO:将 ADC 的输入引脚配置为模拟模式。
- 启用 ADC:启动 ADC 并开始转换。
- 读取转换结果:通过 HAL 库函数获取转换后的数据。
示例代码: 配置 ADC1 读取通道 0 上的模拟信号。
c复制代码ADC_HandleTypeDef hadc1;
ADC_ChannelConfTypeDef sConfig = {0};// 1. 启用 ADC 和 GPIO 时钟
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();// 2. 配置 GPIO 引脚为模拟输入模式
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_0; // PA0 引脚
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; // 模拟模式
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);// 3. 配置 ADC 参数
hadc1.Instance = ADC1;
hadc1.Init.Resolution = ADC_RESOLUTION_12B; // 12 位分辨率
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; // 数据右对齐
hadc1.Init.ScanConvMode = DISABLE; // 单通道模式
HAL_ADC_Init(&hadc1);// 4. 配置 ADC 通道
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);// 5. 启动 ADC 并开始转换
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);// 6. 获取 ADC 转换结果
uint32_t adcValue = HAL_ADC_GetValue(&hadc1);
5.3 多通道 ADC
STM32 的 ADC 可以配置为多通道模式,即通过多个输入引脚采集多个模拟信号。多通道 ADC 的使用步骤和单通道类似,只需启用扫描模式,配置多个通道即可。
6. DAC(数模转换器)
6.1 DAC 原理
STM32 的 DAC(Digital-to-Analog Converter)用于将数字信号转换为模拟信号,常用于音频输出、波形生成、LED 亮度控制等场景。DAC 输出的电压范围通常为 0 到参考电压(如 3.3V)。
6.2 DAC 配置步骤
- 启用时钟:启用 DAC 和相关 GPIO 的时钟。
- 配置 DAC:设置 DAC 的触发源、输出模式、波形生成等参数。
- 配置 GPIO:将 DAC 的输出引脚配置为模拟模式。
- 启用 DAC:启动 DAC 并开始转换。
- 输出模拟信号:通过 HAL 库函数输出模拟信号。
示例代码: 配置 DAC1 输出一个固定电压。
c复制代码DAC_HandleTypeDef hdac1;// 1. 启用 DAC 和 GPIO 时钟
__HAL_RCC_DAC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();// 2. 配置 GPIO 引脚为模拟模式
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_4; // PA4 引脚
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; // 模拟模式
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);// 3. 配置 DAC 参数
hdac1.Instance = DAC;
HAL_DAC_Init(&hdac1);// 4. 启用 DAC 并输出电压
HAL_DAC_Start(&hdac1, DAC_CHANNEL_1);// 5. 设置输出电压
HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 2048); // 输出 1.65V(参考电压为 3.3V)
6.3 DAC 波形生成
DAC 可以生成简单的波形,例如正弦波、三角波或噪声信号,常用于测试或信号模拟。STM32 的 DAC 支持通过 DMA 进行波形的自动更新,减少 CPU 的负担。
7. 定时器(TIM)
7.1 定时器原理
STM32 的定时器外设用于计时、生成 PWM 信号、测量输入脉冲宽度、编码器接口等。定时器分为基本定时器、通用定时器和高级定时器:
- 基本定时器:用于简单的定时功能,如延时。
- 通用定时器:可以配置为计时、输入捕获、输出比较、PWM 生成等多种功能。
- 高级定时器:功能更为强大,通常用于电机控制等复杂场景。
7.2 定时器配置步骤
- 启用时钟:启用定时器时钟。
- 配置定时器参数:设置计数模式、分频系数、自动重装载值等。
- 配置中断或输出模式:如果需要中断处理或 PWM 输出,则进行相应的配置。
- 启动定时器:使能定时器。
示例代码: 配置通用定时器 TIM3 实现一个 1 秒的中断。
c复制代码TIM_HandleTypeDef htim3;// 1. 启用定时器时钟
__HAL_RCC_TIM3_CLK_ENABLE();// 2. 配置定时器
htim3.Instance = TIM3;
htim3.Init.Prescaler = 7999; // 预分频器
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 9999; // 自动重装载值
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htim3);// 3. 启用中断
HAL_TIM_Base_Start_IT(&htim3);// 中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if (htim->Instance == TIM3){// 定时器中断处理逻辑,例如切换 LED 状态}
}
7.3 PWM 输出
PWM(脉冲宽度调制)常用于控制电机、LED 调光等应用。通过改变占空比,可以控制输出信号的平均电压。
PWM 输出示例代码:
c复制代码// 配置定时器用于 PWM 输出
htim3.Init.Period = 1000; // PWM 频率
HAL_TIM_PWM_Init(&htim3);// 配置 PWM 通道
TIM_OC_InitTypeDef sConfigOC = {0};
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 500; // 设置占空比 50%
HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1);// 启动 PWM 输出
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);