STM32F103C8T6主从机SPI通信

SPI协议:

        SPI协议是串行外设接口的缩写,是美国摩托罗拉公司最先推出的一种同步串行传输规范,也是一种单片机外设芯片串行扩展接口,是一种高速、全双工、同步通信总线,所以可以在同一时间发送和接收数据,SPI没有定义速度限制,通常能达到甚至超过10M/bps。使用SPI通信需要四根线:MISO(主设备数据输入)、MOSI(主设备数据输出)、SCLK(时钟)和CS/SS(片选),CS/SS为从设备使能信号,由主设备控制。

准备两块STM32F103C8T6单片机,两条USB转TTL下载线

串口下载工具,串口调试工具

主机发送函数:

void SPI_Master_Init(void)
{
    //初始化GPIO引脚
    GPIO_InitTypeDef GPIO_InitStruct;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE);

    //配置SCK和MOSI引脚为复用推挽输出
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStruct);

    //配置MISO引脚为浮空输入
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA,&GPIO_InitStruct);

    //配置CS引脚为输出
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOA,&GPIO_InitStruct);

    //初始化SPI主机
    SPI_InitTypeDef SPI_InitStruct;
    SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //全双工模式
    SPI_InitStruct.SPI_Mode = SPI_Mode_Master; //主模式
    SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; //8位数据
    SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; //时钟极性为低电平
    SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; //时钟相位为第一个时钟沿
    SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;  //软件控制片选信号
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //波特率预分频值256
    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; //最高有效位先传输
    SPI_InitStruct.SPI_CRCPolynomial = 7; //设置CRC多项式
    SPI_Init(SPI1,&SPI_InitStruct); //SPI初始化

    SPI_Cmd(SPI1, ENABLE);  //使能SPI外设
}

void SPI_Master_Send(uint8_t data)
{
    //等待发送缓冲区为空
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
    //发送数据
    SPI_I2S_SendData(SPI1, data);
}

uint8_t SPI_Master_Receive(void)
{
    //等待接收缓冲区非空
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
    //读取接收数据
    return SPI_I2S_ReceiveData(SPI1);
}

从机接收函数:

//SPI从机初始化
void SPI_Slave_Init(void)
{
    // 初始化GPIO引脚
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE);

    // 配置SCK、MISO和MOSI引脚为复用推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置CS引脚为浮空输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    //初始化SPI主机
    SPI_InitTypeDef SPI_InitStruct;
    SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //全双工模式
    SPI_InitStruct.SPI_Mode = SPI_Mode_Slave; //从模式
    SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; //8位数据
    SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; //时钟极性为低电平
    SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; //时钟相位为第一个时钟沿
    SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;  //软件控制片选信号
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //波特率预分频值256
    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; //最高有效位先传输
    SPI_InitStruct.SPI_CRCPolynomial = 7; //设置CRC多项式
    SPI_Init(SPI1,&SPI_InitStruct); //SPI初始化

    SPI_Cmd(SPI1, ENABLE);  //使能SPI外设
}


void SPI_Slave_Receive(void)
{
    // 等待接收到数据
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
    // 读取接收缓冲区数据
    return SPI_I2S_ReceiveData(SPI1);
}

void SPI_Slave_Send(uint8_t data)
{
    //等待发送缓冲区为空
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
    //发送数据
    SPI_I2S_SendData(SPI1, data);
}

主机main函数测试

uint8_t sendData = 0x11;
uint8_t receivedData;
while(1)
{
	printf("我是主机\r\n");
    SPI_Master_Send(sendData);
    receivedData = SPI_Master_Receive();
    printf("主机发送的消息是:0x%02X\r\n", sendData);
    printf("接收从机的消息是:0x%02X\r\n", receivedData);
}

从机main函数测试

uint8_t sendData = 0x22;
uint8_t receivedData;
while(1)
{
	printf("我是从机\r\n");
    receivedData = SPI_Slave_Receive();
    printf("接收主机的消息是:0x%02X\r\n", receiveData);
    SPI_Slave_Send(sendData);
    printf("从机发送的消息是:0x%02X\r\n", senddData);
}

程序烧录好后,用杜邦线将主从机配置SPI的GPIO引脚相连,打开两个串口助手查看显示效果。

注意:

       1. 使用SPI主从机数据传输,如果在发送或接收数据后要添加延时函数,需要确保主从机延时一致,否则传递的数据容易发生漂移。 

        2.主机先发送,从机后接收。传递浮点型数据容易丢失小数位,可以考虑小数位放大倍数传递,或者转换成字符串的方式传递。

        SPI主从机通信,主要可以用于解决日常MCU产品设计中串口不够用,而设计成本有限不能更换芯片的问题,除了这种简单的方式,还可以使用软件模拟串口的方式,将GPIO口通过配置定时器来达到模拟串口通信的方式。

        本文仅供学习交流!

STM32F103C8T6是一款基于ARM Cortex-M3内核的微控制器单元,其强大的处理能力和丰富的外设资源备受广大工程师的喜爱。在STM32F103C8T6的外设资源中,SPI通信模块是应用较为广泛的一个模块。 SPI全称为Serial Peripheral Interface,是串行外设接口的一种,它采用Master-Slave方式进行通信,采用4线或者3线接口方式,可以最多支持8个从设备。SPI带有多路数据信号以及一路时钟信号,因此SPI通信模块的通信速率较高,比I2C等其他通信方式更加快速和稳定。 以下为一个简单的STM32F103C8T6 SPI通信例程: 1. 首先需要配置SPI外设: #include "stm32f10x.h" void Spi_Init(void) { GPIO_InitTypeDef GPIO_SPI_InitStruct; SPI_InitTypeDef SPI_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //打开GPIOA的时钟 GPIO_SPI_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; //SCK,MISO,MOSI GPIO_SPI_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; //推挽复用模式 GPIO_SPI_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //GPIO速度50MHz GPIO_Init(GPIOA, &GPIO_SPI_InitStruct); //初始化SPI的GPIOA RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); //SPI1时钟使能 SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; //时钟频率分频系数 SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; //CPHA=1,第二个边沿采样 SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; //CPOL=0,空闲为LOW SPI_InitStruct.SPI_CRCPolynomial = 7; //CRC多项式设置 SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; //数据位8位 SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //全双工模式 SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; //高位在前 SPI_InitStruct.SPI_Mode = SPI_Mode_Master; //主模式 SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; //软件控制 SPI_Init(SPI1, &SPI_InitStruct); //初始化SPI1 SPI_Cmd(SPI1, ENABLE); //SPI1使能 } 2. 编写SPI传输函数: void Spi_WriteByte(uint8_t data) { while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); //等待发送区空 SPI_I2S_SendData(SPI1, data); //发送数据 while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); //等待接收区不为空 SPI_I2S_ReceiveData(SPI1); //清除SPI_SR_RXNE位 } 3. 进行SPI通信: int main(void) { Spi_Init(); //初始化SPI while(1) { Spi_WriteByte(0x01); //向SPI总线发送数据0x01 } } 以上是一个简单的SPI通信例程。在具体应用中,需要根据具体的应用场景和需求进行修改和调整,以最大限度发挥STM32F103C8T6 SPI通信模块的性能和功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值