【电子DIY】DIY行车记录仪为游戏机之主控软件开发(5)spi驱动

代码见:https://github.com/qinyunti/py32f403-nes.git


一.前言

硬件上有一颗SPI FLASH:W25Q256J, 用于做文件系统存储文件。我们先来实现SPI驱动,然后再来实现FLASH的读写最后移植文件系统。

213611dviris0foofereee

213611lhhffhff539fhxk3

二.SPI驱动

我们混合使用HAL和LL库,初始化使用HAL,读写使用LL更直接。添加SPI的HAL驱动需要

py32f403_hal_conf.h中

#define HAL_SPI_MODULE_ENABLED

2.1接口设计

考虑有多个SPI和多个CS,所有初始化接口有id参数。

void spi_init(int id, uint32_t baud, int mode);

传输接口有id参数区分不同SPI,CS参数区分共用SPI不同CS。

tx_buffer表示待发送数据,可以为空,此时就发送FF

rx_buffer表示待接受数据,可以为空,

Len表示收发长度

Flag表示是否自动拉高CS。

所以一个接口可以实现只法,只收,或者收发。

uint32_t spi_trans(int id, int cs, uint8_t* tx_buffer, uint8_t* rx_buffer, uint32_t len, int flag);

2.2引脚

PA15  NSS1   FLASH   APB2

PB3   SCK  AF3

PB4   MISO AF3

PB5   MOSI AF3

PC6   NSS2   TOUCH

PB3/4/5对应SPI1,AF2, PA15使用IO方式控制CS。

还和触摸芯片共享SPI,触摸芯片使用PC6控制CS。

213611msolc98961zi9dos

初始化代码如下

                        GPIO_InitTypeDef  GPIO_InitStruct;

                        __HAL_RCC_GPIOA_CLK_ENABLE();         

                        __HAL_RCC_GPIOB_CLK_ENABLE();   

                        __HAL_RCC_GPIOC_CLK_ENABLE();   

                        __HAL_RCC_SPI1_CLK_ENABLE();


                        GPIO_InitStruct.Pin = GPIO_PIN_15;

                        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;              

                        GPIO_InitStruct.Pull = GPIO_PULLUP;                    

                        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;   

                        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);  

                        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET);

                        GPIO_InitStruct.Pin = GPIO_PIN_6;

                        HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

                        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_SET);


                        GPIO_InitStruct.Pin = GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5;

                        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;              

                        GPIO_InitStruct.Pull = GPIO_PULLUP;                    

                        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;   

                        GPIO_InitStruct.Alternate = GPIO_AF3_SPI1;   

                        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);  

2.3SPI配置

初始化代码如下

                        SPI_HandleTypeDef hspi;

                        hspi.Instance = SPI1;

                        uint32_t clk = HAL_RCC_GetPCLK2Freq();

                        uint32_t div = clk/baud;

                        if(div<=2){

                                hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;

                        }else if(div<=4){

                                hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;

                        }else if(div<=8){

                                hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;

                        }else if(div<=16){

                                hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;

                        }else if(div<=32){

                                hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;

                        }else if(div<=64){

                                hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;

                        }else if(div<=128){

                                hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;

                        }else{

                                hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;

                        }

                        if(mode & 0x01){

                                hspi.Init.CLKPhase = SPI_PHASE_2EDGE;

                        }else{

                                hspi.Init.CLKPhase = SPI_PHASE_1EDGE;

                        }

                        if(mode & 0x02){

                                hspi.Init.CLKPolarity = SPI_POLARITY_HIGH;

                        }else{

                                hspi.Init.CLKPolarity = SPI_POLARITY_LOW;

                        }

                        hspi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;

                        hspi.Init.CRCPolynomial = 0;

                        hspi.Init.DataSize = SPI_DATASIZE_8BIT;

                        hspi.Init.Direction = SPI_DIRECTION_2LINES;

                        hspi.Init.FirstBit = SPI_FIRSTBIT_MSB;

                        hspi.Init.Mode = SPI_MODE_MASTER;

                        HAL_SPI_Init(&hspi);

                        LL_SPI_Enable(SPI1);

特别要注意的是HAL_SPI_Init后需要LL_SPI_Enable使能。

2.4传输

