STM32中USART,IIC,SPI,CAN协议成员的配置,及数据读写解读(

目录

USART协议

串口初始化代码示例:

STM中断知识补充:

USART协议读数据

USART协议写数据

IIC协议

IIC数据的传输过程:

IIC协议读数据:

IIC协议写数据:

(软件模拟IIC)

(硬件IIC)

SPI协议

SPI协议初始化代码示例

SPI协议读数据:

SPI协议写数据:

CAN协议

CAN外设初始化代码示例

CAN协议读数据:

CAN协议写数据:



USART协议

串口初始化代码示例:
void USART2_Init()
 {
     RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
     
     GPIO_InitTypeDef GPIO_InitStructure;
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;         //复用推挽
     GPIO_InitStructure.GPIO_Pin =GPIO_Pin_2;               //TX
     GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz ;       //配置引脚的速率
     GPIO_Init(GPIOA,&GPIO_InitStructure);
     
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;          //上拉输入
     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 ;             //RX
     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz ;      //配置引脚的速率
     GPIO_Init(GPIOA,&GPIO_InitStructure);
     
     //定义完引脚的模式,功能之后开始设置USART2的相关配置
     USART_InitTypeDef USART2_InitStructure;
     USART2_InitStructure.USART_BaudRate = 115200;           //配置波特率
     USART2_InitStructure.USART_HardwareFlowControl =USART_HardwareFlowControl_None;//是否开启硬件流控模式 
     USART2_InitStructure.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;  //设置工作模式
     USART2_InitStructure.USART_Parity = USART_Parity_No;            //设置奇偶校验
     USART2_InitStructure.USART_StopBits = USART_StopBits_1;         //设置停止位
     USART2_InitStructure.USART_WordLength = USART_WordLength_8b;    //设置数据长度
     USART_Init(USART2,&USART2_InitStructure);
     
     //串口中断配置 NVIC(Nested Vectored Interrupt Controller),用于处理中断优先级和中断使能设置
     USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);  //启用了 USART2 的接收中断(USART_IT_RXNE),当 USART2 接收到数据后将触发中断。
     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置 NVIC 的优先级分组为 2,这个函数用于设置中断优先级分组的方式,决定了优先级位数的分配,以及子优先级位数的分配
     /*NVIC成员的属性配置*/
     NVIC_InitTypeDef NVIC_InitStruct;
     NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn;  //设置中断通道为 USART2 的中断通道
     NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;    //使能 USART2 的中断通道
     NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;  //设置 USART2 中断的抢占优先级为 1。抢占优先级数值越低,优先级越高。
     NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; //设置 USART2 中断的子优先级为 1。子优先级是在同一抢占优先级下用于区分不同中断的优
     NVIC_Init(&NVIC_InitStruct);
     USART_Cmd(USART2,ENABLE);
 }
STM中断知识补充:

在ARM Cortex-M处理器中,中断优先级的分组和子优先级是通过NVIC(Nested Vectored Interrupt Controller)来配置的。这个配置是为了更灵活地管理中断优先级,以适应不同外设和应用的需求。

  1. 中断优先级分组: NVIC 支持将中断优先级分为两个部分,一个是抢占优先级(Preemption Priority),另一个是子优先级(Subpriority)。抢占优先级用来决定在同一抢占组中哪个中断优先处理,而子优先级则用来在同一抢占优先级中决定中断的执行顺序。

    • 如果中断源的抢占优先级高于正在执行的中断,那么会立即切换到新的中断。

    • 如果中断源的抢占优先级相同,那么通过子优先级来决定执行顺序。

    中断优先级分组的设置通过NVIC_PriorityGroupConfig函数进行配置,你的代码中的NVIC_PriorityGroup_2指定了分组方式为2。

  2. 子优先级: 子优先级是在同一抢占优先级组内,用于区分不同中断的执行顺序。子优先级数值越低,优先级越高。

