/**
 ******************************************************************************
 * @文件     spi.c
 * @作者     阜阳师范大学物电学院
 * @版本     V0.1
 * @日期     2025-03-28
 * @简介     硬件 SPI 驱动程序
 *******
 **/
 
#include "spi.h"  


// 定义一个SPI句柄结构体变量，用于存储SPI的初始化参数和状态信息
SPI_HandleTypeDef SPI2_Handle;


/**
 * @brief 初始化SPI2外设
 *
 * 该函数用于初始化SPI2外设，配置SPI的工作模式、数据大小、时钟极性等参数，
 * 并使能SPI2外设。
 */
void SPI_Init(void)
{
    // 使能SPI2的时钟，确保SPI2外设能够正常工作
    SPI2_SPI_CLK_ENABLE();

    // 指定SPI实例为SPI2
    SPI2_Handle.Instance = SPI2;
    // 设置SPI工作模式为主机模式，由该SPI设备控制通信过程
    SPI2_Handle.Init.Mode = SPI_MODE_MASTER;
    // 设置SPI通信方向为单线模式，即只使用一条数据线进行通信
    SPI2_Handle.Init.Direction = SPI_DIRECTION_1LINE;
    // 设置SPI数据大小为8位，每次传输8位数据
    SPI2_Handle.Init.DataSize = SPI_DATASIZE_8BIT;
    // 设置SPI时钟极性为高电平，即空闲状态下时钟信号为高电平
    SPI2_Handle.Init.CLKPolarity = SPI_POLARITY_HIGH;
    // 设置SPI时钟相位为第二个边沿采样数据
    SPI2_Handle.Init.CLKPhase = SPI_PHASE_2EDGE;    /*上升沿采样数据*/
    // 设置SPI片选信号为软件控制
    SPI2_Handle.Init.NSS = SPI_NSS_SOFT;
    // 设置SPI波特率预分频器为256，降低SPI通信速率
    SPI2_Handle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
    // 设置SPI数据传输的起始位为高位在前
    SPI2_Handle.Init.FirstBit = SPI_FIRSTBIT_MSB;
    // 禁用SPI的TI模式
    SPI2_Handle.Init.TIMode = SPI_TIMODE_DISABLE;
    // 禁用SPI的CRC计算功能
    SPI2_Handle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    // 设置CRC多项式为7（虽然已禁用CRC计算，但仍需设置该参数）
    SPI2_Handle.Init.CRCPolynomial = 7;

    // 根据上述配置初始化SPI2外设
    HAL_SPI_Init(&SPI2_Handle);
    // 使能SPI2外设，开始工作
    __HAL_SPI_ENABLE(&SPI2_Handle);
}

/**
 * @brief SPI外设的底层硬件初始化函数
 *
 * 该函数用于初始化SPI外设所使用的GPIO引脚，包括时钟线、数据线等。
 *
 * @param hspi 指向SPI句柄结构体的指针，用于指定要初始化的SPI实例
 */
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
    // 定义一个GPIO初始化结构体变量，用于配置GPIO引脚的参数
    GPIO_InitTypeDef gpio_init_struct;
    // 判断要初始化的SPI实例是否为SPI2
    if (hspi->Instance == SPI2)
    {
        // 使能SPI2的MOSI引脚的时钟
        SPI_MOSI_GPIO_CLK_ENABLE();
        // 注释掉的代码，原本可能用于使能SPI2的MISO引脚的时钟
        // SPI_MISO_GPIO_CLK_ENABLE();
        // 使能SPI2的时钟线引脚的时钟
        SPI_CLK_GPIO_CLK_ENABLE();

        // 配置SPI2的时钟线引脚
        gpio_init_struct.Pin = SPI_CLK_GPIO_PIN;
        // 设置引脚模式为复用推挽输出，用于连接SPI时钟信号
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;
        // 设置引脚速度为高频
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
        // 根据上述配置初始化SPI2的时钟线引脚
        HAL_GPIO_Init(SPI_CLK_GPIO_PORT, &gpio_init_struct);

        // 注释掉的代码，原本可能用于配置SPI2的MISO引脚
        // MISO引脚模式设置(复用输出)
        // gpio_init_struct.Pin = SPI_MISO_GPIO_PIN;
        // HAL_GPIO_Init(SPI_MISO_GPIO_PORT, &gpio_init_struct);

        // 配置SPI2的MOSI引脚
        gpio_init_struct.Pin = SPI_MOSI_GPIO_PIN;
        // 设置引脚模式为复用推挽输出，用于连接SPI数据输出信号
        gpio_init_struct.Mode = GPIO_MODE_AF_PP; 
        // 设置引脚速度为高频
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
        // 根据上述配置初始化SPI2的MOSI引脚
        HAL_GPIO_Init(SPI_MOSI_GPIO_PORT, &gpio_init_struct);
    }
}


/**
  * @简介  SPI 写入一个字节8位
  * @输入参数    pData：要写入的数据指针
  * @输入参数    Size：要写入的数据大小
  * @输入参数    dir：1 数据从后面往前发送  0：数据从前往后发送
  * @注意事项    此函数是高位优先写入
  * @返回值 无
  */
void Hard_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint8_t dir) 
{
    uint16_t i = 0;
    
    dir ? pData += (Size-1): pData;
    
    for(i =0; i < Size; i++)
    {
        HAL_SPI_Transmit(hspi, pData, 1, 1000);
        dir ? pData--: pData++;
    }
}