可只发,只收(发0xFF),和收发,可以配置是否自动拉高CS用于多次传输组合操作。

        if(id == 0){

                if(cs == 0){

                        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);

                }else if(cs == 1){

                        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_RESET);

                }

                for(uint32_t i=0; i<len; i++){

                        if(tx_buffer != (uint8_t*)0){

                                LL_SPI_TransmitData8(SPI1, tx_buffer);

                        }else{

                                LL_SPI_TransmitData8(SPI1, 0xFF);

                        }

                        while(LL_SPI_IsActiveFlag_RXNE(SPI1) == 0);

                        rx = LL_SPI_ReceiveData8(SPI1);

                        if(rx_buffer != (uint8_t*)0){

                                rx_buffer = rx;

                        }

                }


                if(flag){

                        if(cs == 0){

                                HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET);

                        }else if(cs == 1){

                                HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_SET);

                        }

                }

        }else if(id == 1){



        }

2.5多线程保护

为了方便多任务使用,设计了互斥量进行保护

初始化时创建互斥量

SemaphoreHandle_t s_spi_mutex[2];

s_spi_mutex[id] = xSemaphoreCreateRecursiveMutex();

传输时先获取互斥量,然后再操作,再释放

        if(pdPASS != xSemaphoreTakeRecursive(s_spi_mutex[id], 100)){

                return 0;

        }

    ......

           xSemaphoreGiveRecursive(s_spi_mutex[id]);

2.6完整源码

Spi.h

#ifndef SPI_H

#define SPI_H

#ifdef __cplusplus

extern "C" {

#endif


#include <stdint.h>

void spi_init(int id, uint32_t baud, int mode);

uint32_t spi_trans(int id, int cs, uint8_t* tx_buffer, uint8_t* rx_buffer, uint32_t len, int flag);

#ifdef __cplusplus

}

#endif

#endif

Spi.c

#include "uart.h"

#include "fifo.h"

#include "py32f4xx_hal.h"

#include "py32f403_ll_spi.h"

#include "FreeRTOS.h"

#include "semphr.h"

SemaphoreHandle_t s_spi_mutex[2];

void spi_init(int id, uint32_t baud, int mode)

{

        s_spi_mutex[id] = xSemaphoreCreateRecursiveMutex();

        if(id == 0){               

                /* PA15  NSS1   FLASH   APB2

                 * PB3   SCK  AF3

                 * PB4   MISO AF3

                 * PB5   MOSI AF3

                 * PC6   NSS2   TOUCH

                 */

                        GPIO_InitTypeDef  GPIO_InitStruct;

                        __HAL_RCC_GPIOA_CLK_ENABLE();         

                        __HAL_RCC_GPIOB_CLK_ENABLE();   

                        __HAL_RCC_GPIOC_CLK_ENABLE();   

                        __HAL_RCC_SPI1_CLK_ENABLE();


                        GPIO_InitStruct.Pin = GPIO_PIN_15;

                        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;              

                        GPIO_InitStruct.Pull = GPIO_PULLUP;                    

                        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;   

                        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);  

                        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET);

                        GPIO_InitStruct.Pin = GPIO_PIN_6;

                        HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

                        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_SET);


                        GPIO_InitStruct.Pin = GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5;

                        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;              

                        GPIO_InitStruct.Pull = GPIO_PULLUP;                    

                        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;   

                        GPIO_InitStruct.Alternate = GPIO_AF3_SPI1;   

                        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);  


                        SPI_HandleTypeDef hspi;

                        hspi.Instance = SPI1;

                        uint32_t clk = HAL_RCC_GetPCLK2Freq();

                        uint32_t div = clk/baud;

                        if(div<=2){

                                hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;

                        }else if(div<=4){

                                hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;

                        }else if(div<=8){

                                hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;

                        }else if(div<=16){

                                hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;

                        }else if(div<=32){

                                hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;

                        }else if(div<=64){

                                hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;

                        }else if(div<=128){

                                hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;

                        }else{

                                hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;

                        }

                        if(mode & 0x01){

                                hspi.Init.CLKPhase = SPI_PHASE_2EDGE;

                        }else{

                                hspi.Init.CLKPhase = SPI_PHASE_1EDGE;

                        }

                        if(mode & 0x02){

                                hspi.Init.CLKPolarity = SPI_POLARITY_HIGH;

                        }else{

                                hspi.Init.CLKPolarity = SPI_POLARITY_LOW;

                        }

                        hspi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;

                        hspi.Init.CRCPolynomial = 0;

                        hspi.Init.DataSize = SPI_DATASIZE_8BIT;

                        hspi.Init.Direction = SPI_DIRECTION_2LINES;

                        hspi.Init.FirstBit = SPI_FIRSTBIT_MSB;

                        hspi.Init.Mode = SPI_MODE_MASTER;

                        HAL_SPI_Init(&hspi);

                        LL_SPI_Enable(SPI1);

        }else if(id == 1){



        }

}

