STMicroelectronics 系列:STM32L1 系列_(10).STM32L1系列SPI通信
STM32L1系列SPI通信
1. SPI通信简介
1.1 SPI通信基本概念
SPI(Serial Peripheral Interface)是一种同步串行通信接口,由摩托罗拉公司开发。SPI允许一个主设备和一个或多个从设备之间进行高速、全双工数据传输。SPI通信通常使用四条线:MISO(Master In Slave Out,主设备输入从设备输出)、MOSI(Master Out Slave In,主设备输出从设备输入)、SCLK(Serial Clock,串行时钟)和CS(Chip Select,片选信号)。
1.2 SPI通信工作模式
SPI通信有四种工作模式,由时钟极性(CPOL)和时钟相位(CPHA)决定:
模式0:CPOL = 0, CPHA = 0
模式1:CPOL = 0, CPHA = 1
模式2:CPOL = 1, CPHA = 0
模式3:CPOL = 1, CPHA = 1
这些模式决定了数据在时钟的上升沿或下降沿进行采样,以及时钟空闲状态为高电平或低电平。
2. STM32L1系列SPI硬件特性
2.1 SPI模块概述
STM32L1系列微控制器内置多个SPI模块,每个模块都支持全双工和半双工通信模式,可以配置为主设备或从设备。SPI模块还支持多种数据格式和通信速率,适用于各种嵌入式应用。
2.2 SPI引脚配置
STM32L1系列的SPI模块通常使用以下引脚:
MISO:主设备输入从设备输出
MOSI:主设备输出从设备输入
SCLK:串行时钟
NSS:片选信号(也称为CS)
这些引脚可以映射到不同的GPIO端口上,具体映射关系可以在数据手册中找到。
2.3 SPI时钟配置
SPI通信的时钟频率由主设备控制,可以通过配置SPI模块的时钟分频器来调整。STM32L1系列的SPI模块支持的最高时钟频率通常为18 MHz。
// 配置SPI时钟分频器
void SPI_ConfigClock(SPI_HandleTypeDef *hspi, uint32_t prescaler) {
hspi->Instance->CR1 &= ~SPI_CR1_BR; // 清除时钟分频寄存器
hspi->Instance->CR1 |= (prescaler & SPI_CR1_BR); // 设置新的时钟分频值
}
2.4 SPI数据格式
SPI模块支持多种数据格式,包括8位和16位数据传输。数据格式可以通过配置SPI模块的数据帧格式寄存器来设置。
// 配置SPI数据格式
void SPI_ConfigDataFormat(SPI_HandleTypeDef *hspi, uint32_t dataFormat) {
hspi->Instance->CR1 &= ~SPI_CR1_DFF; // 清除数据格式寄存器
hspi->Instance->CR1 |= (dataFormat & SPI_CR1_DFF); // 设置新的数据格式
}
3. STM32L1系列SPI软件配置
3.1 STM32CubeMX配置
使用STM32CubeMX可以方便地配置SPI模块。以下是配置步骤:
-
打开STM32CubeMX并创建一个新的项目。
-
选择STM32L1系列的微控制器。
-
在“Pinout & Configuration”选项卡中,找到SPI模块并点击配置。
-
选择SPI模式(主设备或从设备)、数据格式(8位或16位)、时钟频率等参数。
-
生成初始化代码并导入到您的开发环境中。
3.2 HAL库初始化
使用STM32 HAL库初始化SPI模块的代码示例如下:
// 包含必要的头文件
#include "stm32l1xx_hal.h"
// 定义SPI句柄
SPI_HandleTypeDef hspi1;
// 初始化SPI
void SPI_Init(void) {
// 配置SPI1的时钟
__HAL_RCC_SPI1_CLK_ENABLE();
// 配置SPI1的GPIO
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; // SPI1 SCLK, MISO, MOSI
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF0_SPI1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置SPI1的NSS引脚
GPIO_InitStruct.Pin = GPIO_PIN_4; // SPI1 NSS
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置SPI1
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi1) != HAL_OK) {
// 初始化失败处理
Error_Handler();
}
}
// 错误处理函数
void Error_Handler(void) {
// 错误处理代码
while (1) {
// 无限循环等待
}
}
3.3 SPI数据传输
使用HAL库进行SPI数据传输的代码示例如下:
// 发送8位数据
void SPI_Send8BitData(uint8_t data) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // 使能NSS信号
if (HAL_SPI_Transmit(&hspi1, &data, 1, HAL_MAX_DELAY) != HAL_OK) {
// 发送失败处理
Error_Handler();
}
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // 禁用NSS信号
}
// 接收8位数据
uint8_t SPI_Receive8BitData(void) {
uint8_t data;
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // 使能NSS信号
if (HAL_SPI_Receive(&hspi1, &data, 1, HAL_MAX_DELAY) != HAL_OK) {
// 接收失败处理
Error_Handler();
}
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // 禁用NSS信号
return data;
}
// 发送和接收8位数据
void SPI_Transfer8BitData(uint8_t *txData, uint8_t *rxData, uint16_t size) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // 使能NSS信号
if (HAL_SPI_TransmitReceive(&hspi1, txData, rxData, size, HAL_MAX_DELAY) != HAL_OK) {
// 传输失败处理
Error_Handler();
}
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // 禁用NSS信号
}
3.4 SPI中断配置
使用中断进行SPI数据传输可以提高系统的响应速度。以下是配置SPI中断的代码示例:
// 配置SPI中断
void SPI_ConfigInterrupt(void) {
// 使能SPI1中断
HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(SPI1_IRQn);
}
// SPI中断处理函数
void SPI1_IRQHandler(void) {
HAL_SPI_IRQHandler(&hspi1);
}
// SPI传输完成回调函数
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) {
// 传输完成处理
if (hspi->Instance == SPI1) {
// 例如,启动下一个传输
SPI_Send8BitData(0x55);
}
}
// SPI接收完成回调函数
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) {
// 接收完成处理
if (hspi->Instance == SPI1) {
// 例如,处理接收到的数据
uint8_t receivedData = SPI_Receive8BitData();
// 处理接收到的数据
}
}
3.5 DMA配置
使用DMA进行SPI数据传输可以进一步提高传输效率。以下是配置SPI DMA的代码示例:
// 包含必要的头文件
#include "stm32l1xx_hal.h"
#include "stm32l1xx_hal_spi.h"
// 定义SPI句柄
SPI_HandleTypeDef hspi1;
DMA_HandleTypeDef hdma_spi1_tx;
DMA_HandleTypeDef hdma_spi1_rx;
// 初始化SPI和DMA
void SPI_InitDMA(void) {
// 配置SPI1的时钟
__HAL_RCC_SPI1_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();
// 配置SPI1的GPIO
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; // SPI1 SCLK, MISO, MOSI
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF0_SPI1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置SPI1的NSS引脚
GPIO_InitStruct.Pin = GPIO_PIN_4; // SPI1 NSS
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置SPI1
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi1) != HAL_OK) {
// 初始化失败处理
Error_Handler();
}
// 配置DMA
__HAL_LINKDMA(&hspi1, hdmarx, hdma_spi1_rx);
__HAL_LINKDMA(&hspi1, hdmatx, hdma_spi1_tx);
// 配置DMA通道
hdma_spi1_rx.Instance = DMA1_Channel2;
hdma_spi1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_spi1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi1_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_spi1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_spi1_rx.Init.Mode = DMA_NORMAL;
hdma_spi1_rx.Init.Priority = DMA_PRIORITY_HIGH;
if (HAL_DMA_Init(&hdma_spi1_rx) != HAL_OK) {
// 初始化失败处理
Error_Handler();
}
hdma_spi1_tx.Instance = DMA1_Channel3;
hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_spi1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi1_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_spi1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_spi1_tx.Init.Mode = DMA_NORMAL;
hdma_spi1_tx.Init.Priority = DMA_PRIORITY_HIGH;
if (HAL_DMA_Init(&hdma_spi1_tx) != HAL_OK) {
// 初始化失败处理
Error_Handler();
}
// 使能DMA中断
HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);
HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);
// 使能SPI DMA传输
__HAL_SPI_ENABLE_DMA(&hspi1, SPI_DMA_TX | SPI_DMA_RX);
}
// DMA传输完成回调函数
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) {
// 传输完成处理
if (hspi->Instance == SPI1) {
// 例如,处理传输完成后的数据
uint8_t receivedData[10];
// 处理接收到的数据
}
}
// DMA1传输完成中断处理函数
void DMA1_Channel2_IRQHandler(void) {
HAL_DMA_IRQHandler(&hdma_spi1_rx);
}
// DMA1传输完成中断处理函数
void DMA1_Channel3_IRQHandler(void) {
HAL_DMA_IRQHandler(&hdma_spi1_tx);
}
3.6 SPI通信实例
3.6.1 主设备发送数据到从设备
// 发送数据到从设备
void SPI_MasterTx(void) {
uint8_t txData[] = {0x01, 0x02, 0x03, 0x04};
uint8_t rxData[4];
SPI_InitDMA(); // 初始化SPI和DMA
// 发送数据
if (HAL_SPI_TransmitReceive_DMA(&hspi1, txData, rxData, 4) != HAL_OK) {
// 传输失败处理
Error_Handler();
}
// 等待传输完成
while (HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY) {
// 等待
}
// 处理接收到的数据
for (int i = 0; i < 4; i++) {
// 处理rxData[i]
}
}
3.6.2 从设备接收数据
// 从设备接收数据
void SPI_SlaveRx(void) {
uint8_t rxData[4];
SPI_InitDMA(); // 初始化SPI和DMA
// 接收数据
if (HAL_SPI_Receive_DMA(&hspi1, rxData, 4) != HAL_OK) {
// 接收失败处理
Error_Handler();
}
// 等待接收完成
while (HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY) {
// 等待
}
// 处理接收到的数据
for (int i = 0; i < 4; i++) {
// 处理rxData[i]
}
}
4. STM32L1系列SPI应用实例
4.1 与外部SPI从设备通信
4.1.1 读取外部SPI从设备的数据
// 读取外部SPI从设备的数据
void SPI_ReadExternalDevice(uint8_t *rxData, uint16_t size) {
uint8_t txData[size]; // 用于发送的虚拟数据
for (int i = 0; i < size; i++) {
txData[i] = 0x00; // 可以根据需要设置虚拟数据
}
SPI_InitDMA(); // 初始化SPI和DMA
// 发送和接收数据
if (HAL_SPI_TransmitReceive_DMA(&hspi1, txData, rxData, size) != HAL_OK) {
// 传输失败处理
Error_Handler();
}
// 等待传输完成
while (HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY) {
// 等待
}
// 处理接收到的数据
for (int i = 0; i < size; i++) {
// 处理rxData[i]
}
}
4.1.2 写入数据到外部SPI从设备
// 写入数据到外部SPI从设备
void SPI_WriteExternalDevice(uint8_t *txData, uint16_t size) {
uint8_t rxData[size]; // 用于接收的虚拟数据
SPI_InitDMA(); // 初始化SPI和DMA
// 发送和接收数据
if (HAL_SPI_TransmitReceive_DMA(&hspi1, txData, rxData, size) != HAL_OK) {
// 传输失败处理
Error_Handler();
}
// 等待传输完成
while (HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY) {
// 等待
}
// 处理接收到的数据
for (int i = 0; i < size; i++) {
// 处理rxData[i]
}
}
4.2 SPI通信调试技巧
4.2.1 使用逻辑分析仪
逻辑分析仪是调试SPI通信的有效工具。它可以捕获SPI通信的波形,帮助您验证时钟和数据信号的正确性。连接逻辑分析仪的探针到SPI的SCLK、MISO、MOSI和NSS引脚,您可以观察到实际的通信波形。这有助于识别信号时序问题、噪声干扰或其他通信错误。
4.2.2 使用串行终端
如果您的SPI从设备支持串行通信,可以使用串行终端(如PUTTY或Tera Term)来调试。通过串行终端,您可以发送命令或数据到从设备,并查看从设备的响应。这有助于验证数据传输的正确性和从设备的工作状态。
4.3 示例应用:与SPI Flash存储器通信
4.3.1 初始化SPI Flash
// 初始化SPI Flash
void SPI_Flash_Init(void) {
// 初始化SPI
SPI_Init();
// 发送复位命令
uint8_t resetCmd = 0x66; // 复位命令
if (HAL_SPI_Transmit(&hspi1, &resetCmd, 1, HAL_MAX_DELAY) != HAL_OK) {
Error_Handler();
}
// 读取状态寄存器
uint8_t statusCmd = 0x05; // 读取状态寄存器命令
uint8_t statusData;
if (HAL_SPI_TransmitReceive(&hspi1, &statusCmd, &statusData, 1, HAL_MAX_DELAY) != HAL_OK) {
Error_Handler();
}
// 检查状态寄存器
if (statusData != 0x00) {
// 状态寄存器错误处理
Error_Handler();
}
}
4.3.2 读取SPI Flash数据
// 读取SPI Flash数据
void SPI_Flash_Read(uint32_t address, uint8_t *rxData, uint16_t size) {
uint8_t cmd[4];
cmd[0] = 0x03; // 读取命令
cmd[1] = (address >> 16) & 0xFF; // 高字节
cmd[2] = (address >> 8) & 0xFF; // 中字节
cmd[3] = address & 0xFF; // 低字节
SPI_InitDMA(); // 初始化SPI和DMA
// 发送读取命令和地址
if (HAL_SPI_Transmit(&hspi1, cmd, 4, HAL_MAX_DELAY) != HAL_OK) {
Error_Handler();
}
// 接收数据
if (HAL_SPI_Receive_DMA(&hspi1, rxData, size) != HAL_OK) {
Error_Handler();
}
// 等待接收完成
while (HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY) {
// 等待
}
}
4.3.3 写入数据到SPI Flash
// 写入数据到SPI Flash
void SPI_Flash_Write(uint32_t address, uint8_t *txData, uint16_t size) {
uint8_t cmd[4];
cmd[0] = 0x02; // 写入命令
cmd[1] = (address >> 16) & 0xFF; // 高字节
cmd[2] = (address >> 8) & 0xFF; // 中字节
cmd[3] = address & 0xFF; // 低字节
SPI_InitDMA(); // 初始化SPI和DMA
// 发送写入命令和地址
if (HAL_SPI_Transmit(&hspi1, cmd, 4, HAL_MAX_DELAY) != HAL_OK) {
Error_Handler();
}
// 发送数据
if (HAL_SPI_Transmit_DMA(&hspi1, txData, size) != HAL_OK) {
Error_Handler();
}
// 等待传输完成
while (HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY) {
// 等待
}
// 等待SPI Flash写入完成
while (SPI_Flash_isBusy()) {
// 等待
}
}
// 检查SPI Flash是否忙
uint8_t SPI_Flash_isBusy(void) {
uint8_t statusCmd = 0x05; // 读取状态寄存器命令
uint8_t statusData;
// 读取状态寄存器
if (HAL_SPI_TransmitReceive(&hspi1, &statusCmd, &statusData, 1, HAL_MAX_DELAY) != HAL_OK) {
Error_Handler();
}
// 检查WIP位(写入进行位)
return (statusData & 0x01) ? 1 : 0;
}
4.4 示例应用:与SPI ADC通信
4.4.1 初始化SPI ADC
// 初始化SPI ADC
void SPI_ADC_Init(void) {
// 初始化SPI
SPI_Init();
// 配置ADC的片选引脚
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_10; // 假设ADC的NSS引脚连接到PA10
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 使能NSS信号
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET);
}
4.4.2 读取ADC数据
// 读取ADC数据
uint16_t SPI_ADC_Read(void) {
uint16_t rxData;
// 使能NSS信号
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_RESET);
// 发送读取命令
uint8_t readCmd = 0x01; // 读取命令
if (HAL_SPI_Transmit(&hspi1, &readCmd, 1, HAL_MAX_DELAY) != HAL_OK) {
Error_Handler();
}
// 接收数据
if (HAL_SPI_Receive(&hspi1, (uint8_t *)&rxData, 2, HAL_MAX_DELAY) != HAL_OK) {
Error_Handler();
}
// 禁用NSS信号
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET);
return rxData;
}
4.4.3 写入ADC配置
// 写入ADC配置
void SPI_ADC_WriteConfig(uint8_t config) {
// 使能NSS信号
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_RESET);
// 发送写入命令
uint8_t writeCmd = 0x02; // 写入命令
if (HAL_SPI_Transmit(&hspi1, &writeCmd, 1, HAL_MAX_DELAY) != HAL_OK) {
Error_Handler();
}
// 发送配置数据
if (HAL_SPI_Transmit(&hspi1, &config, 1, HAL_MAX_DELAY) != HAL_OK) {
Error_Handler();
}
// 禁用NSS信号
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET);
}
4.5 示例应用:与SPI OLED显示屏通信
4.5.1 初始化SPI OLED显示屏
// 初始化SPI OLED显示屏
void SPI_OLED_Init(void) {
// 初始化SPI
SPI_Init();
// 配置OLED的片选引脚
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_11; // 假设OLED的NSS引脚连接到PA11
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置OLED的数据/命令选择引脚
GPIO_InitStruct.Pin = GPIO_PIN_12; // 假设OLED的D/C引脚连接到PA12
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 使能NSS信号
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_SET);
// 发送初始化命令
uint8_t initCmds[] = {
0xAE, // 关闭显示
0x20, // 设置内存模式
0x00, // 0x00水平地址模式
0xA1, // 设置段重映射
0xC8, // 设置COM输出扫描方向
0xA8, // 设置多重复用比率
0x3F, // 1/64复用
0xD3, // 设置显示偏移
0x00, // 无偏移
0xD5, // 设置显示时钟分频
0x80, // 时钟频率
0xD9, // 设置预充电周期
0xF1, // 预充电周期
0xDA, // 设置COM引脚硬件配置
0x12, // 交替COM配置
0xDB, // 设置VCOMH去耦
0x40, // VCOMH去耦
0x8D, // 开启电荷泵
0x14, // 开启电荷泵
0xAF // 打开显示
};
// 发送初始化命令
for (int i = 0; i < sizeof(initCmds); i++) {
SPI_OLED_SendCommand(initCmds[i]);
}
}
// 发送命令到OLED
void SPI_OLED_SendCommand(uint8_t cmd) {
// 选择命令模式
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET);
// 使能NSS信号
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_RESET);
// 发送命令
if (HAL_SPI_Transmit(&hspi1, &cmd, 1, HAL_MAX_DELAY) != HAL_OK) {
Error_Handler();
}
// 禁用NSS信号
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_SET);
}
// 发送数据到OLED
void SPI_OLED_SendData(uint8_t *data, uint16_t size) {
// 选择数据模式
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_SET);
// 使能NSS信号
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_RESET);
// 发送数据
if (HAL_SPI_Transmit(&hspi1, data, size, HAL_MAX_DELAY) != HAL_OK) {
Error_Handler();
}
// 禁用NSS信号
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_SET);
}
4.6 总结
通过以上示例,您可以了解如何在STM32L1系列微控制器上使用SPI模块进行通信。无论是与外部存储器、ADC还是OLED显示屏通信,关键步骤都包括初始化SPI模块、配置引脚、设置时钟和数据格式,以及使用中断或DMA进行高效的数据传输。调试时,可以使用逻辑分析仪和串行终端来帮助验证通信的正确性。
作者:kkchenkx