SPI是一种串行,全双工,同步通信总线
我们先解释下这几个名词,其实在前面的IIC USART中都有出现
串行:要发的数据排成一排,一个(位)一个(位)的发,
并行:要发的数据排成一排(8个或者16、32),一排一起发
单工:单方向的接收或发送,比如电视
半双工:可以接也可以发,但是同一时间一方只能选择 接 或者 发,比如对讲机
全双工:收发信息是独立的,互不影响。比如电话
同步:发送方和接收方用相同频率的时钟(有时钟线 比如IIC 和本文的SPI)
异步:时钟并不是相同的,所以在每一次发送数据的时候,需要有开始位和停止位
有四根线MOSI(SDO) MISO(SDI) SCLK SS(CS) (可能还会存在地线GND)
MOSI: 主设备输出 从设备输入 最大速度复用推挽输出
MISO: 主设备输入 从设备输出 上下拉输入
SCLK: 时钟线 只能由主设备输出 最大速度复用推挽输出
NSS: 选择主从设备 每个从设备可以选择自己的NSS
SPI有四种工作方式:
通过配置CR1中的CPHA、CPOL从而选择不同的工作方式
CPOL = 1 空闲是高电平;CPOL = 0 空闲是低电平
CPHA = 0 第一个跳变时采样;CPHA = 1 第二个跳变时采样
(在本文后面的示例代码中选择第一种工作方式)
在SPI通信中,没有读和写的说法,它使用的是主从模式,你发出数据了,那就必然会收到数据回来,你要想收到数据,那你必须要先发出数据。
因为SPI是串行同步通信,所以需要有一根时钟线,去提供脉冲频率
在通信当中,时钟线的作用就类似于你军训时教官喊的口令121121,由时钟线告诉你,1 和 0 应该怎么发。
代码如下 这是cotex-M3使用的代码
/* 函数功能:SPI2接口初始化 */ void Spi2_Pin_Init(void) { //IO功能配置 RCC->APB2ENR |=0x01<<3; GPIOB->CRH &=~(0xFFF<<20); GPIOB->CRH |=(0xB8B<<20); RCC->APB1ENR |=1<<14; //SPI2时钟使能 SPI2->CR1 &=~(1<<11); //8位数据帧 SPI2->CR1 |=1<<9; // SPI2->CR1 |=1<<8; // SPI2->CR1 &=~(1<<7); //先发高位 SPI2->CR1 &=~(0x7<<3); SPI2->CR1 |=0x0<<3; //波特率 //从四种工作模式中选择一种 SPI2->CR1 |=1<<1; //时钟空闲状态为高电平 SPI2->CR1 |=1<<0; //在时钟第二个电平发送数据 SPI2->CR1 |=1<<2; //主SPI SPI2->CR1 |=1<<6; //使能SPI Spi2_Transfer_Byte(0xff); } /* 函数功能:主机发送一个字节给从机 注意事项:MODE0/3 */ void Spi2_Send_Byte(u8 data) { u8 i; GPIOB->ODR |= 0X1 <<13; //SCLK=1 for(i=0;i<8;i++) { GPIOB->ODR &= ~(0X1 <<13); //SCLK=0//准备数据 if(data&0x80) { GPIOB->ODR |= (0X1 <<15); //MOSI=1; } else { GPIOB->ODR &= ~(0X1 <<15) //MOSI=0; } GPIOB->ODR |= 0X1 <<13; //SCLK=1 data<<=1;//让次高位变成最高位 } } u8 Spi2_Transfer_Byte(u8 data) { while(!(SPI2->SR&(0x01<<1))); //TXE transmit buffer empty SPI2->DR=data; //在数据寄存器中写入数据 while(!(SPI2->SR&(0x01<<0))); //PXNE receive buffer not empty return SPI2->DR; }