SPI总线的实现

这次来记录下SPI总线的过程
SPI分为4根线 NSS,MOSI,MISO,SCK。连接方式如下
SCK: 时钟线,时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通
讯的速率,不同的设备支持的最高时钟频率不一样,如 STM32 的 SPI 时钟频率最大为
fpclk/2,两个设备之间通讯时,通讯速率受限于低速设备。
MOSI:主发从收线
MISO 主收从发线
NSS :也叫CS,片选,可以用内部也可以用外部。大多数情况都是用外部CS。SPI 协议中没有设备地址,它使用 NSS 信
号线来寻址,当主机要选择从设备时,把该从设备的 NSS 信号线设置为低电平,该从
设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI 通讯。所以
SPI 通讯以 NSS 线置低电平为开始信号,以 NSS 线被拉高作为结束信号

参考野火教程上的资料
在这里插入图片描述
SPI一共有4中通讯模式,分别是时钟极性 CPOL和时钟相位 CPHA
1.时钟极性 CPOL 是指 SPI 通讯设备处于空闲状态时, SCK 信号线的电平信号(即 SPI 通
讯开始前、 NSS 线为高电平时 SCK 的状态)。 CPOL=0 时, SCK 在空闲状态时为低电平,
CPOL=1 时,则相反。
2.时钟相位 CPHA 是指数据的采样的时刻,当 CPHA=0 时, MOSI 或 MISO 数据线上的
信号将会在 SCK 时钟线的“奇数边沿‖被采样。当 CPHA=1 时,数据线在 SCK 的“偶数边
沿‖采样
在这里插入图片描述
一般用的比较多的是模式0和模式3。主机和从机通讯时候一定要确定是那种模式。
由于SPI的特性,一个时钟到来的时候,发和收在一个时钟下进行,因此在发数据的同时也可以接收到数据,是全双工通信。
来看下软件代码,用AD5624芯片,这是一个控制DAC芯片输出的。首先看下stm32用HAL库的
/* Private variables ---------------------------------------------------------*/
SPI_HandleTypeDef hspi3;

/**

  • @brief SPI MSP Initialization

  • This function configures the hardware resources used in this example

  • @param hspi: SPI handle pointer

  • @retval None
    /
    void HAL_SPI_MspInit(SPI_HandleTypeDef
    hspi)
    {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
    if(hspi->Instance==SPI3)
    {
    /* USER CODE BEGIN SPI3_MspInit 0 */

    /* USER CODE END SPI3_MspInit 0 /
    /
    * Initializes the peripherals clock
    */
    PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI3;
    PeriphClkInitStruct.Spi123ClockSelection = RCC_SPI123CLKSOURCE_PLL;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
    {
    // Error_Handler();
    }

    /* Peripheral clock enable */
    __HAL_RCC_SPI3_CLK_ENABLE();

    __HAL_RCC_GPIOC_CLK_ENABLE();
    /**SPI3 GPIO Configuration
    PC10 ------> SPI3_SCK
    PC12 ------> SPI3_MOSI
    PC11 ------> SPI3_MISO
    */
    GPIO_InitStruct.Pin = DAC_SCK_Pin|DAC_MOSI_Pin;
    // GPIO_InitStruct.Pin = DAC_SCK_Pin|DAC_NSS_Pin|DAC_MISO_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    /* USER CODE BEGIN SPI3_MspInit 1 */

    /* USER CODE END SPI3_MspInit 1 */
    }

}

/**

  • @brief SPI MSP De-Initialization

  • This function freeze the hardware resources used in this example

  • @param hspi: SPI handle pointer

  • @retval None
    /
    void HAL_SPI_MspDeInit(SPI_HandleTypeDef
    hspi)
    {
    if(hspi->Instance==SPI3)
    {
    /* USER CODE BEGIN SPI3_MspDeInit 0 */

    /* USER CODE END SPI3_MspDeInit 0 /
    /
    Peripheral clock disable */
    __HAL_RCC_SPI3_CLK_DISABLE();

    /**SPI3 GPIO Configuration
    PC10 ------> SPI3_SCK
    PC12 ------> SPI3_MOSI
    PC11 ------> SPI3_MISO
    */
    HAL_GPIO_DeInit(GPIOC, DAC_SCK_Pin|DAC_MOSI_Pin);

    /* USER CODE BEGIN SPI3_MspDeInit 1 */

    /* USER CODE END SPI3_MspDeInit 1 */
    }

}

