下面这些图来自b站,理解了这几张图,基本就懂了SPI, ps:深入理解SPi通讯协议,5分钟看懂!_哔哩哔哩_bilibili
SPI:
- SCK = Serial Clock = This line is used for clock pulses to synchronize data transmission from Master devices
- MOSI = Master Out Slave In = This line is used for sending data from Master device to Slave devices.
- MISO = Master In Slave Out = This line is used for sending data from Slave devices to Master device.
- SS = Slave Select = This pin is used by Master device to enable and disable specific devices to communicate with only some number of devices out of all.
(时钟信号)
完整实验安装下文操作即可:
ps:SPI Communication Between Two Arduino Boards SPI Communication Between Two Arduino Boards - Arduino SPI Guide
主从的代码略改写了一下,增加了注释等: SPI.h? 内置的,可以直接用。
#include <SPI.h>
// 定义引脚号
#define PUSH_BUTTON 2
#define LED_PIN 4
#define SS_PIN 10 // 假设SS引脚连接到数字引脚10,这取决于你的硬件设置
// 声明变量
int pushButtonValue;
int ledState;
byte masterSend, masterReceive;
void setup() {
Serial.begin(115200); // 初始化串口通信
pinMode(PUSH_BUTTON, INPUT); // 设置按钮引脚为输入模式
pinMode(LED_PIN, OUTPUT); // 设置LED引脚为输出模式
pinMode(SS_PIN, OUTPUT); // 设置SS引脚为输出模式,用于控制从设备
SPI.begin(); // 初始化SPI总线
SPI.setClockDivider(SPI_CLOCK_DIV8); // 设置SPI时钟分频
digitalWrite(SS_PIN, HIGH); // 初始时将SS引脚拉高,防止SPI通信意外启动
}
void loop() {
// 读取按钮状态
pushButtonValue = digitalRead(PUSH_BUTTON);
// 根据按钮状态设置要发送的数据
if (pushButtonValue == HIGH) {
masterSend = 1; // 按钮被按下,发送1
} else {
masterSend = 0; // 按钮未被按下,发送0
}
// 拉低SS引脚,启动SPI通信
digitalWrite(SS_PIN, LOW);
// 通过SPI发送数据并接收从设备返回的数据
masterReceive = SPI.transfer(masterSend);
// 根据接收到的数据控制LED
if (masterReceive == 1) {
ledState = HIGH; // 如果从设备返回1,点亮LED
} else {
ledState = LOW; // 如果从设备返回0,熄灭LED
}
digitalWrite(LED_PIN, ledState); // 设置LED状态
// 延时一秒后再次检测按钮状态
delay(1000);
// 拉高SS引脚,结束SPI通信
digitalWrite(SS_PIN, HIGH);
}
从机:略
stm32:
在STM32上使用SPI(Serial Peripheral Interface)来读写存储芯片(如Flash, EEPROM等)是一个常见的任务。这里我将给出一个简化的示例,展示如何使用STM32 HAL库来配置SPI并读写一个简单的EEPROM(如AT24C系列)。请注意,虽然示例是针对EEPROM的,但大多数SPI设备的读写逻辑是类似的。
步骤 1: 初始化SPI
首先,你需要使用STM32CubeMX或手动配置SPI接口的相关参数(如时钟极性CPOL、时钟相位CPHA、数据位长度等)。以下是一个使用STM32 HAL库初始化SPI的基本示例。
#include "stm32f1xx_hal.h"
SPI_HandleTypeDef hspi1;
void MX_SPI1_Init(void)
{
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_16;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
HAL_SPI_Init(&hspi1);
}
步骤 2: 编写SPI读写函数
对于EEPROM,你可能需要发送设备地址和内存地址(取决于EEPROM的类型),然后读取或写入数据。
#define EEPROM_CS_GPIO_Port GPIOC
#define EEPROM_CS_Pin GPIO_PIN_13
void EEPROM_CS_LOW(void)
{
HAL_GPIO_WritePin(EEPROM_CS_GPIO_Port, EEPROM_CS_Pin, GPIO_PIN_RESET);
}
void EEPROM_CS_HIGH(void)
{
HAL_GPIO_WritePin(EEPROM_CS_GPIO_Port, EEPROM_CS_Pin, GPIO_PIN_SET);
}
// 发送一个字节
void EEPROM_WriteByte(uint8_t byte)
{
HAL_SPI_Transmit(&hspi1, &byte, 1, HAL_MAX_DELAY);
}
// 读取一个字节
uint8_t EEPROM_ReadByte(void)
{
uint8_t byte = 0xFF;
HAL_SPI_Receive(&hspi1, &byte, 1, HAL_MAX_DELAY);
return byte;
}
// 写入一个字节到EEPROM
void EEPROM_WriteData(uint16_t devAddress, uint16_t memAddress, uint8_t data)
{
// 根据EEPROM的协议发送设备地址和内存地址
// 这里只是一个示例,具体取决于EEPROM的数据手册
EEPROM_CS_LOW();
// 发送设备地址
EEPROM_WriteByte(0xA0); // 假设的设备地址和写指令
// 发送内存地址
EEPROM_WriteByte(memAddress >> 8);
EEPROM_WriteByte(memAddress & 0xFF);
// 发送数据
EEPROM_WriteByte(data);
EEPROM_CS_HIGH();
}
// 从EEPROM读取一个字节
uint8_t EEPROM_ReadData(uint16_t devAddress, uint16_t memAddress)
{
uint8_t data;
EEPROM_CS_LOW();
// 发送设备地址和读指令
EEPROM_WriteByte(0xA1); // 假设的设备地址和读指令
// 发送内存地址
EEPROM_WriteByte(memAddress >> 8);
EEPROM_WriteByte(memAddress & 0xFF);
// 读取数据
data = EEPROM_ReadByte();
EEPROM_CS_HIGH();
return data;
}
注意事项
- 上述代码示例基于假设的EEPROM设备地址和指令。你需要根据你所使用的EEPROM的数据手册来调整这些值。
- 确保在发送或接收数据之前,通过GPIO控制CS(Chip Select)引脚来选中EEPROM设备。
- 根据EEPROM的要求,可能需要额外的延时来等待操作完成。
- 以上代码未包含错误检查或处理,实际使用中应添加适当的错误处理逻辑。
总结
这是一个基本的STM32 SPI读写EEPROM的示例。你需要根据具体的硬件和EEPROM的数据手册来调整代码中的参数和指令。