• stm32使用硬件SPI


    Driver_SPI.c

    #include "Driver_SPI.h"
    
    void Driver_SPI_Init(void)
    {
        /* 1. 开启SPI1的时钟 开启GPIO时钟 PA和PC*/
        RCC->APB2ENR |= (RCC_APB2ENR_SPI1EN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPAEN);
    
        /* 2. 设置引脚的工作模式 */
        /* 2.1 cs: 通用推挽输出 PC13*  CNF=00 MODE=11 */
        GPIOC->CRH &= ~GPIO_CRH_CNF13;
        GPIOC->CRH |= GPIO_CRH_MODE13;
        /* 2.2 sck: 推挽输出 PA5*/ /* cnf=10  mode=11 */
        /* 2.3 mosi: 推挽输出 PA7*/
        GPIOA->CRL |= (GPIO_CRL_MODE5 | GPIO_CRL_MODE7 | GPIO_CRL_CNF7_1 | GPIO_CRL_CNF5_1);
        GPIOA->CRL &= ~(GPIO_CRL_CNF5_0 | GPIO_CRL_CNF7_0);
        /* 2.4 miso: 浮空输入 PA6  CNF=01 MODE=00*/
        GPIOA->CRL &= ~(GPIO_CRL_CNF6_1 | GPIO_CRL_MODE6);
        GPIOA->CRL |= GPIO_CRL_CNF6_0;
    
        /* 3. SPI相关的配置 */
        /* 3.1 配置SPI1为主模式 */
        SPI1->CR1 |= SPI_CR1_MSTR;
        /* 3.2 NSS禁用, 从设备的片选使用普通的GPIO控制*/
        SPI1->CR1 |= SPI_CR1_SSM;//ssm设置为1,通过软件NSS,内部NSS连接SSI
        SPI1->CR2 &= ~SPI_CR2_SSOE;//设置ssom = 0,禁止spi输出
        SPI1->CR1 |= SPI_CR1_SSI;//SSI设置为高电平,控制主机正常工作,内部NSS始终高电平
        /* 3.3 配置SPI的工作模式 模式0   时钟极性和相位*/
        SPI1->CR1 &= ~(SPI_CR1_CPOL | SPI_CR1_CPHA);
        /* 3.4 配置波特率的分频系数 0=2分频 1=4分频 2=8分频 ....*/
        SPI1->CR1 &= ~SPI_CR1_BR;
        SPI1->CR1 |= SPI_CR1_BR_1;
        /* 3.5 配置数据帧的格式: 8为或16位 */
        SPI1->CR1 &= ~SPI_CR1_DFF;
        /* 3.6 配置LSB 或 MSB*/
        SPI1->CR1 &= ~SPI_CR1_LSBFIRST;
        /* 3.7 使能SPI */
        SPI1->CR1 |= SPI_CR1_SPE;
    }
    void Driver_SPI_Start(void)
    {
        CS_LOW;
    }
    
    void Driver_SPI_Stop(void)
    {
        CS_HIGH;
    }
    
    uint8_t Driver_SPI_SwapByte(uint8_t byte)
    {
        /* 1. 写数据到发送缓冲区 */
        /* 1.1 判断发送缓冲功区为空 */
        while ((SPI1->SR & SPI_SR_TXE) == 0)
            ;
        /* 1.2 把数据放入DR寄存器 */
        SPI1->DR = byte;
        /* 2. 读数据 */
        /* 2.1 先判断接收缓冲区非空 */
        while ((SPI1->SR & SPI_SR_RXNE) == 0)
            ;
        /* 2.1 从接收缓冲区读取数据 */
        return (uint8_t)(SPI1->DR & 0xff);
    }
    

    Driver_SPI.h

    #ifndef __DRIVER_SPI_H
    #define __DRIVER_SPI_H
    
    #include "stm32f10x.h"
    #include "Delay.h"
    
    
    
    #define CS_HIGH (GPIOC->ODR |= GPIO_ODR_ODR13)
    #define CS_LOW (GPIOC->ODR &= ~GPIO_ODR_ODR13)
    
    
    #define SPI_DELAY Delay_us(5)
    
    void Driver_SPI_Init(void);
    
    void Driver_SPI_Start(void);
    
    void Driver_SPI_Stop(void);
    
    uint8_t Driver_SPI_SwapByte(uint8_t byte);
    
    #endif
    
    

    Inf_W25Q32.c

    #include "Inf_W25Q32.h"
    /*
    一共64块(0-63) 每块16个扇区(0-15)  每个扇区16页(0-15)  每页256个字节
    */
    void Inf_W25Q32_Init(void)
    {
        Driver_SPI_Init();
    }
    
    void Inf_W25Q32_ReadId(uint8_t *mid, uint16_t *did)
    {
    
        Driver_SPI_Start();
    
        /* 1. 发送 Jedec id指令 */
        Driver_SPI_SwapByte(0x9f);
    
        /* 2. 获取厂商id (发送的数据随意)*/
        *mid = Driver_SPI_SwapByte(0xff);
    
        /* 3. 获取设备id */
        *did = 0;
        *did |= Driver_SPI_SwapByte(0xff) << 8;
        *did |= Driver_SPI_SwapByte(0xff) & 0xff;
    
        Driver_SPI_Stop();
    }
    
    /**
     * @description: 读取内部寄存器的busy,一直等到不忙再结束函数
     * @return {*}
     */
    void Inf_W25Q32_WaiteNotBusy(void)
    {
        Driver_SPI_Start();
        Driver_SPI_SwapByte(0x05);
    
        while (Driver_SPI_SwapByte(0xff) & 0x01)
            ;
        Driver_SPI_Stop();
    }
    
    /**
     * @description: FLASH写使能  0x06
     * @return {*}
     */
    void Inf_W25q32_WiteEnable(void)
    {
        Driver_SPI_Start();
        Driver_SPI_SwapByte(0x06);
        Driver_SPI_Stop();
    }
    
    /**
     * @description: FLASH写失能  0x04
     * @return {*}
     */
    void Inf_W25q32_WiteDisanable(void)
    {
        Driver_SPI_Start();
        Driver_SPI_SwapByte(0x04);
        Driver_SPI_Stop();
    }
    
    /**
     * @description: 擦除指定块内的指定扇区
     *  一共64块(0-63) 每块16个扇区(0-15)  每个扇区16页(0-15)  每页256个字节
     * @param {uint8_t} block
     * @param {uint8_t} sector
     */
    void Inf_W25Q32_EraseSector(uint8_t block, uint8_t sector)
    {
        Inf_W25Q32_WaiteNotBusy();
    
        Inf_W25q32_WiteEnable();
        /* 计算出要擦除的扇区的首地址 */
        uint32_t sectorAddr = block * 0x010000 + sector * 0x001000;
    
        Driver_SPI_Start();
        Driver_SPI_SwapByte(0x20);
        // 0000 0000 0000 0000 0000 0000
        Driver_SPI_SwapByte(sectorAddr >> 16 & 0xff);
        Driver_SPI_SwapByte(sectorAddr >> 8 & 0xff);
        Driver_SPI_SwapByte(sectorAddr & 0xff);
        Driver_SPI_Stop();
    
        Inf_W25q32_WiteDisanable();
    }
    
    /**
     * @description: 执行页写入 写入时,从指定页的首地址开始写入.
     * @param {uint8_t} block
     * @param {uint8_t} sector
     * @param {uint8_t} page
     * @param {uint8_t*} data
     * @param {uint16_t} len
     */
    void Inf_W25Q32_WritePage(uint8_t block,
                              uint8_t sector,
                              uint8_t page,
                              uint8_t *data,
                              uint16_t len)
    {
        Inf_W25Q32_WaiteNotBusy();
    
        Inf_W25q32_WiteEnable();
        /* 计算出要写入的数据的页的首地址 */
        uint32_t pageAddr = block * 0x010000 + sector * 0x001000 + page * 0x000100;
    
        Driver_SPI_Start();
        Driver_SPI_SwapByte(0x02);
    
        Driver_SPI_SwapByte(pageAddr >> 16 & 0xff);
        Driver_SPI_SwapByte(pageAddr >> 8 & 0xff);
        Driver_SPI_SwapByte(pageAddr & 0xff);
    
        for (uint16_t i = 0; i < len; i++)
        {
            Driver_SPI_SwapByte(data[i]);
        }
    
        Driver_SPI_Stop();
    
        Inf_W25q32_WiteDisanable();
    }
    
    /**
     * @description: 从flash读取数据
     * @return {*}
     */
    void Inf_W25Q32_Read(uint8_t block,
                         uint8_t sector,
                         uint8_t page,
                         uint8_t *data,
                         uint16_t len)
    {
        Inf_W25Q32_WaiteNotBusy();
        
        uint32_t pageAddr = block * 0x010000 + sector * 0x001000 + page * 0x000100;
    
        Driver_SPI_Start();
        Driver_SPI_SwapByte(0x03);
    
        Driver_SPI_SwapByte(pageAddr >> 16 & 0xff);
        Driver_SPI_SwapByte(pageAddr >> 8 & 0xff);
        Driver_SPI_SwapByte(pageAddr & 0xff);
        for (uint16_t i = 0; i < len; i++)
        {
            data[i] = Driver_SPI_SwapByte(0xff);
        }
    
        Driver_SPI_Stop();
    }
    

    Inf_W25Q32.h

    #ifndef __INF_W25Q32_
    #define __INF_W25Q32_
    
    #include "Driver_SPI.h"
    
    void Inf_W25Q32_Init(void);
    
    void Inf_W25Q32_ReadId(uint8_t *mid, uint16_t *did);
    
    void Inf_W25q32_WiteEnable(void);
    
    void Inf_W25q32_WiteDisanable(void);
    
    void Inf_W25Q32_EraseSector(uint8_t block, uint8_t sector);
    
    void Inf_W25Q32_WritePage(uint8_t block, uint8_t sector, uint8_t page, uint8_t *data, uint16_t len);
    
    void Inf_W25Q32_Read(uint8_t block, uint8_t sector, uint8_t page, uint8_t *data, uint16_t len);
    
    #endif
    
    
    

    main.c

    #include "Driver_USART.h"
    
    #include "Delay.h"
    #include "Inf_W25Q32.h"
    
    int main()
    {
        Driver_USART1_Init();
        Inf_W25Q32_Init();
    
        /* 读取id测试是否正常 */
        uint8_t mid = 0;
        uint16_t did = 0;
        Inf_W25Q32_ReadId(&mid, &did);
        printf("mid=0x%X, did=0x%X\r\n", mid, did);
    
        /* 先擦除 */;
        Inf_W25Q32_EraseSector(0,0);
        Inf_W25Q32_EraseSector(0,1);
        
        Inf_W25Q32_WritePage(0,0,0, "abc", 3);
        Inf_W25Q32_WritePage(0,1,0, "456", 3);
        uint8_t buff[10] = {0};
        Inf_W25Q32_Read(0,0,0, buff, 3);
        printf("%s\r\n", buff);
        Inf_W25Q32_Read(0,1,0, buff, 3);
        printf("%s\r\n", buff);
    
    
        while (1)
        {
         
        }
    }
    
    
  • 相关阅读:
    设计师找灵感就上这几个网站。
    chatgpt输出mysql常用语法汇总
    pytest(7)-yield与终结函数
    ESP8266/ESP32 +1.3“ or 0.96“ IIC OLED指针式时钟
    Spring Boot开发之参数处理
    原生CLI指令构建npm run减少硬盘node_modules的开销
    笔试强训第十九天 (最长公共子串+汽水瓶)
    设计模式之代理模式
    CAS虚拟化平台Linux虚拟机安装vGPU显卡驱动并获取许可
    C#:Winfrom 实现DataGridView 自定义分页
  • 原文地址:https://blog.csdn.net/m0_46145044/article/details/140326797