/**

  • @brief DACµÄ³õʼ»¯
  • @param None
  • @retval None
    */
    void BSP_InitDAC(void)
    {
    MX_SPI3_Init();
    }

/**

  • @brief SPI3 Initialization Function
  • @param None
  • @retval None
    */
    static void MX_SPI3_Init(void)
    {

/* USER CODE BEGIN SPI3_Init 0 */

/* USER CODE END SPI3_Init 0 */

/* USER CODE BEGIN SPI3_Init 1 */

/* USER CODE END SPI3_Init 1 /
/
SPI3 parameter configuration*/
hspi3.Instance = SPI3;
hspi3.Init.Mode = SPI_MODE_MASTER;
hspi3.Init.Direction = SPI_DIRECTION_2LINES;
hspi3.Init.DataSize = SPI_DATASIZE_8BIT;
hspi3.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi3.Init.CLKPhase = SPI_PHASE_2EDGE;
hspi3.Init.NSS = SPI_NSS_SOFT;
hspi3.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;
hspi3.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi3.Init.TIMode = SPI_TIMODE_DISABLE;
hspi3.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi3.Init.CRCPolynomial = 7;

// hspi3.Init.NSSPMode = SPI_NSS_PULSE_ENABLE; //ԭʼ(»áÓ°Ïìµ½SPI·¢ËÍÊý¾ÝÅäƫѹʧ°Ü)
hspi3.Init.NSSPMode = SPI_NSS_PULSE_DISABLE; // /* ½ûÖ¹Âö³åÊä³ö */0803 ¸üÐÂ

hspi3.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
// hspi3.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
// hspi3.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
// hspi3.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
// hspi3.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
// hspi3.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
// hspi3.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
// hspi3.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;

// hspi3.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE; //ԭʼ£¨x£©
hspi3.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE;/* ½ûÖ¹SPIºó£¬SPIÏà¹ØÒý½Å±£³Öµ±Ç°×´Ì¬ / //0803 ¸üÐÂ
// hspi3.Init.IOSwap = SPI_IO_SWAP_DISABLE;
if (HAL_SPI_Init(&hspi3) != HAL_OK)
{
Error_Handler();
}
/
USER CODE BEGIN SPI3_Init 2 */

/* USER CODE END SPI3_Init 2 */

}

/**

  • @brief ·¢ËÍ1¸ö×Ö½Ú
  • @param Ò»¸ö×Ö½ÚµÄÊý¾Ý
  • @retval None
  • @note
    */
    void SPI_WriteByte(uint8_t data)
    {
    // uint8_t pdata =&data;
    HAL_SPI_Transmit(&hspi3,&data,1,10);
    /
    дÈëÊý¾Ý¼Ä´æÆ÷£¬°ÑҪдÈëµÄÊý¾ÝдÈë·¢ËÍ»º³åÇø */
    }

/**

  • @brief DACд

  • @param DACдµÄÊý¾Ý

  • @retval None

  • @note Ä£ÄâSPI·¢ËÍ
    */
    void SPI_WriteDAC(uint16_t data)
    {
    uint16_t *pdata;
    uint8_t tempData[2];
    uint16_t ConvertData;
    tempData[1]=((data >> 8) & 0x0000FF); //¸ß8λ
    tempData[0]=((data >> 0) & 0x0000FF); //µÍ8λ

    ConvertData=tempData[1];
    ConvertData=(ConvertData<<8)+tempData[0];

    pdata=&ConvertData;

// pdata=&data;

DAC_SYNC(0x01);
	HAL_Delay(1);	

// delay_us(10);
DAC_SYNC(0x00);
HAL_Delay(1);
// delay_us(10);

HAL_SPI_Transmit(&hspi3,  (uint8_t *)pdata,1,10);

// delay_us(10);
DAC_SYNC(0x01);
}

