赞
踩
目录
1、并行通信/串行通信
2、异步通信/同步通信
3、半双工通信/全双工通信
并行通信:传输速度快,一次传输8bit,但是通信成本高,需要8个独立的通道,另外不支持长距离传输。用于打印机和扫描仪等设备,例如DB-25接口。
串行通信:传输速度慢,成本低,支持长距离传输,是计算机通信的主要方式,例如DB-接口。
异步通信:用于低速设备,会有更高的误码率。
同步通信:用于高速设备传输,同步传输有同步时钟为节拍进行传输数据。
单工通信:发送机只能给接收机发送数据,不允许从接收机发送给发送机。
半双工通信:发送机和接收机可以相互读写通信,但不能同时读写。
全双工工信:发送机和接收机可以相互读写通信,且能同时读写。
UART(Universal Asynchronous Receiver Transmitter),具有串行通信、异步通信、全双工通信的特点,两线制(TX, RX),传输速度慢,点对点的异步通信,一般用于RJ45 Console、打印机等。
UART工作原理:
发送器UART1从发送端数据总线接收到并行数据,将起始位、校验位和停止位添加到数据帧中,打包成数据包;然后将数据包以串行方式发送给接收器UART2;UART2以预配置的波特率对数据进行采样,将数据包还原成数据帧;最后UART2将数据帧串行转并传输给接收端的数据总线。
I2C(Inter-Integrated Circuit),具有串行通信、同步通信、半双工通信的特点,两线制(SDA, SCLK),用于监控、存储和数字信号处理器等。
I2C是两线制(SDA, SCLK),通过上拉电阻接到电源线,总线空闲时,SDA,SCLK都保持高电平。
I2C的数据传输过程:
Step1: I2C总线空闲时,上拉电阻使SDA, SCLK处于高电平。
Step2:Master发送start后,将SDA由高电平切换成低电平,然后SCLK线也由高电平切换成低电平。
Step3: Master在发送start后,再发送 slave的地址和读/写的命令,其中write是0,read是1,slave收到地址和读写命令后,向master回复ASK。
Step4:Master收到ASK后,再发送特定寄存器的地址,slave收到后回复master ASK。
Step5:Master再次收到ASK后,再像特定的寄存器发送8bit数据,slave收到数据后回复ASK,重复这一动作直至数据发完。
Step6:Master收到stop,SCLK由低电平切换成高电平,随后SDA也从低电平切换成高电平。
SPI(Serial Peripheral Interface),具有串行通信、同步通信、全双工通信的特点,四线制(CS, SCLK, MOSI, MISO),传输速度快,时许同步准确,一般用于存储器、数字信号处理、传感器和语音识别等。
- /*******************************************************************************
- * 函 数 名 : iic_start
- * 函数功能 : 产生IIC起始信号
- * 输 入 : 无
- * 输 出 : 无
- *******************************************************************************/
- void iic_start(void)
- {
- IIC_SDA=1;//如果把该条语句放在SCL后面,第二次读写会出现问题
- delay_10us(1);
- IIC_SCL=1;
- delay_10us(1);
- IIC_SDA=0; //当SCL为高电平时,SDA由高变为低
- delay_10us(1);
- IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
- delay_10us(1);
- }
- void iic_start(void)
- {
- IIC_SDA=1;//如果把该条语句放在SCL后面,第二次读写会出现问题
- delay_10us(1);
- IIC_SCL=1;
- delay_10us(1);
- IIC_SDA=0; //当SCL为高电平时,SDA由高变为低
- delay_10us(1);
- IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
- delay_10us(1);
- }
-
- /*******************************************************************************
- * 函 数 名 : iic_stop
- * 函数功能 : 产生IIC停止信号
- * 输 入 : 无
- * 输 出 : 无
- *******************************************************************************/
- void iic_stop(void)
- {
- IIC_SDA=0;//如果把该条语句放在SCL后面,第二次读写会出现问题
- delay_10us(1);
- IIC_SCL=1;
- delay_10us(1);
- IIC_SDA=1; //当SCL为高电平时,SDA由低变为高
- delay_10us(1);
- }
-
- /*******************************************************************************
- * 函 数 名 : iic_ack
- * 函数功能 : 产生ACK应答
- * 输 入 : 无
- * 输 出 : 无
- *******************************************************************************/
- void iic_ack(void)
- {
- IIC_SCL=0;
- IIC_SDA=0; //SDA为低电平
- delay_10us(1);
- IIC_SCL=1;
- delay_10us(1);
- IIC_SCL=0;
- }
-
- /*******************************************************************************
- * 函 数 名 : iic_nack
- * 函数功能 : 产生NACK非应答
- * 输 入 : 无
- * 输 出 : 无
- *******************************************************************************/
- void iic_nack(void)
- {
- IIC_SCL=0;
- IIC_SDA=1; //SDA为高电平
- delay_10us(1);
- IIC_SCL=1;
- delay_10us(1);
- IIC_SCL=0;
- }
-
- /*******************************************************************************
- * 函 数 名 : iic_wait_ack
- * 函数功能 : 等待应答信号到来
- * 输 入 : 无
- * 输 出 : 1,接收应答失败
- 0,接收应答成功
- *******************************************************************************/
- u8 iic_wait_ack(void)
- {
- u8 time_temp=0;
-
- IIC_SCL=1;
- delay_10us(1);
- while(IIC_SDA) //等待SDA为低电平
- {
- time_temp++;
- if(time_temp>100)//超时则强制结束IIC通信
- {
- iic_stop();
- return 1;
- }
- }
- IIC_SCL=0;
- return 0;
- }
- /*******************************************************************************
- * 函 数 名 : iic_write_byte
- * 函数功能 : IIC发送一个字节
- * 输 入 : dat:发送一个字节
- * 输 出 : 无
- *******************************************************************************/
- void iic_write_byte(u8 dat)
- {
- u8 i=0;
-
- IIC_SCL=0;
- for(i=0;i<8;i++) //循环8次将一个字节传出,先传高再传低位
- {
- if((dat&0x80)>0)
- IIC_SDA=1;
- else
- IIC_SDA=0;
- dat<<=1;
- delay_10us(1);
- IIC_SCL=1;
- delay_10us(1);
- IIC_SCL=0;
- delay_10us(1);
- }
- }
-
- /*******************************************************************************
- * 函 数 名 : iic_read_byte
- * 函数功能 : IIC读一个字节
- * 输 入 : ack=1时,发送ACK,ack=0,发送nACK
- * 输 出 : 应答或非应答
- *******************************************************************************/
- u8 iic_read_byte(u8 ack)
- {
- u8 i=0,receive=0;
-
- for(i=0;i<8;i++ ) //循环8次将一个字节读出,先读高再传低位
- {
- IIC_SCL=0;
- delay_10us(1);
- IIC_SCL=1;
- receive<<=1;
- if(IIC_SDA)receive++;
- delay_10us(1);
- }
- if (!ack)
- iic_nack();
- else
- iic_ack();
-
- return receive;
- }
PA4和PA2分别作为SCL和SDA。
- void MyI2C_Init(void)
- {
- //开启时钟
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
-
- /*GPIO初始化*/
- GPIO_InitTypeDef GPIO_InitStructure;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_2;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOA, &GPIO_InitStructure); //将PB10和PB11引脚初始化为开漏输出
-
- /*设置默认电平*/
- GPIO_SetBits(GPIOA, GPIO_Pin_4 | GPIO_Pin_2); //设置PB10和PB11引脚初始化后默
- }
- void MyI2C_W_SCL(uint8_t BitValue)
- {
- GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue); //根据BitValue,设置SCL引脚的电平
- Delay_us(10); //延时10us,防止时序频率超过要求
- }
- void MyI2C_W_SDA(uint8_t BitValue)
- {
- GPIO_WriteBit(GPIOA, GPIO_Pin_2, (BitAction)BitValue);
- Delay_us(10);
- }
- uint8_t MyI2C_R_SDA(void)
- {
- uint8_t BitValue;
- BitValue = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_2); //读取SDA电平
- Delay_us(10); //延时10us,防止时序频率超过要求
- return BitValue; //返回SDA电平
- }
- void MyI2C_Start(void)
- {
- MyI2C_W_SCL(1);
- MyI2C_W_SDA(1);
- MyI2C_W_SDA(0);
- MyI2C_W_SCL(0);
- }
- void MyI2C_Stop(void)
- {
- MyI2C_W_SDA(0);
- MyI2C_W_SCL(1);
- MyI2C_W_SDA(1);
- }
- void MyI2C_SendByte(uint8_t Byte)
- {
- uint8_t i;
- for(i=0;i<8;i++)
- {
- MyI2C_W_SDA(Byte&0x80>>i);
- MyI2C_W_SCL(1);
- MyI2C_W_SCL(0);
- }
- }
- uint8_t MyI2C_ReceiveByte(void)
- {
- uint8_t i,Byte=0x00;
- MyI2C_W_SDA(1);
- for(i=0;i<8;i++)
- {
- MyI2C_W_SCL(1);
- if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}
- MyI2C_W_SCL(0);
- }
- return Byte;
- }
- void MyI2C_SendAck(uint8_t AckBit)
- {
- MyI2C_W_SDA(AckBit);
- MyI2C_W_SCL(1);
- MyI2C_W_SCL(0);
- }
- uint8_t MyI2C_ReceiveAck(void)
- {
- uint8_t AckBit;
- MyI2C_W_SDA(1);
- MyI2C_W_SCL(1);
- AckBit= MyI2C_R_SDA();
- MyI2C_W_SCL(0);
- return AckBit;
- }
51单片机中没有硬件I2C的功能,只有STM32中有,这里只讲解STM32中的I2C功能。
通信引脚:
数据逻辑控制:
主接收器:
主接收器接收流程及事件说明如下:
I2C初始化结构体:
- typedef struct
- {
- uint32_t I2C_ClockSpeed; /*!< 设置 SCL 时钟频率,此值要低于 400000*/
- uint16_t I2C_Mode; /*!< 指定工作模式,可选 I2C 模式及 SMBUS 模式 */
- uint16_t I2C_DutyCycle; /* 指定时钟占空比,可选 low/high = 2:1 及 16:9 模式*/
- uint16_t I2C_OwnAddress1; /*!< 指定自身的 I2C 设备地址 */
- uint16_t I2C_Ack; /*!< 使能或关闭响应 (一般都要使能) */
- uint16_t I2C_AcknowledgedAddress; /*!< 指定地址的长度,可为 7 位及 10 位 */
- } I2C_InitTypeDef;
配置完这些结构体成员值,调用库函数 I2C_Init 即可把结构体的配置写入到寄存器中。
说白了,硬件I2C就是帮我们完成了I2C协议的基本时序,但是如何实现通信,需要我们把他进行组合,不断检测标志位,判断通信到了哪一步,然后我们在调用硬件I2C的函数,一般是清楚标志位、发送开始时序,发送结束时序,发送非应答,接收数据(读取SR)、发送数据(写入SR)生成对应的时序,继续通信。
这里可以和DMA模块一起复用,通过DMA,可以不断的发送数据,这里可以粗略的讲解一下,我们知道,当I2C硬件完成发送一个数据之后,相应的标志位会被置位,我们在设置相应DMA触发事件,就可以不断的往SR中搬运数据,实现连续发送数据,读取也是一样。
1、前者时序的搭建,需要CPU的参与,不断的改变引脚电平,来满足时序的要求。
2、后者不需要CPU的参与,大大节省了CPU的资源。
3、他们的波形也有些许差别。
软件:
硬件:
仔细观察,可以发现,当SCL拉低的那一瞬间之后,软件的SDA没有立即改变,而硬件控制的SDA则迅速反应,这是因为软件完成时序是通过函数改变引脚电平,这段时间会有点延时。
在单片机使用的串口通讯中,一般只使用 RXD、TXD 以及 GND 三条信号线,直接传输数据信号。
这里需要用到CH340USB转TTL模块。
CH340是一种USB转串口芯片,常用于单片机与计算机之间的串口通信。它的主要作用是将计算机的USB接口转换为串行通信接口,从而实现计算机与单片机之间的数据传输。
在单片机开发中,通常需要通过串口与计算机进行通信,以实现数据的传输、调试和监控等功能。而现代计算机通常只提供USB接口而不再配备传统的串口接口,因此需要通过USB转串口芯片来连接单片机和计算机。
CH340芯片具有成本低廉、稳定可靠、兼容性好等优点,因此被广泛应用于单片机开发中。它提供了简单易用的串口通信功能,使得开发者可以轻松实现单片机与计算机之间的数据交换。
在数据包的起始位之后紧接着的就是要传输的主体数据内容,也称为有效数据,有效数据的长度常被约定为 5、6、7 或 8 位长。
串口通信初始化代码可以从STC-ISP中获取:
发送数据:
- //串口发送一个字节数据
- void UART_SendByte(unsigned char Byte){
- SBUF=Byte;
- //检测是否完成
- while(TI==0);
- TI=0;//TI复位
- }
接收数据:
这里用的是中断4
- void uart() interrupt 4 //串口通信中断函数
- {
- u8 rec_data;
-
- RI = 0; //清除接收中断标志位
- rec_data=SBUF; //存储接收到的数据
- }
TX:发送数据输出引脚。
- static void NVIC_Configuration(void)
- {
- NVIC_InitTypeDef NVIC_InitStructure;
- /* 嵌套向量中断控制器组选择 */
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
- /* 配置 USART 为中断源 */
- NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
- /* 抢断优先级为 1 */
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
- /* 子优先级为 1 */
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
- /* 使能中断 */
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
- /* 初始化配置 NVIC */
- NVIC_Init(&NVIC_InitStructure);
- }
- void USART_Config(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- USART_InitTypeDef USART_InitStructure;
-
- // 打开串口 GPIO 的时钟
- DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
-
- // 打开串口外设的时钟
- DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
- 将 USART Tx 的 GPIO 配置为推挽复用模式
- GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
- // 将 USART Rx 的 GPIO 配置为浮空输入模式
- GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
- GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
-
- // 配置串口的工作参数
- // 配置波特率
- USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
- // 配置 针数据字长
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;
- // 配置停止位
- USART_InitStructure.USART_StopBits = USART_StopBits_1;
- // 配置校验位USART_InitStructure.USART_Parity = USART_Parity_No ;
- // 配置硬件流控制
- USART_InitStructure.USART_HardwareFlowControl =
- USART_HardwareFlowControl_None;
- // 配置工作模式,收发一起
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
- // 完成串口的初始化配置
- USART_Init(DEBUG_USARTx, &USART_InitStructure);
-
- // 串口中断优先级配置
- NVIC_Configuration();
- // 使能串口接收中断
- USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
- // 使能串口
- USART_Cmd(DEBUG_USARTx, ENABLE);
- }
- /**
- * 函 数:串口发送一个字节
- * 参 数:Byte 要发送的一个字节
- * 返 回 值:无
- */
- void Serial_SendByte(uint8_t Byte)
- {
- USART_SendData(USART1, Byte); //将字节数据写入数据寄存器,写入后USART自动生成时序波形
- while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); //等待发送完成
- /*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
- }
- /**
- * 函 数:USART1中断函数
- * 参 数:无
- * 返 回 值:无
- * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
- * 函数名为预留的指定名称,可以从启动文件复制
- * 请确保函数名正确,不能有任何差异,否则中断函数将不能进入
- */
- void USART1_IRQHandler(void)
- {
- if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) //判断是否是USART1的接收事件触发的中断
- {
- Serial_RxData = USART_ReceiveData(USART1); //读取数据寄存器,存放在接收的数据变量
- Serial_RxFlag = 1; //置接收标志位变量为1
- USART_ClearITPendingBit(USART1, USART_IT_RXNE); //清除USART1的RXNE标志位
- //读取数据寄存器会自动清除此标志位
- //如果已经读取了数据寄存器,也可以不执行此代码
- }
- }
这里由于还是通过CPU直接控制GPIO引脚来模拟时序,对于51和32,思路都是一样,我这里就只列举STM32中软件模拟。
- /**
- * 函 数:SPI写SS引脚电平
- * 参 数:BitValue 协议层传入的当前需要写入SS的电平,范围0~1
- * 返 回 值:无
- * 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SS为低电平,当BitValue为1时,需要置SS为高电平
- */
- void MySPI_W_SS(uint8_t BitValue)
- {
- GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue); //根据BitValue,设置SS引脚的电平
- }
-
- /**
- * 函 数:SPI写SCK引脚电平
- * 参 数:BitValue 协议层传入的当前需要写入SCK的电平,范围0~1
- * 返 回 值:无
- * 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SCK为低电平,当BitValue为1时,需要置SCK为高电平
- */
- void MySPI_W_SCK(uint8_t BitValue)
- {
- GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue); //根据BitValue,设置SCK引脚的电平
- }
-
- /**
- * 函 数:SPI写MOSI引脚电平
- * 参 数:BitValue 协议层传入的当前需要写入MOSI的电平,范围0~0xFF
- * 返 回 值:无
- * 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置MOSI为低电平,当BitValue非0时,需要置MOSI为高电平
- */
- void MySPI_W_MOSI(uint8_t BitValue)
- {
- GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue); //根据BitValue,设置MOSI引脚的电平,BitValue要实现非0即1的特性
- }
-
- /**
- * 函 数:I2C读MISO引脚电平
- * 参 数:无
- * 返 回 值:协议层需要得到的当前MISO的电平,范围0~1
- * 注意事项:此函数需要用户实现内容,当前MISO为低电平时,返回0,当前MISO为高电平时,返回1
- */
- uint8_t MySPI_R_MISO(void)
- {
- return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6); //读取MISO电平并返回
- }
-
- /**
- * 函 数:SPI初始化
- * 参 数:无
- * 返 回 值:无
- * 注意事项:此函数需要用户实现内容,实现SS、SCK、MOSI和MISO引脚的初始化
- */
- void MySPI_Init(void)
- {
- /*开启时钟*/
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
-
- /*GPIO初始化*/
- GPIO_InitTypeDef GPIO_InitStructure;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA4、PA5和PA7引脚初始化为推挽输出
-
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA6引脚初始化为上拉输入
-
- /*设置默认电平*/
- MySPI_W_SS(1); //SS默认高电平
- MySPI_W_SCK(0); //SCK默认低电平
- }
-
- /*协议层*/
-
- /**
- * 函 数:SPI起始
- * 参 数:无
- * 返 回 值:无
- */
- void MySPI_Start(void)
- {
- MySPI_W_SS(0); //拉低SS,开始时序
- }
-
- /**
- * 函 数:SPI终止
- * 参 数:无
- * 返 回 值:无
- */
- void MySPI_Stop(void)
- {
- MySPI_W_SS(1); //拉高SS,终止时序
- }
-
- /**
- * 函 数:SPI交换传输一个字节,使用SPI模式0
- * 参 数:ByteSend 要发送的一个字节
- * 返 回 值:接收的一个字节
- */
- uint8_t MySPI_SwapByte(uint8_t ByteSend)
- {
- uint8_t i, ByteReceive = 0x00; //定义接收的数据,并赋初值0x00,此处必须赋初值0x00,后面会用到
-
- for (i = 0; i < 8; i ++) //循环8次,依次交换每一位数据
- {
- MySPI_W_MOSI(ByteSend & (0x80 >> i)); //使用掩码的方式取出ByteSend的指定一位数据并写入到MOSI线
- MySPI_W_SCK(1); //拉高SCK,上升沿移出数据
- if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);} //读取MISO数据,并存储到Byte变量
- //当MISO为1时,置变量指定位为1,当MISO为0时,不做处理,指定位为默认的初值0
- MySPI_W_SCK(0); //拉低SCK,下降沿移入数据
- }
-
- return ByteReceive; //返回接收到的一个字节数据
- }
数据控制逻辑
SPI初始化:
- void MySPI_Init(void)
- {
- /*开启时钟*/
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); //开启SPI1的时钟
-
- /*GPIO初始化*/
- GPIO_InitTypeDef GPIO_InitStructure;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA4引脚初始化为推挽输出
-
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA5和PA7引脚初始化为复用推挽输出
-
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA6引脚初始化为上拉输入
-
- /*SPI初始化*/
- SPI_InitTypeDef SPI_InitStructure; //定义结构体变量
- SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //模式,选择为SPI主模式
- SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //方向,选择2线全双工
- SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //数据宽度,选择为8位
- SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //先行位,选择高位先行
- SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128; //波特率分频,选择128分频
- SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //SPI极性,选择低极性
- SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //SPI相位,选择第一个时钟边沿采样,极性和相位决定选择SPI模式0
- SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS,选择由软件控制
- SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC多项式,暂时用不到,给默认值7
- SPI_Init(SPI1, &SPI_InitStructure); //将结构体变量交给SPI_Init,配置SPI1
-
- /*SPI使能*/
- SPI_Cmd(SPI1, ENABLE); //使能SPI1,开始运行
-
- /*设置默认电平*/
- MySPI_W_SS(1); //SS默认高电平
- }
SPI配置:
- /**
- * 函 数:SPI写SS引脚电平,SS仍由软件模拟
- * 参 数:BitValue 协议层传入的当前需要写入SS的电平,范围0~1
- * 返 回 值:无
- * 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SS为低电平,当BitValue为1时,需要置SS为高电平
- */
- void MySPI_W_SS(uint8_t BitValue)
- {
- GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue); //根据BitValue,设置SS引脚的电平
- }
- /**
- * 函 数:SPI起始
- * 参 数:无
- * 返 回 值:无
- */
- void MySPI_Start(void)
- {
- MySPI_W_SS(0); //拉低SS,开始时序
- }
-
- /**
- * 函 数:SPI终止
- * 参 数:无
- * 返 回 值:无
- */
- void MySPI_Stop(void)
- {
- MySPI_W_SS(1); //拉高SS,终止时序
- }
-
- /**
- * 函 数:SPI交换传输一个字节,使用SPI模式0
- * 参 数:ByteSend 要发送的一个字节
- * 返 回 值:接收的一个字节
- */
- uint8_t MySPI_SwapByte(uint8_t ByteSend)
- {
- while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET); //等待发送数据寄存器空
-
- SPI_I2S_SendData(SPI1, ByteSend); //写入数据到发送数据寄存器,开始产生时序
-
- while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET); //等待接收数据寄存器非空
-
- return SPI_I2S_ReceiveData(SPI1); //读取接收到的数据并返回
- }
总结:
硬件SPI看似很复杂,实际上,我们只需要通过配置好结构体,然后把结构体放入库封装好的初始化函数,之后我们调用它给我们写好的时序函数,在不断判断标志位,并调用对应的库函数,实现SPI外设与其他设备的通信。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。