在你的代码片段中,抢占优先级为1,子优先级也为1。这意味着USART2的中断具有相对较高的优先级。当有多个中断同时发生时,系统会根据这些优先级来确定中断的执行顺序。

一般来说,对于外设的中断配置,可以根据应用需求来设置不同的优先级。例如,一些关键任务可能需要更高的中断优先级,而一些普通任务可以使用较低的优先级。在实际配置中,需要根据具体应用场景和系统要求来调整中断优先级的设置。

USART协议读数据

这段代码是ST官方库中提供的一条函数,实现串口的数据接收功能。

 uint16_t USART_ReceiveData(USART_TypeDef* USARTx)
 {
   /* Check the parameters */
   assert_param(IS_USART_ALL_PERIPH(USARTx));
   
   /* Receive Data */
   return (uint16_t)(USARTx->DR & (uint16_t)0x01FF);
     从 USART 的数据寄存器(DR,Data Register)中读取接收到的数据,并以 16 位无符号整数的形式返回。
 }

下面这段代码是一个USART2串口的中断服务函数。当USART2接收到数据(USART_IT_RXNE标志被置位)时,会触发中断,执行这个中断服务函数。

 void USART2_IRQHandler(void)
 {
     if (USART_GetITStatus(USART2, USART_IT_RXNE) == SET) //检查USART2接收寄存器是否准备好接收新的数据
     {
         Serial_RxData = USART_ReceiveData(USART2);//读取USART2接收到的数据,将其存储在Serial_RxData变量中
         Serial_RxFlag = 1;  //设置一个标志位(通常是一个全局变量),表示数据已经被接收到了
         USART_ClearITPendingBit(USART2, USART_IT_RXNE); //清除接收寄存器非空的中断标志位,表示已经处理了这个中断
     }
 }
USART协议写数据

下面这段代码是官方源文件中给的一个函数,可以实现通过USARTx外设传输单个数据。

 
/**
   * @brief  Transmits single data through the USARTx peripheral.
   * @param  USARTx: Select the USART or the UART peripheral. 
   *   This parameter can be one of the following values:
   *   USART1, USART2, USART3, UART4 or UART5.
   * @param  Data: the data to transmit.
   * @retval None
   */
 void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
 {
   /* Check the parameters 检查函数的参数是否合法*/ 
   assert_param(IS_USART_ALL_PERIPH(USARTx));    //这里的两个函数是一个宏定义,具体参看工程源文件
   assert_param(IS_USART_DATA(Data)); 
     
   /* Transmit Data */
   USARTx->DR = (Data & (uint16_t)0x01FF);   //这行代码实际上是将要发送的数据写入 USART 数据寄存器 (DR - Data Register)。在这里,使用了位掩码 0x01FF 来确保只有低9位有效,因为 USART 的数据寄存器通常是 9 位。这可以确保超过 9 位的数据部分不会影响传输
 }

我们可以在此基础上来封装函数,实现长数据的发送。

 void Serial_SendByte(uint8_t Byte)                          //发送一个字节
 {
     USART_SendData(USART2, Byte);
     while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
 }
 ​
 void Serial_SendArray(uint8_t *Array, uint16_t Length)      //发送一个数组
 {
     uint16_t i;
     for (i = 0; i < Length; i ++)
     {
         Serial_SendByte(Array[i]);
     }
 }
 ​
 void Serial_SendString(char *String)                    //发送一个字符串
 {
     uint8_t i;
     for (i = 0; String[i] != '\0'; i ++)
     {
         Serial_SendByte(String[i]);
     }
 }
 

——————————————————————————————————————————————————————

IIC协议

IIC数据的传输过程:
  • 空闲状态

  • 起始信号

  • 传输过程

  • 结束信号

IIC协议读数据:

读取数据的简要流程:

  1. 总线初始化: 首先,需要初始化I2C总线。这包括设置I2C的时钟频率、选择模式(主模式或从模式)以及配置I2C控制寄存器。

  2. 开始信号(Start Signal): 在进行读取操作之前,主设备(通常是微控制器或处理器)发送一个开始信号,表示要开始I2C通信。

  3. 发送从设备地址和读取位: 主设备发送要读取的从设备的7位地址,并设置读取位(R/W位)为1(表示读取操作)。这将引发从设备的应答信号。

  4. 从设备应答: 从设备接收到地址和读取位后,如果找到匹配的地址,它将发送一个应答信号。

  5. 读取数据: 主设备读取从设备发送的数据。在每个字节接收之后,主设备发送一个应答信号,表示继续读取下一个字节。如果主设备不需要继续读取,则发送无应答信号。

  6. 停止信号(Stop Signal): 在读取完成后,主设备发送一个停止信号,表示I2C通信结束。

 // 从指定地址读数据  --->硬件模式
 uint8_t I2C_ReadData(uint8_t deviceAddress, uint8_t registerAddress) {
     uint8_t receivedData;
     // 等待直到I2C总线空闲
     while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
     // 生成起始条件
     I2C_GenerateSTART(I2C1, ENABLE);
     while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
     // 发送设备地址和写模式(0)
     I2C_Send7bitAddress(I2C1, deviceAddress, I2C_Direction_Transmitter);
     while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
     // 发送寄存器地址
     I2C_SendData(I2C1, registerAddress);
     while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
     // 生成重复起始条件
     I2C_GenerateSTART(I2C1, ENABLE);
     while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
     // 发送设备地址和读模式(1)
     I2C_Send7bitAddress(I2C1, deviceAddress, I2C_Direction_Receiver);
     while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
     // 禁用应答
     I2C_AcknowledgeConfig(I2C1, DISABLE);
     // 产生停止条件
     I2C_GenerateSTOP(I2C1, ENABLE);
     // 等待接收到数据
     while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));
     receivedData = I2C_ReceiveData(I2C1);
     // 启用应答
     I2C_AcknowledgeConfig(I2C1, ENABLE);
 ​
     return receivedData;
 }

IIC协议写数据:
(软件模拟IIC)
 
#include "stm32f10x.h"
 ​
 #define OLED_W_SCL(x)       GPIO_WriteBit(GPIOB, GPIO_Pin_8, (BitAction)(x))
 #define OLED_W_SDA(x)       GPIO_WriteBit(GPIOB, GPIO_Pin_9, (BitAction)(x))
 ​
 /*引脚初始化*/
 void OLED_I2C_Init(void)
 {
     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
     
     GPIO_InitTypeDef GPIO_InitStructure;
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
     GPIO_Init(GPIOB, &GPIO_InitStructure);
     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
     GPIO_Init(GPIOB, &GPIO_InitStructure);
     
     OLED_W_SCL(1);
     OLED_W_SDA(1);
 }
 ​
 ​
 /**
   * @brief  I2C开始
   * @param  无
   * @retval 无
   */
 void OLED_I2C_Start(void)
 {
     OLED_W_SDA(1);
     OLED_W_SCL(1);
     OLED_W_SDA(0);
     OLED_W_SCL(0);
 }
 ​
 /**
   * @brief  I2C停止
   * @param  无
   * @retval 无
   */
 void OLED_I2C_Stop(void)
 {
     OLED_W_SDA(0);
     OLED_W_SCL(1);
     OLED_W_SDA(1);
 }
 ​
 /**
   * @brief  I2C发送一个字节
   * @param  Byte 要发送的一个字节
   * @retval 无
   */
 void OLED_I2C_SendByte(uint8_t Byte)
 {
     uint8_t i;
     for (i = 0; i < 8; i++)
     {
         OLED_W_SDA(Byte & (0x80 >> i));
         OLED_W_SCL(1);
         OLED_W_SCL(0);
     }
     OLED_W_SCL(1);  //额外的一个时钟,不处理应答信号
     OLED_W_SCL(0);
 }