void AD5624_WriteDAC(uint32_t data)
{
DAC_SYNC(0x01);
// delay_us(10);
HAL_Delay(1);
DAC_SYNC(0x00);
// delay_us(10);
HAL_Delay(1);
SPI_WriteByte((data >> 16) & 0x0000FF);
SPI_WriteByte((data >> 8) & 0x0000FF);
SPI_WriteByte((data >> 0) & 0x0000FF);
// delay_us(10);
HAL_Delay(1);
DAC_SYNC(0x01);
}

再来看下GD32的
/*!
\brief configure the RCU of peripherals
\param[in] none
\param[out] none
\retval none
/
static void DAC_rcu_config(void)
{
/
enable the clock of peripherals */
rcu_periph_clock_enable(BOARD_DAC_GPIO_RCU);
rcu_periph_clock_enable(BOARD_DAC_RCU );

}

/*!
\brief configure the related GPIO
\param[in] none
\param[out] none
\retval none
/
void gpio_config(void)
{
/
once enabled the DAC, the corresponding GPIO pin is connected to the DAC converter automatically */
gpio_mode_set(BOARD_DAC_GPIO_PORT, GPIO_MODE_ANALOG, GPIO_PUPD_PULLDOWN, BOARD_DAC_GPIO_PIN);
gpio_output_options_set(BOARD_DAC_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,BOARD_DAC_GPIO_PIN);
}

/**

  • @brief ³õʼ»¯ÄÚ²¿DAC

  • @param None

  • @retval None

  • @note
    /
    void InterDAC_Config(void)
    {
    DAC_rcu_config();
    gpio_config();
    /
    configure the DAC0 */
    dac_trigger_source_config(DAC0, DAC_TRIGGER_SOFTWARE);
    dac_trigger_enable(DAC0);
    dac_wave_mode_config(DAC0, DAC_WAVE_DISABLE);
    dac_lfsr_noise_config(DAC0, DAC_LFSR_BITS10_0);
    dac_output_buffer_disable(DAC0);

    /* configure the DAC1 */
    dac_trigger_source_config(DAC1, DAC_TRIGGER_SOFTWARE);
    dac_trigger_enable(DAC1);
    dac_wave_mode_config(DAC1, DAC_WAVE_DISABLE);
    dac_lfsr_noise_config(DAC1, DAC_LFSR_BITS10_0);
    dac_output_buffer_disable(DAC1);

// /* enable DAC concurrent mode and set data */

}

/**

  • @brief ÅäÖÃÄÚ²¿DAC1µçѹ
  • @param ÅäÖõçѹֵ
  • @retval None
  • @note
    */

void Set_DAC1(uint16_t value)
{
if(value < 4096)
{
dac_data_set(DAC0,DAC_ALIGN_12B_R,value);
dac_concurrent_software_trigger_enable();
}
}

/**

  • @brief ÅäÖÃÄÚ²¿DAC2µçѹ
  • @param ÅäÖõçѹֵ
  • @retval None
  • @note
    */
    void Set_DAC2(uint16_t value)
    {
    if(value < 4096)
    {
    dac_data_set(DAC1,DAC_ALIGN_12B_R,value);
    dac_concurrent_software_trigger_enable();
    }
    }

/**

  • @brief DACÄÚ²¿²Î¿¼µçѹ
  • @param None
  • @retval None
  • @note
    */
    void AD5624InterVref()
    {
    AD5624_WriteDAC(0x380001);
    }

/**

  • @brief DAC¸´Î»
  • @retval None
  • @note
    */
    void AD5624SoftReset()
    {
    AD5624_WriteDAC(0x280001);
    }

/**

  • @brief ·¢ËÍ1¸ö×Ö½Ú
  • @param Ò»¸ö×Ö½ÚµÄÊý¾Ý
  • @retval None
  • @note
    /
    void SPI_WriteByte(uint8_t data)
    {
    /
    loop while data register in not emplty */
    spi_i2s_data_transmit(SPI2,data);
    while(RESET == spi_i2s_flag_get(SPI2,SPI_FLAG_TBE));

}

