• LoRaWAN_stack移植笔记(三)__SPI


    stm32相关的配置

    由于例程使用的主控芯片为STM32L151C8T6,而在本设计中使用的主控芯片为STM32L051C8T6,内核不一样,并且Cube库相关的函数接口及配置也会有不同,所以芯片的驱动所以做修改。

    SPI 的配置

    SPI使用的是STM32的硬件接口-SPI1 MOSI MISO
    可以看到例程中,对SPI接口进行了再一层的封装,封装如下:

    /*!
     * SPI driver structure definition
     */
    struct Spi_s
    {
        SPI_HandleTypeDef Spi;
        Gpio_t Mosi;
        Gpio_t Miso;
        Gpio_t Sclk;
        Gpio_t Nss;
    };
    

    其中:

    	SPI_HandleTypeDef Spi;
    

    是原先的STM32Cube库的封装,在此基础上,将SPI的引脚也封装进了自定义的Spi_s结构体中。这样,查看结构体就可以看到SPI的所有情况。

    SPI 初始化配置

    初始化的函数体如下:

    void SpiInit( Spi_t *obj, PinNames mosi, PinNames miso, PinNames sclk, PinNames nss )
    {
        __HAL_RCC_SPI1_FORCE_RESET( );
        __HAL_RCC_SPI1_RELEASE_RESET( );
    
        __HAL_RCC_SPI1_CLK_ENABLE( );
    
        obj->Spi.Instance = ( SPI_TypeDef *) SPI1_BASE;
    
        GpioInit( &obj->Mosi, mosi, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_DOWN, GPIO_AF0_SPI1 );
        GpioInit( &obj->Miso, miso, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_DOWN, GPIO_AF0_SPI1 );
        GpioInit( &obj->Sclk, sclk, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_DOWN, GPIO_AF0_SPI1 );
    
        if( nss != NC )
        {
            GpioInit( &obj->Nss, nss, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_UP, GPIO_AF0_SPI1 );
        }
        else
        {
            obj->Spi.Init.NSS = SPI_NSS_SOFT;
    			  GpioInit( &SX1276.Spi.Nss, RADIO_NSS, PIN_OUTPUT, PIN_PUSH_PULL, PIN_PULL_UP, 1 );
        }
    
        if( nss == NC )
        {
            SpiFormat( obj, SPI_DATASIZE_8BIT, SPI_POLARITY_LOW, SPI_PHASE_1EDGE, 0 );
        }
        else
        {
            SpiFormat( obj, SPI_DATASIZE_8BIT, SPI_POLARITY_LOW, SPI_PHASE_1EDGE, 1 );
        }
    		obj->Spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
    
        HAL_SPI_Init( &obj->Spi );
    }
    ``` c
    SPI的初始化函数是这样被调用的:
    ``` c
    SpiInit( &SX1276.Spi, RADIO_MOSI, RADIO_MISO,RADIO_SCLK, NC );
    
    //初始化函数的原型
    void SpiInit( Spi_t *obj, PinNames mosi, PinNames miso, PinNames sclk, PinNames nss );
    

    其中引脚定义是这样的

    #define RADIO_MOSI                                PA_7
    #define RADIO_MISO                                PA_6
    #define RADIO_SCLK                                PA_5
    #define RADIO_NSS                                 PA_4
    

    可以看到SPI的MOSI/MISO/SCLK脚都有看到,但是NSS脚看到,而是传了NC。

    这是为什么呢?
    可以看到程序里面有段话

    if( nss != NC )
    {
        GpioInit( &obj->Nss, nss, PIN_ALTERNATE_FCT, PIN_PUSH_PULL, PIN_PULL_UP, GPIO_AF0_SPI1 );
    }
    else
    {
        obj->Spi.Init.NSS = SPI_NSS_SOFT;
    	GpioInit( &SX1276.Spi.Nss, RADIO_NSS, PIN_OUTPUT, PIN_PUSH_PULL, PIN_PULL_UP, 1 );
    }
    

    其意思就是设置为NC就配置NSS 为软件控制,即NSS脚只做片选使用,x像GPIO一样控制他拉高拉低就可以控制片选的使能与否了。

    还有一处,设置SPI的工作频率的

    SpiFrequency( obj, 10000000 );
    
    void SpiFrequency( Spi_t *obj, uint32_t hz )
    {
        uint32_t divisor;
    
        divisor = SystemCoreClock / hz;
    
        // Find the nearest power-of-2
        divisor = divisor > 0 ? divisor-1 : 0;
        divisor |= divisor >> 1;
        divisor |= divisor >> 2;
        divisor |= divisor >> 4;
        divisor |= divisor >> 8;
        divisor |= divisor >> 16;
        divisor++;
    
        divisor = __ffs( divisor ) - 1;
    
        divisor = ( divisor > 0x07 ) ? 0x07 : divisor;
    
        obj->Spi.Init.BaudRatePrescaler = divisor << 3;
    }
    

    由于__ffs这个函数只有Cotex-M3以上内核才能调用,但是通过计算可知若传参为10000000,__ffs这个函数的返回值为0x03,所以可得obj->Spi.Init.BaudRatePrescaler = 8,即SPI_BAUDRATEPRESCALER_8
    因为

    #define SPI_BAUDRATEPRESCALER_8         ((uint32_t)SPI_CR1_BR_1)
    #define SPI_CR1_BR_1                (0x2U << SPI_CR1_BR_Pos)                   
    #define SPI_CR1_BR_Pos              (3U)   
    

    所以此处设置Spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
    最后再调用

    HAL_SPI_Init( &obj->Spi );
    

    至此,SPI的初始化就完成了。

    SPI 读写

    接下来就是SPI的读写操作了,由于都是使用的Cube库,对寄存器的命名并没有什么不同,直接保留例程中的代码就可以了。

    //获取SPI的标志位状态
    FlagStatus SpiGetFlag( Spi_t *obj, uint16_t flag )
    {
        FlagStatus bitstatus = RESET;
    
        // Check the status of the specified SPI flag
        if( ( obj->Spi.Instance->SR & flag ) != ( uint16_t )RESET )
        {
            // SPI_I2S_FLAG is set
            bitstatus = SET;
        }
        else
        {
            // SPI_I2S_FLAG is reset
            bitstatus = RESET;
        }
        // Return the SPI_I2S_FLAG status
        return  bitstatus;
    }
    
    //SPI读写
    uint16_t SpiInOut( Spi_t *obj, uint16_t outData )
    {
        uint8_t rxData = 0;
    
        if( ( obj == NULL ) || ( obj->Spi.Instance ) == NULL )
        {
            assert_param( FAIL );
        }
    
        __HAL_SPI_ENABLE( &obj->Spi );
    
        while( SpiGetFlag( obj, SPI_FLAG_TXE ) == RESET );
        obj->Spi.Instance->DR = ( uint16_t ) ( outData & 0xFF );
    
        while( SpiGetFlag( obj, SPI_FLAG_RXNE ) == RESET );
        rxData = ( uint16_t ) obj->Spi.Instance->DR;
    
        return( rxData );
    }
    

    在使用上,需要注意NSS引脚的操作,在进行读写前进行使能,读写完毕之后失能。程序如下图所示:

    //SPI写
    void SX1276WriteBuffer( uint8_t addr, uint8_t *buffer, uint8_t size )
    {
        uint8_t i;
    
        //NSS = 0;
        GpioWrite( &SX1276.Spi.Nss, 0 );
    
        SpiInOut( &SX1276.Spi, addr | 0x80 );
        for( i = 0; i < size; i++ )
        {
            SpiInOut( &SX1276.Spi, buffer[i] );
        }
    
        //NSS = 1;
        GpioWrite( &SX1276.Spi.Nss, 1 );
    }
    
    //SPI读
    void SX1276ReadBuffer( uint8_t addr, uint8_t *buffer, uint8_t size )
    {
        uint8_t i;
    
        //NSS = 0;
        GpioWrite( &SX1276.Spi.Nss, 0 );
    
        SpiInOut( &SX1276.Spi, addr & 0x7F );
    
        for( i = 0; i < size; i++ )
        {
            buffer[i] = SpiInOut( &SX1276.Spi, 0 );
        }
    
        //NSS = 1;
        GpioWrite( &SX1276.Spi.Nss, 1 );
    }
    

    至此,LoRaWAN例程中的SPI的移植就完成了。

  • 相关阅读:
    Treap
    P1650 田忌赛马
    wqs二分
    P3810 【模板】三维偏序(陌上花开)(CDQ分治)
    UVA1205 Color a Tree
    P2887 [USACO07NOV]Sunscreen G
    Snowflake Snow Snowflakes
    P1613 跑路
    P5018 [NOIP2018 普及组] 对称二叉树
    装模作样的停课记录
  • 原文地址:https://www.cnblogs.com/answerinthewind/p/6272334.html
Copyright © 2020-2023  润新知