IIC协议传输代码示例

(硬件IIC)
 #include "stm32f10x.h"
 ​
 void I2C_init(void)
 {
     GPIO_InitTypeDef GPIO_InitStructure;
     I2C_InitTypeDef I2C_InitStructure;
 ​
     // Step 1: Enable the Clocks
     RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
 ​
     // Step 2: Configure the GPIO pins
     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
     GPIO_Init(GPIOB, &GPIO_InitStructure);
 ​
     // Step 3: Configure the I2C peripheral
     I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;          //设置I2C的工作模式为I2C模式
     I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;  //设置I2C的占空比为50%
     I2C_InitStructure.I2C_OwnAddress1 = 0x00;           //not used in master mode : 在主模式下,该值不被使用
     I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;         //启用I2C的应答功能。当从设备收到数据后,需要向主设备发送一个应答信号,表示数据已被接收。
 ​
 ​
     I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;   //采用7位地址模式对从设备的地址进行确认
     I2C_InitStructure.I2C_ClockSpeed = 100000; //100kHz 设置I2C的时钟速度为100kHz。这个参数决定了I2C通信的速度,即SCL线的频率
     I2C_Init(I2C1, &I2C_InitStructure);
     
     // Step 4: Enable the I2C peripheral
     I2C_Cmd(I2C1, ENABLE);
 }
 ​
 // 向指定地址写数据
 void I2C_WriteData(uint8_t deviceAddress, uint8_t registerAddress, uint8_t data) {
     // 等待直到I2C总线空闲
     while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
     // 生成起始条件
     I2C_GenerateSTART(I2C1, ENABLE);
     while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
     // 发送设备地址和写模式(0)
     I2C_Send7bitAddress(I2C1, deviceAddress, I2C_Direction_Transmitter);
     while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
     // 发送寄存器地址
     I2C_SendData(I2C1, registerAddress);
     while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
     // 发送数据
     I2C_SendData(I2C1, data);
     while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
     // 产生停止条件
     I2C_GenerateSTOP(I2C1, ENABLE);
 }
 ​

——————————————————————————————————————————————————————

SPI协议

SPI协议初始化代码示例
//SPI——————VS1053芯片的读写操作