void AD5624_WriteDAC(uint32_t data)
{
DAC_SYNC(0x01);
delay_us(10);
DAC_SYNC(0x00);
delay_us(10);
SPI_WriteByte((data >> 16) & 0x0000FF);
SPI_WriteByte((data >> 8) & 0x0000FF);
SPI_WriteByte((data >> 0) & 0x0000FF);
delay_us(10);
DAC_SYNC(0x01);
}

//void SPI_WriteByte(uint8_t data)
//{
// uint8_t i=0;
//
// DAC_CLK(1);
// delay_1us(1);
// for(i=0;i<8;i++)
// {
// DAC_CLK(0);
// delay_1us(1);
// if((data&0x80)==0x80)
// {
// DAC_DIN(1);
// }
// else
// {
// DAC_DIN(0);
// }
//
// delay_1us(1);
// DAC_CLK(1);
// delay_1us(1);
// data<<=1;
// }

//}

/**

  • @brief DACд
  • @param DACдµÄÊý¾Ý
  • @retval None
  • @note Ä£ÄâSPI·¢ËÍ
    */
    void SPI_WriteDAC(uint16_t data)
    {
    DAC_SYNC(0x01);
    delay_us(10);
    DAC_SYNC(0x00);
    delay_us(10);
    // SPI_WriteByte((data >> 16) & 0x0000FF);
    SPI_WriteByte((data >> 8) & 0x0000FF);
    SPI_WriteByte((data >> 0) & 0x0000FF);
    delay_us(10);
    DAC_SYNC(0x01);
    }

/**

  • @brief DAC³õʼ»¯

  • @param None

  • @retval None

  • @note
    */
    void DAC_Config()
    {
    /ÍⲿDAC³õʼ»¯/
    // AD5624InterVref(); //ÄÚ²¿»ù×¼

    /ÄÚ²¿DAC³õʼ»¯/
    // InterDAC_Config();
    }

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是SPI总线读写的Verilog代码实现示例: ``` module spi_master ( input clk, input reset_n, input [7:0] tx_data, output reg [7:0] rx_data, output reg spi_ss_n, output reg spi_sck, output reg spi_mosi, input spi_miso ); // 状态机状态定义 parameter IDLE = 2'b00; parameter SEND = 2'b01; parameter RECV = 2'b10; reg [1:0] state; reg [7:0] tx_count; reg [7:0] rx_count; reg [7:0] shift_reg; always @(posedge clk or negedge reset_n) begin if (!reset_n) begin state <= IDLE; spi_ss_n <= 1'b1; tx_count <= 8'd0; rx_count <= 8'd0; shift_reg <= 8'd0; end else begin case (state) IDLE: begin spi_sck <= 1'b0; spi_mosi <= 1'b0; rx_data <= 8'd0; if (spi_ss_n == 1'b0) begin state <= SEND; end end SEND: begin spi_sck <= 1'b1; spi_mosi <= tx_data[tx_count]; tx_count <= tx_count + 1; if (tx_count == 8'd8) begin spi_ss_n <= 1'b1; state <= RECV; end end RECV: begin spi_sck <= 1'b0; if (rx_count < 8'd8) begin shift_reg <= {shift_reg[6:0], spi_miso}; rx_count <= rx_count + 1; end else begin rx_data <= shift_reg; state <= IDLE; end end endcase end end endmodule ``` 该代码实现了一个SPI主设备,包括状态机、时钟、复位、数据输入和输出线,以及一个状态机实现SPI总线的读写操作。具体实现方式如下: 1. 状态机状态定义:IDLE表示空闲状态,SEND表示发送数据状态,RECV表示接收数据状态。 2. 在时钟的上升沿或复位信号的下降沿触发状态机。 3. 在空闲状态下,将时钟、数据输出线和接收数据线清零,并等待从机设备选择信号。 4. 在发送数据状态下,将时钟线置高,同时将数据输出线置为待发送的数据,计数器加1,直到发送完8位数据。一旦发送完毕,将从机设备选择信号置高,并进入接收数据状态。 5. 在接收数据状态下,将时钟线置低。接收8位数据,将每个位存储在移位寄存器中,直到接收完8位数据。一旦接收完毕,将移位寄存器的值存储在接收数据线中,并返回到空闲状态。 需要注意的是,该代码只实现SPI总线的读写基本操作,实际使用时还需要根据具体应用进行修改和优化。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值