uint32_t spi_trans(int id, int cs, uint8_t* tx_buffer, uint8_t* rx_buffer, uint32_t len, int flag)

{

        uint8_t rx;


        if(pdPASS != xSemaphoreTakeRecursive(s_spi_mutex[id], 100)){

                return 0;

        }


        if(id == 0){

                if(cs == 0){

                        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);

                }else if(cs == 1){

                        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_RESET);

                }

                for(uint32_t i=0; i<len; i++){

                        if(tx_buffer != (uint8_t*)0){

                                LL_SPI_TransmitData8(SPI1, tx_buffer);

                        }else{

                                LL_SPI_TransmitData8(SPI1, 0xFF);

                        }

                        while(LL_SPI_IsActiveFlag_RXNE(SPI1) == 0);

                        rx = LL_SPI_ReceiveData8(SPI1);

                        if(rx_buffer != (uint8_t*)0){

                                rx_buffer = rx;

                        }

                }


                if(flag){

                        if(cs == 0){

                                HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET);

                        }else if(cs == 1){

                                HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_SET);

                        }

                }

        }else if(id == 1){



        }

        xSemaphoreGiveRecursive(s_spi_mutex[id]);

        return len;

}

三.测试

Main.c中初始化

        spi_init(0, 36000000ul, 3);

Shell_func中

#include spi.h

添加两个函数申明

static void spirxfunc(uint8_t* param);

static void spitxrxfunc(uint8_t* param);

g_shell_cmd_list_ast中添加两行,即添加两个命令

  { (uint8_t*)"spirx",        spirxfunc,        (uint8_t*)"spirx id cs len flag"},                

  { (uint8_t*)"spitxrx",      spitxrxfunc,      (uint8_t*)"spirx id cs hexstr flag"},

对应的实现代码如下

static void spirxfunc(uint8_t* param)

{

        int id;

        int cs;

        int len;

        int flag;

        uint8_t* rx_tmp;

  if(4 == sscanf((const char*)param, "%*s %d %d %d %d", &id, &cs, &len, &flag))

  {

    xprintf("spitxrx %d %d %d %d\r\n",id,cs,len,flag);

                rx_tmp = pvPortMalloc(len);

                spi_trans(id,cs,0,rx_tmp,len,flag);

                xprintf("[rx]:");

                for(int i=0; i<len; i++){

                        xprintf("%02x ", rx_tmp);

                }

                xprintf("\r\n");

                vPortFree(rx_tmp);

  }

  else

  {

    xprintf("param err");

  }

}

static void spitxrxfunc(uint8_t* param)

{

        int id;

        int cs;

  uint8_t hexstr[32+1];

        uint8_t tx_tmp[16];

        uint8_t rx_tmp[16];

        uint32_t hexnum;

        int flag;

  if(4 == sscanf((const char*)param, "%*s %d %d %s %d", &id, &cs, hexstr, &flag))

  {

    xprintf("spitxrx %d %d %s %d\r\n",id,cs,hexstr,flag);

                hexnum = str2hex((const char*)hexstr,tx_tmp,32);

                spi_trans(id,cs,tx_tmp,rx_tmp,hexnum,flag);

                xprintf("[rx]:");

                for(uint32_t i=0; i<hexnum; i++){

                        xprintf("%02x ", rx_tmp);

                }

                xprintf("\r\n");

  }

  else

  {

    xprintf("param err");

  }

}

运行后可以看到添加的命令

                213611tnnb0nknntkk01uf

查看芯片手册读ID命令为0x9F

213611pr01j1011rskcnj1

所以输入命令

spitxrx 0 0 9fff 1

即发送两字节9FFF,接收两字节。

213612ab8tccg8ficcnyns

可以看到收到EF,即对应的ID,说明SPI接口正常。

可以修改

        spi_init(0, 36000000ul, 3);

测试频率,模式等参数

这里测试频率26M和72M,示波器抓取的波形

213612u5ys58yqkiga8618

213612u03ggjrdzdp1at3r

四.总结

以上我们实现了灵活的SPI接口,一个接口实现SPI传输,方便使用,然后基于shell进行了测试。需要注意的是HAL_SPI_Init后需要LL_SPI_Enable使能。