//初始化VS1053的IO口	 
void VS_Init(void)
{
	SPI_InitTypeDef  SPI_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(VS_SPIGPIO_CLK|VS_GPIO_DREQ_CLK|VS_GPIO_RST_CLK|VS_GPIO_XDCS_CLK, ENABLE);
	
 	GPIO_InitStructure.GPIO_Pin = VS_DREQ;			     //DREQ
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 		 //输入
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(VS_GPIO_DREQ_PORT, &GPIO_InitStructure);

 	GPIO_InitStructure.GPIO_Pin = VS_RST;	 //PB9
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;      //推挽输出
	GPIO_Init(VS_GPIO_RST_PORT, &GPIO_InitStructure);
	
	/*初始化STM32 SPI2接口*/
	RCC_APB1PeriphClockCmd(VS_SPI_CLK, ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = VS_SCLK | VS_MISO | VS_MOSI;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;		   // 复用输出
	GPIO_Init(VS_SPIGPIO_PORT, &GPIO_InitStructure);
   
	GPIO_InitStructure.GPIO_Pin = VS_XCS;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;	   // 推挽输出
	GPIO_Init(VS_SPIGPIO_PORT, &GPIO_InitStructure);	
	
	GPIO_InitStructure.GPIO_Pin = VS_XDCS;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;	   // 推免输出
	GPIO_Init(VS_GPIO_XDCS_PORT, &GPIO_InitStructure);
	
	/* SPI2 配置 */ 
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; 	//设置SPI的通讯方式--全双工
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;				  	//SPI工作在主模式
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;			 	//每次数据传输8位
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;					//表示时钟空闲状态为高电平
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;					//表示数据采样发生在第二个时钟沿(前面的时钟极性为SPI_CPOL_High,时钟默认为高电平。第二个时钟沿也就是下降沿,对应下降沿采样)
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;						//表示软件管理NSS信号
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;	//表示波特率预分频为256
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;				//表示数据传输从最高位开始
	SPI_InitStructure.SPI_CRCPolynomial = 7;						//指定SPI的CRC多项式。在这里设置为7

	SPI_Init(VS_SPI, &SPI_InitStructure);
	
	/* 使能 SPI2 */
	SPI_Cmd(VS_SPI, ENABLE); 
	SPI2_ReadWriteByte(0xff);//启动传输	
	
}	  
SPI协议读数据:

SPI协议接收数据的流程通常如下:

  1. 发送数据: 在SPI通信开始之前,通常需要先发送一些数据到从设备,这通常会激活从设备并准备好它来对主设备发送数据。

  2. 发送和接收同步: 在SPI的全双工模式下,发送和接收是同时进行的。主设备发送数据的同时也接收从设备返回的数据。

  3. 发送接收数据: 主设备一边发送数据,一边接收从设备返回的数据。SPI使用一组寄存器来进行数据的发送和接收。

  4. 等待数据准备: 主设备发送完一个字节后,需要等待从设备准备好返回的数据。

  5. 读取数据: 当从设备准备好返回数据时,主设备从SPI数据寄存器中读取接收到的数据。

/*******************************************************************************
* Function Name  : SPI_FLASH_SendByte
* Description    : Sends a byte through the SPI interface and return the byte
*                  received from the SPI bus.
* Input          : byte : byte to send.
* Output         : None
* Return         : The value of the received byte.
*******************************************************************************/
unsigned char SPI2_ReadWriteByte(unsigned char writedat)
{
	/* Loop while DR register in not emplty */
	while(SPI_I2S_GetFlagStatus(VS_SPI,SPI_I2S_FLAG_TXE) == RESET);
	
	/* Send byte through the SPI1 peripheral */
	SPI_I2S_SendData(VS_SPI, writedat);
    
	while(SPI_I2S_GetFlagStatus(VS_SPI, SPI_I2S_FLAG_RXNE) == RESET);
	
	/* Return the byte read from the SPI bus */
	return SPI_I2S_ReceiveData(VS_SPI);
}

这个函数可以理解发送一个字节的数据到SPI总线上,并等待接收到一个字节的数据作为返回值

  1. while(SPI_I2S_GetFlagStatus(VS_SPI,SPI_I2S_FLAG_TXE) == RESET)`:通过检查SPI_I2S_FLAG_TXE标志位的状态,等待直到发送缓冲区为空(即可以发送新数据)。

  2. SPI_I2S_SendData(VS_SPI, writedat):将参数 writedat 的值发送到SPI总线上。

  3. while(SPI_I2S_GetFlagStatus(VS_SPI, SPI_I2S_FLAG_RXNE) == RESET):通过检查SPI_I2S_FLAG_RXNE标志位的状态,等待直到接收缓冲区中有数据。

  4. return SPI_I2S_ReceiveData(VS_SPI):从SPI总线接收数据,并将接收到的数据返回。

#include "stm32f4xx.h" // 包含适用于你的STM32型号的头文件

// 定义片选引脚
#define CS_PIN GPIO_PIN_4
#define CS_PORT GPIOA

// 初始化SPI和片选引脚
void SPI_Init() {
    // 初始化SPI外设
    // 例如:设置SPI的工作模式、数据帧格式、速度等
    // 这里假设SPI已经被正确地初始化

    // 初始化片选引脚
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_Pin = CS_PIN;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(CS_PORT, &GPIO_InitStruct);
}

// 选择从设备
void SPI_SelectDevice() {
    GPIO_ResetBits(CS_PORT, CS_PIN); // 低电平选中从设备
}

// 取消选择从设备
void SPI_DeselectDevice() {
    GPIO_SetBits(CS_PORT, CS_PIN); // 高电平取消选择从设备
}

// 通过SPI发送和接收数据
unsigned char SPI_ReadWriteByte(unsigned char writedat) {
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
    SPI_I2S_SendData(SPI1, writedat);
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
    return SPI_I2S_ReceiveData(SPI1);
}

int main() {
    SPI_Init();
    // 选择从设备
    SPI_SelectDevice();
    // 向从设备发送数据并接收返回数据
    unsigned char dataToSend = 0x55; // 你要发送的数据
    unsigned char receivedData = SPI_ReadWriteByte(dataToSend);
    // 取消选择从设备
    SPI_DeselectDevice();
    // 处理接收到的数据
    // ...
    while (1) {
        // 主程序循环
    }
}
/*
在这个示例中,我们首先定义了片选引脚的引脚号和端口,然后初始化SPI外设和片选引脚。SPI_SelectDevice函数用于选择从设备,即通过将片选引脚置为低电平来激活从设备。SPI_DeselectDevice函数用于取消选择从设备,即通过将片选引脚置为高电平来取消从设备的激活。

在main函数中,首先调用SPI_SelectDevice来选择从设备,然后使用SPI_ReadWriteByte函数向从设备发送数据并接收返回的数据。最后,通过调用SPI_DeselectDevice来取消选择从设备。

*/

SPI协议写数据:

首先发送要写入数据的地址,然后再发送需要写入的数据。

中间 涉及到数据的采样在什么时候发生,例如(上升沿采样还是下降沿采样-->由SPI的时钟相性决定)

SPI的时钟极性决定了 时钟的空闲状态为高电平还是低电平。

void VS_WR_Cmd(u8 address,u16 data)
{  
	while(VS_DREQ_IN==0);//等待空闲		  
	VS_SPI_SpeedLow();//低速 	   
	VS_XDCS_SET; 
	VS_XCS_CLR;	 
	VS_SPI_ReadWriteByte(VS_WRITE_COMMAND);//发送VS10XX的写命令
	VS_SPI_ReadWriteByte(address); 	//地址
	VS_SPI_ReadWriteByte(data>>8); 	//发送高八位
	VS_SPI_ReadWriteByte(data);	 	//第八位
	VS_XCS_SET;           
	VS_SPI_SpeedHigh();				//高速	   
} 

//向VS10XX写数据
//data:要写入的数据
void VS_WR_Data(u8 data)
{
	VS_SPI_SpeedHigh();//高速,对VS1003B,最大值不能超过36.864/4Mhz,这里设置为9M 
	VS_XDCS_CLR;   
	VS_SPI_ReadWriteByte(data);
	VS_XDCS_SET;      
}         

——————————————————————————————————————————————————————

CAN协议

在CAN(Controller Area Network)协议中,存在几种不同的工作模式,包括正常模式、静默模式、静默回环模式和回环模式。以下是对这些模式的简要介绍:

  1. 正常模式(Normal Mode):

    • 正常模式是CAN总线的默认工作模式。在这种模式下,节点可以发送和接收数据帧,与其他节点进行通信。节点在这个模式下正常地发送数据,并接收其他节点发送的数据。这是CAN总线的标准工作状态。

  2. 静默模式(Silent Mode):

    • 静默模式是一种特殊的模式,节点在这个模式下不发送自己的数据帧到总线上,但仍然能够接收其他节点发送的数据帧。这意味着节点在静默模式下处于被动状态,仅接收数据而不发送任何数据。静默模式通常用于故障诊断或特定测试场景下。

  3. 静默回环模式(Silent Loopback Mode):

    • 静默回环模式是一种结合了静默模式和回环模式的特殊模式。节点在这个模式下不发送数据到总线上,并且发送的数据会通过内部回环机制回收,以便节点可以接收自己发送的数据。这种模式通常用于测试CAN控制器和软件的功能性,能够在不影响真实网络的情况下测试发送和接收功能。

  4. 回环模式(Loopback Mode):

    • 回环模式是一种用于自测和诊断的模式,节点发送的数据会被传送到接收端,同时也会被发送端自己接收到。这使得节点能够测试其CAN控制器和通信软件,而无需实际的物理总线连接。在这种模式下,发送的数据会经过内部环回,并由接收端接收,从而可以验证通信的正确性。

CAN外设初始化代码示例
#include "stm32f10x.h"

CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CanTxMsg TxMessage;
CanRxMsg RxMessage;

//初始化配置can外设引脚,及can模块成员属性
void CAN_Configuration(void) {
  GPIO_InitTypeDef GPIO_InitStructure;

  // 使能GPIO和CAN时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA, ENABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);

  // 配置CAN引脚
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  // CAN模块初始化
  CAN_InitStructure.CAN_TTCM = DISABLE;				//时间触发通信方式:禁止
  CAN_InitStructure.CAN_ABOM = DISABLE;				//禁用自动总线关闭管理
  CAN_InitStructure.CAN_AWUM = DISABLE;				//禁用自动唤醒模式
  CAN_InitStructure.CAN_NART = DISABLE;				//禁用自动重传
  CAN_InitStructure.CAN_RFLM = DISABLE;				//禁用接收FIFO锁定模式
  CAN_InitStructure.CAN_TXFP = DISABLE;				//禁用传输FIFO优先级
  CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;		//CAN_Mode_Normal表示使用正常模式
  CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;			//这里设置为CAN_SJW_1tq,表示1个时间单元
  CAN_InitStructure.CAN_BS1 = CAN_BS1_4tq;			//时间段1的时间单元数量,表示4个时间单元
  CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq;			//时间段2的时间单元数量,表示3个时间单元	
  CAN_InitStructure.CAN_Prescaler = 10; 			// 根据波特率需求进行调整
  CAN_Init(CAN1, &CAN_InitStructure);

  // CAN滤波器配置
  CAN_FilterInitStructure.CAN_FilterNumber = 0;						//配置的过滤器编号,这里设置为0。
  CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;		//表示标识符掩码模式
  CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;		//过滤器的比例表示32位比例
  CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;		
  CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;			//配置标识符的高位和低位,表示不过滤任何标识符
  CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;	
  CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;		//标识符的掩码,表示不使用掩码
  CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;		//指定过滤器与FIFO关联,设置为0表示关联到FIFO0
  CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;	//启用过滤器
  CAN_FilterInit(&CAN_FilterInitStructure);
}
CAN协议读数据:
//CanRxMsg RxMessage;

void My_CAN_Receive(void) {
  // 检查是否有消息待接收   
  if (CAN_MessagePending(CAN1, CAN_FIFO0) > 0) {
    // 接收消息
    CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);	//st官方提供的库函数

    // 处理接收到的数据,根据实际情况进行处理
    // RxMessage.StdId 是接收到的消息的标准标识符
    // RxMessage.DLC 是接收到的数据长度
    // RxMessage.Data 包含了实际的数据
  }
}
CAN协议写数据:
void My_CAN_Transmit(uint32_t id, uint8_t *data, uint8_t length) {
  // 设置CAN发送消息    CanTxMsg TxMessage;  CanTxMsg结构体定义了can发送帧的成员属性
  TxMessage.StdId = id;
  TxMessage.ExtId = 0x01;
  TxMessage.RTR = CAN_RTR_DATA;
  TxMessage.IDE = CAN_ID_STD;
  TxMessage.DLC = length;

  for (int i = 0; i < length; i++) {
    TxMessage.Data[i] = data[i];
  }

  // 发送消息
  CAN_Transmit(CAN1, &TxMessage);				//st官方提供的库函数
}

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值