ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SPI通信原理

SPI通信原理 目录总述信号线通信模式和时序通信模式通信时序软件SPI初始化代码四种模拟SPICPOL 0, CPHA 0, first 模式下读写多字节数据总述SPI 是英语Serial Peripheral interface的缩写顾名思义就是串行外围设备接口。是Motorola(摩托罗拉)首先在其MC68HCXX系列处理器上定义的。SPI是一种高速的全双工同步的通信总线并且在芯片的管脚上只占用四根线节约了芯片的管脚同时为PCB的布局上节省空间提供方便主要应用在 EEPROMFLASH实时时钟AD转换器还有数字信号处理器和数字信号解码器之间。SPI是全双工且SPI没有定义速度限制一般的实现通常能达到甚至超过10 Mbps信号线SPI接口一般使用四条信号线通信SDI数据输入SDO数据输出SCK时钟CS片选MISO 主设备输入/从设备输出引脚。该引脚在从模式下发送数据在主模式下接收数据。MOSI 主设备输出/从设备输入引脚。该引脚在主模式下发送数据在从模式下接收数据。SCLK串行时钟信号由主设备产生。CS/SS从设备片选信号由主设备控制。它的功能是用来作为“片选引脚”也就是选择指定的从设备让主设备可以单独地与特定从设备通讯避免数据线上的冲突。通信模式和时序时钟极性(CPOL)定义了时钟空闲状态电平CPOL0表示当SCLK0时处于空闲态所以有效状态就是SCLK处于高电平时CPOL1表示当SCLK1时处于空闲态所以有效状态就是SCLK处于低电平时时钟相位(CPHA)定义数据的采集时间。CPHA0在时钟的第一个跳变沿上升沿或下降沿进行数据采样在第2个边沿发送数据CPHA1在时钟的第二个跳变沿上升沿或下降沿进行数据采样在第1个边沿发送数据通信模式Mode0CPOL0CPHA0此时空闲态时SCLK处于低电平数据采样是在第1个边沿也就是SCLK由低电平到高电平的跳变所以数据采样是在上升沿(准备数据发送数据数据发送是在下降沿。Mode1CPOL0CPHA1此时空闲态时SCLK处于低电平数据发送是在第1个边沿也就是SCK由低电平到高电平的跳变所以数据采样是在下降沿数据发送是在上升沿。Mode2CPOL1CPHA0此时空闲态时SCLK处于高电平数据采集是在第1个边沿也就是SCLK由高电平到低电平的跳变所以数据采集是在下降沿数据发送是在上升沿。Mode3CPOL1CPHA1此时空闲态时SCLK处于高电平数据发送是在第1个边沿也就是SCLK由高电平到低电平的跳变所以数据采集是在上升沿数据发送是在下降沿。通信时序软件SPI初始化代码/**SPI1 GPIO Configuration PA5 ------ SPI1_SCK PA6 ------ SPI1_MISO PA7 ------ SPI1_MOSI */ #define SPI_SCK_PIN GPIO_PIN_5 #define SPI_SCK_GPIO_PORT GPIOA #define SPI_MOSI_PIN GPIO_PIN_7 #define SPI_MOSI_GPIO_PORT GPIOA #define SPI_MISO_PIN GPIO_PIN_6 #define SPI_MISO_GPIO_PORT GPIOA #define SPI_NSS_PIN GPIO_PIN_14 #define SPI_NSS_GPIO_PORT GPIOD #define SPI_SCK_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define SPI_MISO_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define SPI_MOSI_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define SPI_NSS_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE() #define MOSI_H HAL_GPIO_WritePin(SPI_MOSI_GPIO_PORT, SPI_MOSI_PIN, GPIO_PIN_SET) #define MOSI_L HAL_GPIO_WritePin(SPI_MOSI_GPIO_PORT, SPI_MOSI_PIN, GPIO_PIN_RESET) #define SCK_H HAL_GPIO_WritePin(SPI_SCK_GPIO_PORT, SPI_SCK_PIN, GPIO_PIN_SET) #define SCK_L HAL_GPIO_WritePin(SPI_SCK_GPIO_PORT, SPI_SCK_PIN, GPIO_PIN_RESET) #define MISO HAL_GPIO_ReadPin(SPI_MISO_GPIO_PORT, SPI_MISO_PIN) #define NSS_H HAL_GPIO_WritePin(SPI_NSS_GPIO_PORT, SPI_NSS_PIN, GPIO_PIN_SET) #define NSS_L HAL_GPIO_WritePin(SPI_NSS_GPIO_PORT, SPI_NSS_PIN, GPIO_PIN_RESET) · void SPI_Init(void) { /*##-1- Enable peripherals and GPIO Clocks #########################*/ /* Enable GPIO TX/RX clock */ SPI_SCK_GPIO_CLK_ENABLE(); SPI_MISO_GPIO_CLK_ENABLE(); SPI_MOSI_GPIO_CLK_ENABLE(); SPI_NSS_GPIO_CLK_ENABLE(); /*##-2- Configure peripheral GPIO #######################*/ /* SPI SCK GPIO pin configuration */ GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin SPI_SCK_PIN; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; //GPIO_InitStruct.Pull GPIO_PULLDOWN; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_VERY_HIGH; HAL_GPIO_Init(SPI_SCK_GPIO_PORT, GPIO_InitStruct); HAL_GPIO_WritePin(SPI_SCK_GPIO_PORT, SPI_SCK_PIN, GPIO_PIN_SET); /* SPI MISO GPIO pin configuration */ GPIO_InitStruct.Pin SPI_MISO_PIN; GPIO_InitStruct.Mode GPIO_MODE_INPUT; HAL_GPIO_Init(SPI_MISO_GPIO_PORT, GPIO_InitStruct); /* SPI MOSI GPIO pin configuration */ GPIO_InitStruct.Pin SPI_MOSI_PIN; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; HAL_GPIO_Init(SPI_MOSI_GPIO_PORT, GPIO_InitStruct); HAL_GPIO_WritePin(SPI_MOSI_GPIO_PORT, SPI_MOSI_PIN, GPIO_PIN_SET); GPIO_InitStruct.Pin SPI_NSS_PIN; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; HAL_GPIO_Init(SPI_NSS_GPIO_PORT, GPIO_InitStruct); HAL_GPIO_WritePin(SPI_NSS_GPIO_PORT, SPI_NSS_PIN, GPIO_PIN_SET); }四种模拟SPI/* CPOL 0, CPHA 0, MSB first */ uint8_t SOFT_SPI_RW_MODE0(uint8_t write_dat) { uint8_t i, read_dat 0; // 初始化读取数据的变量 // 遍历处理每一个比特位8位对于 uint8_t 类型 for (i 0; i 8; i) { // 将最高位MSB数据输出到 MOSI 引脚 if (write_dat 0x80) MOSI_H; // 如果最高位是 1则设置 MOSI 为高电平 else MOSI_L; // 如果最高位是 0则设置 MOSI 为低电平 // 将数据字节向左移动一位为下一个数据位做准备 write_dat 1; // 延时可能是为了满足时序要求 delay_us(1); // 在 SCK时钟上升沿之前从设备准备采样 MOSI 的数据 // 这里省略了从设备采样的具体代码通常在硬件 SPI 中从设备会在 SCK 上升沿时采样 MOSI 的数据 // 将 SCK时钟置高主设备在此时刻采样 MISO 上的数据 SCK_H; // 从 MISO 输入的数据位被主设备读取到 read_dat 中 read_dat 1; // 主设备左移一位为下一个数据位做准备 if (MISO) read_dat; // 如果 MISO 为高电平则在 read_dat 中设置相应的位 // 延时可能是为了满足时序要求 delay_us(1); // 将 SCK时钟置低结束本次数据传输周期 SCK_L; // 空操作可能是为了处理时序 __nop(); } // 返回读取到的数据 return read_dat; } /* CPOL0CPHA1, MSB first */ uint8_t SOFT_SPI_RW_MODE1(uint8_t byte) { uint8_t i,Temp0; for(i0;i8;i) // 循环8次 { SCK_H; //拉高时钟 if(byte0x80) { MOSI_H; //若最到位为高则输出高 } else { MOSI_L; //若最到位为低则输出低 } byte 1; // 低一位移位到最高位 delay_us(1); SCK_L; //拉低时钟 Temp 1; //数据左移 if(MISO) Temp; //若从从机接收到高电平数据自加一 delay_us(1); } return (Temp); //返回数据 } /* CPOL1CPHA0, MSB first */ uint8_t SOFT_SPI_RW_MODE2(uint8_t byte) { uint8_t i,Temp0; for(i0;i8;i) // 循环8次 { if(byte0x80) { MOSI_H; //若最到位为高则输出高 } else { MOSI_L; //若最到位为低则输出低 } byte 1; // 低一位移位到最高位 delay_us(1); SCK_L; //拉低时钟 Temp 1; //数据左移 if(MISO) Temp; //若从从机接收到高电平数据自加一 delay_us(1); SCK_H; //拉高时钟 } return (Temp); //返回数据 } /* CPOL 1, CPHA 1, MSB first */ uint8_t SOFT_SPI_RW_MODE3( uint8_t write_dat ) { uint8_t i, read_dat; for( i 0; i 8; i ) { SCK_L; if( write_dat 0x80 ) MOSI_H; else MOSI_L; write_dat 1; delay_us(1); SCK_H; read_dat 1; if( MISO ) read_dat; delay_us(1); __nop(); } return read_dat; }CPOL 0, CPHA 0, first 模式下读写多字节数据/* u16Len: 读写的数据长度 pu8Dat 写入的数据 pu8RxBuf 读取的数据 */ int soft_spi_trx_bytes ( uint16_t u16Len, uint8_t* pu8Dat, uint8_t* pu8RxBuf) { uint16_t i 0; if (( NULL pu8Dat ) || ( 0 u16Len )) { return 1; } for ( i 0; i u16Len; i ) { pu8RxBuf[i]SOFT_SPI_RW_MODE0 ( pu8Dat[i]); } return 0; }
返回列表