打印
[研电赛技术支持]

GD32F470如何搭建BSP驱动框架(连载)

[复制链接]
13332|14
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 caizhiwei 于 2024-6-2 08:58 编辑

#申请原创# #技术资源#@21小跑堂
各位二姨家网友好。
      前段时间在做一个GD32F470为核心的嵌入式项目,一开始想套用STM32的代码,后来发现简单的IO控制,uart是没啥问题的,复杂的外设ADC+DMA, USB_Device虚拟串口等,会出现bug,后来咨询原厂FAE,他们给出的答案是GD芯片虽然兼容ST,但是不是100%兼容,内部有改进和创新,推荐使用GD官网下载的寄存器驱动库。
      寄存器库虽然可以用,但是还要写不少接口函数,所以打算自己实现一个bsp层,分享给网友。
      代码分层框架如下(橙色的外设就是要实现的bsp层):
采用IAR IDE




使用特权

评论回复
沙发
caizhiwei|  楼主 | 2024-6-2 08:45 | 只看该作者
先说说flash驱动吧,GD32F470 支持扇区内的按页擦除(每一页是4kb),这样给fatfs移植提供了方便。
/*对于1MB flash容量的GD32F470VGT来说, 没有定义 8,9,10,11扇区,从而软件是兼容1~ 3MB型号的*/
//对于GD32F4xx,使用了两片闪存;前1024KB容量在第0片闪存(bank0)中
//后1024kb的容量在第1片闪存(bank1)中
// 每个扇区中,以4KB字节大小为一页,Flash可以按页(最小单位)擦除。
#ifndef __BSP_FLASH_H__
#define __BSP_FLASH_H__

#include "gd32f4xx.h"
#include <stdbool.h>
#include <stdio.h>
#include "includes.h"

/* flash相关宏定义 */

/*对于1MB flash容量的GD32F470VGT来说, 没有定义 8,9,10,11扇区,从而软件是兼容1~ 3MB型号的*/

//对于GD32F4xx,使用了两片闪存;前1024KB容量在第0片闪存(bank0)中
//后1024kb的容量在第1片闪存(bank1)中
// 每个扇区中,以4KB字节大小为一页,Flash可以按页(最小单位)擦除

#define ADDR_FLASH_SECTOR_0     ((uint32_t)0x08000000) /* Base [url=home.php?mod=space&uid=72445]@[/url] of Sector 0, /                    */
#define ADDR_FLASH_SECTOR_1     ((uint32_t)0x08004000) /* Base @ of Sector 1, 16 Kbytes */

#define ADDR_FLASH_SECTOR_2     ((uint32_t)0x08008000) /* Base @ of Sector 2, 16 Kbytes */
#define ADDR_FLASH_SECTOR_3     ((uint32_t)0x0800C000) /* Base @ of Sector 3, 16 Kbytes */

#define ADDR_FLASH_SECTOR_4     ((uint32_t)0x08010000) /* Base @ of Sector 4, 64 Kbytes */
#define ADDR_FLASH_SECTOR_5     ((uint32_t)0x08020000) /* Base @ of Sector 5, 128 Kbytes */

#define ADDR_FLASH_SECTOR_6     ((uint32_t)0x08040000) /* Base @ of Sector 6, 128 Kbytes */
#define ADDR_FLASH_SECTOR_7     ((uint32_t)0x08060000) /* Base @ of Sector 7, 128 Kbytes */

#define ADDR_FLASH_SECTOR_8     ((uint32_t)0x08080000) /* Base @ of Sector 8, 128 Kbytes */
#define ADDR_FLASH_SECTOR_9     ((uint32_t)0x080A0000) /* Base @ of Sector 9, 128 Kbytes */
#define ADDR_FLASH_SECTOR_10    ((uint32_t)0x080C0000) /* Base @ of Sector 10, 128 Kbytes */
#define ADDR_FLASH_SECTOR_11    ((uint32_t)0x080E0000) /* Base @ of Sector 11, 128 Kbytes */

/* end of 1024KB here */

#define ADDR_FLASH_SECTOR_12    ((uint32_t)0x08100000) /* Base @ of Sector 12, 16 Kbytes */
/*
....  13 ~ 22 add here
*/
#define ADDR_FLASH_SECTOR_23    ((uint32_t)0x081E0000) /* Base @ of Sector 23, 128 Kbytes */


#define ADDR_FLASH_Bank0_START   ((uint32_t)0x08000000)
#define ADDR_FLASH_Bank1_START   ((uint32_t)0x08100000)

#define FMC_PAGE_SIZE  ((uint32_t)0x1000)   /*4kb*/

/**
  * @{扇区号}
  */
#define FLASH_Sector_0     ((uint16_t)0x0000) /*!< Sector Number 0   */
#define FLASH_Sector_1     ((uint16_t)0x0008) /*!< Sector Number 1   */
#define FLASH_Sector_2     ((uint16_t)0x0010) /*!< Sector Number 2   */
#define FLASH_Sector_3     ((uint16_t)0x0018) /*!< Sector Number 3   */
#define FLASH_Sector_4     ((uint16_t)0x0020) /*!< Sector Number 4   */
#define FLASH_Sector_5     ((uint16_t)0x0028) /*!< Sector Number 5   */
#define FLASH_Sector_6     ((uint16_t)0x0030) /*!< Sector Number 6   */
#define FLASH_Sector_7     ((uint16_t)0x0038) /*!< Sector Number 7   */
#define FLASH_Sector_8     ((uint16_t)0x0040) /*!< Sector Number 8   */
#define FLASH_Sector_9     ((uint16_t)0x0048) /*!< Sector Number 9   */
#define FLASH_Sector_10    ((uint16_t)0x0050) /*!< Sector Number 10  */
#define FLASH_Sector_11    ((uint16_t)0x0058) /*!< Sector Number 11  */
#define FLASH_Sector_12    ((uint16_t)0x0080) /*!< Sector Number 12  */
#define FLASH_Sector_13    ((uint16_t)0x0088) /*!< Sector Number 13  */
#define FLASH_Sector_14    ((uint16_t)0x0090) /*!< Sector Number 14  */
#define FLASH_Sector_15    ((uint16_t)0x0098) /*!< Sector Number 15  */
#define FLASH_Sector_16    ((uint16_t)0x00A0) /*!< Sector Number 16  */
#define FLASH_Sector_17    ((uint16_t)0x00A8) /*!< Sector Number 17  */
#define FLASH_Sector_18    ((uint16_t)0x00B0) /*!< Sector Number 18  */
#define FLASH_Sector_19    ((uint16_t)0x00B8) /*!< Sector Number 19  */
#define FLASH_Sector_20    ((uint16_t)0x00C0) /*!< Sector Number 20  */
#define FLASH_Sector_21    ((uint16_t)0x00C8) /*!< Sector Number 21  */
#define FLASH_Sector_22    ((uint16_t)0x00D0) /*!< Sector Number 22  */
#define FLASH_Sector_23    ((uint16_t)0x00D8) /*!< Sector Number 23  */


typedef union
{
        struct
        {
                uint8_t data1;
                uint8_t data2;
                uint8_t data3;
                uint8_t data4;
        }bf;
        uint32_t data;
}fmc_data_t;

/* FMC sector information */
typedef struct
{
    uint32_t sector_name;                                         /*!< the name of the sector */
    uint32_t sector_num;                                          /*!< the number of the sector */
    uint32_t sector_size;                                         /*!< the size of the sector */
    uint32_t sector_start_addr;                                   /*!< the start address of the sector */
    uint32_t sector_end_addr;                                     /*!< the end address of the sector */
} fmc_sector_info_struct;

// 根据固件大小,计算出需要擦除的扇区

/* sector size */
#define SIZE_16KB                  ((uint32_t)0x00004000U)        /*!< size of 16KB*/
#define SIZE_64KB                  ((uint32_t)0x00010000U)        /*!< size of 64KB*/
#define SIZE_128KB                 ((uint32_t)0x00020000U)        /*!< size of 128KB*/
#define SIZE_256KB                 ((uint32_t)0x00040000U)        /*!< size of 256KB*/

/* FMC BANK address */
#define FMC_START_ADDRESS          FLASH_BASE                               /*!< FMC start address */
#define FMC_BANK0_START_ADDRESS    FMC_START_ADDRESS                        /*!< FMC BANK0 start address */
#define FMC_BANK1_START_ADDRESS    ((uint32_t)0x08100000U)                  /*!< FMC BANK1 start address (1MB)*/
#define FMC_SIZE                   (*(uint16_t *)0x1FFF7A22U)               /*!< FMC SIZE,存储在Flash中 */
#define FMC_END_ADDRESS            (FLASH_BASE + (FMC_SIZE * 1024) - 1)     /*!< FMC end address */
#define FMC_MAX_END_ADDRESS        ((uint32_t)0x08300000U)                  /*!< FMC maximum end address(3MB) */

/* FMC error message */
#define FMC_WRONG_SECTOR_NAME      ((uint32_t)0xFFFFFFFFU)        /*!< wrong sector name*/
#define FMC_WRONG_SECTOR_NUM       ((uint32_t)0xFFFFFFFFU)        /*!< wrong sector number*/
#define FMC_INVALID_SIZE           ((uint32_t)0xFFFFFFFFU)        /*!< invalid sector size*/
#define FMC_INVALID_ADDR           ((uint32_t)0xFFFFFFFFU)        /*!< invalid sector address*/

ErrStatus BSP_fmc_erase_sectors(uint32_t sec_num, int sec_count);

void BSP_fmc_write_Word_NoCheck(uint32_t address, uint32_t length, uint32_t* data_32);

void BSP_fmc_read_Wrod(uint32_t address, uint16_t length, uint32_t* Outbuff);

/*页擦除*/
void BSP_fmc_erase_page(uint32_t sector_addr, int index);
void BSP_fmc_erase_multi_pages(uint32_t sector_addr, int start_page_index, int page_num);

bool BSP_fmc_sectors_is_blank_Check(uint32_t address);
bool BSP_fmc_page_is_blank_Check(uint32_t address);

// bank擦除
void BSP_fmc_erase_bank1(void);

#endif



使用特权

评论回复
板凳
caizhiwei|  楼主 | 2024-6-2 08:47 | 只看该作者
再看看bsp_flash.c 中我们要实现的接口函数:
#include "bsp_flash.h"

const uint16_t FlashSec_Tab[24][2] =
{
        {0,FLASH_Sector_0},          
        {1,FLASH_Sector_1},
        {2,FLASH_Sector_2},
        {3,FLASH_Sector_3},   
        {4,FLASH_Sector_4},
        {5,FLASH_Sector_5},
        {6,FLASH_Sector_6},
        {7,FLASH_Sector_7},
        {8,FLASH_Sector_8},   
        {9,FLASH_Sector_9},
        {10,FLASH_Sector_10},
        {11,FLASH_Sector_11},
        {12,FLASH_Sector_12},
        {13,FLASH_Sector_13},  
        {14,FLASH_Sector_14},
        {15,FLASH_Sector_15},
        {16,FLASH_Sector_16},
        {17,FLASH_Sector_17},
        {18,FLASH_Sector_18},   
        {19,FLASH_Sector_19},
        {20,FLASH_Sector_20},
        {21,FLASH_Sector_21},
        {22,FLASH_Sector_22},   
        {23,FLASH_Sector_23},
};


static uint8_t Fmc_get_Sector_index( int sector_num)
{
        uint8_t index = 0;
        for(int i = 0; i < 24; i++)
        {       
                if(sector_num == FlashSec_Tab[i][1])
                {
                        index = i;
                        break;
                }
        }
        return index;
}


/*!
    \brief      连续擦除多个扇区
    \param[in]  sec_num 起始扇区号( FLASH_Sector_0 ~ FLASH_Sector_23)
    \param[in]  sec_count 扇区数量
    \retval     none
*/
ErrStatus BSP_fmc_erase_sectors(uint32_t sec_num, int sec_count)
{
        uint8_t index = 0;
        fmc_unlock();
        /* clear pending flags */
        fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_OPERR | FMC_FLAG_WPERR\
                                   | FMC_FLAG_PGMERR | FMC_FLAG_PGSERR);
        index = Fmc_get_Sector_index( sec_num );

        for(int i = 0; i < sec_count; i++)
        {
                fmc_sector_erase(CTL_SN(index + i));
                //printf("Erase sector index = %d!\r\n", (index+i));
        }
        /* lock the flash program erase controller */
        fmc_lock();
    return SUCCESS;
}


/********************************************************************
    \brief      从address 起始地址处 连续读出length个字到 Outbuff中,
    \param[1]   length 为字的数量, sizeof = length*4
    \param[2]   读出来的 Outbuff指针
    \retval     none
********************************************************************/
void BSP_fmc_read_Wrod(uint32_t address, uint16_t length, uint32_t* Outbuff)
{
    for(int i = 0; i < length; i++)
    {
                Outbuff[i] =  *(volatile uint32_t*)(address);  
        address = address + 4;
    }
}


/********************************************************************
    \brief      不检查 连续写入length个字到flash
    \param[1]   length 为字的数量, sizeof = length*4
    \param[2]   要写入的 u32 buff
    \retval     none
********************************************************************/
void BSP_fmc_write_Word_NoCheck(uint32_t address, uint32_t length, uint32_t* data_32)
{
    /* unlock the flash program erase controller */
    fmc_unlock();
    /* clear pending flags */
    fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_OPERR | FMC_FLAG_WPERR | FMC_FLAG_PGMERR | FMC_FLAG_PGSERR);

         /* write data_32 to the corresponding address */
    for(int i=0; i<length; i++)
    {
        if(FMC_READY == fmc_word_program(address, data_32[i]))
        {
            address = address + 4;
        }
    }
        fmc_lock();       
}


/********************************************************************
    \brief      擦除某个扇区中的某一页(4KB)
    \param[1]   Sector起始地址
    \param[2]   页 index: 0 ~ 31
    \retval     none
********************************************************************/
void BSP_fmc_erase_page(uint32_t sector_addr, int index)
{
        fmc_unlock();
        fmc_flag_clear(FMC_FLAG_END| FMC_FLAG_WPERR| FMC_FLAG_PGMERR);       
        fmc_page_erase(sector_addr + FMC_PAGE_SIZE*index);
        fmc_lock();
}


/********************************************************************
    \brief      擦除某个扇区中的多页(4KB)
    \param[1]   Sector起始地址
    \param[2]   start_page_index 起始页 : 0 ~ 63
        \param[3]   page_num 页数量 : 1 ~ 64(256kb)
        \note       跨扇区擦除,最多支持2个连续的扇区
    \retval     none
********************************************************************/
void BSP_fmc_erase_multi_pages(uint32_t sector_addr, int start_page_index, int page_num)
{
        fmc_unlock();
        fmc_flag_clear(FMC_FLAG_END| FMC_FLAG_WPERR| FMC_FLAG_PGMERR);       
        for(int i = start_page_index; i < (page_num + start_page_index); i++)
        {
                fmc_page_erase(sector_addr + FMC_PAGE_SIZE*i);
        }
        fmc_lock();
}


/********************************************************************
    \brief      擦除整个bank
    \param[1]   void
        \note       erase whole bank1       
    \retval     none
********************************************************************/
void BSP_fmc_erase_bank1(void)
{
        fmc_unlock();
        fmc_flag_clear(FMC_FLAG_END| FMC_FLAG_WPERR| FMC_FLAG_PGMERR);       
        fmc_bank1_erase();  
        fmc_lock();
}


/*!
    \brief      CHECK 4KB 页内容是否为空(FF)
    \param[in]  address  实际地址(Must 4KB 对齐)
    \param[in]  sec_count 扇区数量
    \retval     TRUE Blank   FALSE Not Blank
*/
bool BSP_fmc_page_is_blank_Check(uint32_t address)  
{
        uint32_t  readout = 0;               
          for(int i = 0; i < FMC_PAGE_SIZE; i+=4)
        {
                 readout = *(volatile int32_t*)(address + i);  
                 if(readout != 0xffffffff)
                 {
                         return false;
                 }
        }
        return true;
}



/*!
    \brief      CHECK 一个扇区内容是否为空(FF)
    \param[in]  address  实际地址(Must 扇区 对齐)
    \retval     TRUE Blank   FALSE Not Blank
        \note:     此函数只针对128kb大小的扇区
*/
bool BSP_fmc_sectors_is_blank_Check(uint32_t address)
{
        uint32_t buff;       
          for(int i = 0; i < SIZE_128KB; i+=4)
    {
                buff = *(volatile uint32_t*)(address + i);  
                if(buff != 0xffffffff)
                {
                        return  false;       
                }
    }
        return true;
}

/*!
    \brief      get the sector number, size and range of the given address
    \param[in]  address: The flash address
    \param[out] none
    \retval     fmc_sector_info_struct: The information of a sector
*/
fmc_sector_info_struct fmc_sector_info_get(uint32_t addr)
{
    fmc_sector_info_struct sector_info;
    uint32_t temp = 0x00000000U;
    if((FMC_START_ADDRESS <= addr)&&(FMC_END_ADDRESS >= addr)) {
        if ((FMC_BANK1_START_ADDRESS > addr)) {
            /* bank0 area */
            temp = (addr - FMC_BANK0_START_ADDRESS) / SIZE_16KB;
            if (4U > temp) {
                sector_info.sector_name = (uint32_t)temp;
                sector_info.sector_num = CTL_SN(temp);
                sector_info.sector_size = SIZE_16KB;
                sector_info.sector_start_addr = FMC_BANK0_START_ADDRESS + (SIZE_16KB * temp);
                sector_info.sector_end_addr = sector_info.sector_start_addr + SIZE_16KB - 1;
            } else if (8U > temp) {
                sector_info.sector_name = 0x00000004U;
                sector_info.sector_num = CTL_SN(4);
                sector_info.sector_size = SIZE_64KB;
                sector_info.sector_start_addr = 0x08010000U;
                sector_info.sector_end_addr = 0x0801FFFFU;
            } else {
                temp = (addr - FMC_BANK0_START_ADDRESS) / SIZE_128KB;
                sector_info.sector_name = (uint32_t)(temp + 4);
                sector_info.sector_num = CTL_SN(temp + 4);
                sector_info.sector_size = SIZE_128KB;
                sector_info.sector_start_addr = FMC_BANK0_START_ADDRESS + (SIZE_128KB * temp);
                sector_info.sector_end_addr = sector_info.sector_start_addr + SIZE_128KB - 1;
            }
        } else {
            /* bank1 area */
            temp = (addr - FMC_BANK1_START_ADDRESS) / SIZE_16KB;
            if (4U > temp) {
                sector_info.sector_name = (uint32_t)(temp + 12);
                sector_info.sector_num = CTL_SN(temp + 16);
                sector_info.sector_size = SIZE_16KB;
                sector_info.sector_start_addr = FMC_BANK0_START_ADDRESS + (SIZE_16KB * temp);
                sector_info.sector_end_addr = sector_info.sector_start_addr + SIZE_16KB - 1;
            } else if (8U > temp) {
                sector_info.sector_name = 0x00000010;
                sector_info.sector_num = CTL_SN(20);
                sector_info.sector_size = SIZE_64KB;
                sector_info.sector_start_addr = 0x08110000U;
                sector_info.sector_end_addr = 0x0811FFFFU;
            } else if (64U > temp){
                temp = (addr - FMC_BANK1_START_ADDRESS) / SIZE_128KB;
                sector_info.sector_name = (uint32_t)(temp + 16);
                sector_info.sector_num = CTL_SN(temp + 20);
                sector_info.sector_size = SIZE_128KB;
                sector_info.sector_start_addr = FMC_BANK1_START_ADDRESS + (SIZE_128KB * temp);
                sector_info.sector_end_addr = sector_info.sector_start_addr + SIZE_128KB - 1;
            } else {
                temp = (addr - FMC_BANK1_START_ADDRESS) / SIZE_256KB;
                sector_info.sector_name = (uint32_t)(temp + 20);
                sector_info.sector_num = CTL_SN(temp + 8);
                sector_info.sector_size = SIZE_256KB;
                sector_info.sector_start_addr = FMC_BANK1_START_ADDRESS + (SIZE_256KB * temp);
                sector_info.sector_end_addr = sector_info.sector_start_addr + SIZE_256KB - 1;
            }
        }
    } else {
        /* invalid address */
        sector_info.sector_name = FMC_WRONG_SECTOR_NAME;
        sector_info.sector_num = FMC_WRONG_SECTOR_NUM;
        sector_info.sector_size = FMC_INVALID_SIZE;
        sector_info.sector_start_addr = FMC_INVALID_ADDR;
        sector_info.sector_end_addr = FMC_INVALID_ADDR;
    }
    return sector_info;
}

/*!
    \brief      get the sector number by a given sector name
    \param[in]  address: a given sector name
    \param[out] none
    \retval     uint32_t: sector number
*/
uint32_t sector_name_to_number(uint32_t sector_name)
{
    if(11 >= sector_name){
        return CTL_SN(sector_name);
    }else if(23 >= sector_name){
        return CTL_SN(sector_name + 4);
    }else if(27 >= sector_name){
        return CTL_SN(sector_name - 12);
    }else{
        while(1);
    }
}


int bsw_drv_flash_erase ( uint32_t address )
{
   fmc_sector_info_struct sector_info;
//    printf("\r\nFMC erase operation:\n");
    /* get information about the sector in which the specified address is located */
    sector_info = fmc_sector_info_get(address);
    if(FMC_WRONG_SECTOR_NAME == sector_info.sector_name){
//        printf("\r\nWrong address!\n");
        return CL_FAIL;
    }else{
//        printf("\r\nErase start ......\n");
        /* unlock the flash program erase controller */
        fmc_unlock();
        /* clear pending flags */
        fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_OPERR | FMC_FLAG_WPERR | FMC_FLAG_PGMERR | FMC_FLAG_PGSERR);
        /* wait the erase operation complete*/
        if(FMC_READY != fmc_sector_erase(sector_info.sector_num)){
           return CL_FAIL;
        }
        /* lock the flash program erase controller */
        fmc_lock();
//        printf("\r\nAddress 0x%08X is located in the : SECTOR_NUMBER_%d !\n", address, sector_info.sector_name);
//        printf("\r\nSector range: 0x%08X to 0x%08X\n", sector_info.sector_start_addr, sector_info.sector_end_addr);
//        printf("\r\nSector size: %d KB\n", (sector_info.sector_size/1024));
//        printf("\r\nErase success!\n");
//        printf("\r\n");
                return CL_OK;
    }
}

//写数据前会自动先擦除
int bsw_drv_flash_write ( uint32_t address , uint8_t* buf , uint16_t length )
{
    uint16_t writeNum;
    uint32_t data = 0;

        fmc_sector_info_struct start_sector_info;
    fmc_sector_info_struct end_sector_info;
    uint32_t sector_num,i;
   
//    printf("\r\nFMC word programe operation:\n");
    /* unlock the flash program erase controller */
    fmc_unlock();
    /* clear pending flags */
    fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_OPERR | FMC_FLAG_WPERR | FMC_FLAG_PGMERR | FMC_FLAG_PGSERR);
    /* get the information of the start and end sectors */
    start_sector_info = fmc_sector_info_get(address);
    end_sector_info = fmc_sector_info_get(address + length);
    /* erase sector */
    for(i = start_sector_info.sector_name; i <= end_sector_info.sector_name; i++){
        sector_num = sector_name_to_number(i);
        if(FMC_READY != fmc_sector_erase(sector_num)){
            return CL_FAIL;
        }
    }

        if ( length % 4 == 0 )
    {
        writeNum = length / 4;
    }
    else
    {
        writeNum = length / 4 + 1;
    }

    /* write data_32 to the corresponding address */
    for(i=0; i<writeNum; i++){

                memcpy ( &data , buf + i * 4 , 4 );
        if(FMC_READY == fmc_word_program(address, data)){
            address = address + 4;
        }else{
            return CL_FAIL;
        }
    }
    /* lock the flash program erase controller */
    fmc_lock();
//    printf("\r\nWrite complete!\n");
//    printf("\r\n");
        return CL_OK;
}

int bsw_drv_flash_read ( uint32_t address , uint8_t* data_8 , uint16_t length )
{
        uint16_t i;

    if ( address < FLASH_BASE )  //非法地址
    {
        return CL_FAIL;
    }
   
    for(i=0; i<length; i++){
        data_8[i] = (*(volatile uint8_t *)(uint32_t)(address));
        address++;
    }

        return CL_OK;
}

使用特权

评论回复
地板
caizhiwei|  楼主 | 2024-6-2 08:53 | 只看该作者
再说说RTC吧,RTC不仅需要提供简单的读写接口,还需要支持时间戳的提取,UTC时间转换等等,
头文件中要定义一个结构体,方便与time.h的C库时间做转换。
#ifndef __BSP_RTC_H__
#define __BSP_RTC_H__

#include "gd32f4xx.h"
#include <stdio.h>
#include <stdbool.h>
#include <time.h>

#define RTC_CLOCK_SOURCE_LXTAL   // RTC使用外部低速时钟

/*ms 级精确时间 */
typedef struct
{
   int tm_sec;         /* 秒,范围从 0 到 59      */
   int tm_min;         /* 分,范围从 0 到 59      */
   int tm_hour;        /* 小时,范围从 0 到 23    */
   int tm_mday;        /* 月份中的第几天,范围从 1 到 31 */
   int tm_mon;         /* 月,范围从1到12(和系统time不同) */
   int tm_year;        /* 2000年起的年份(和系统time不同)*/
   int tm_wday;        /* 一周中的第几天,范围从 1 到 7 (和系统time不同)*/
   int tm_yday;        /* 一年中的第几天,范围从 0 到 365*/
   int tm_subsec;      /* ms */
}hw_rtc;


//RTC初始化
void BSP_RTC_Init(void);
void BSP_RTC_time_Set(uint8_t year, uint8_t month, uint8_t date,\
              uint8_t hour,  uint8_t minute,  uint8_t second);
void BSP_RTC_time_Get( hw_rtc *p );
void BSP_RTC_str_time_Get(char *buff);
uint32_t BSP_RTC_count_Get(void);


#endif

使用特权

评论回复
5
caizhiwei|  楼主 | 2024-6-2 08:54 | 只看该作者
如何调用C库里的mktime() 函数?上干货吧,直接分享源码:
#include "bsp_rtc.h"



//BCD转二进制
uint8_t RTC_BCDToByte(uint8_t Value)
{
        uint8_t bit[2];
        uint8_t retValue;
        bit[1] = (Value >> 4) & 0x0F;
        bit[0] = Value & 0x0F;
        retValue = bit[1] * 10 + bit[0];
        return (retValue);  
}

//二进制转BCD
uint8_t RTC_ByteToBCD(uint8_t Value)
{
    uint8_t bit[2];
        uint8_t retValue;
        bit[0] = Value % 10;
        bit[1] = Value % 100 / 10;
        retValue = (bit[1] << 4) | bit[0];
        return (retValue);   
}

//RTC预配置功能
void rtc_pre_config(void)
{  
    __IO uint32_t prescaler_a = 0, prescaler_s = 0;

#if defined (RTC_CLOCK_SOURCE_IRC32K)
    rcu_osci_on(RCU_IRC32K);
    rcu_osci_stab_wait(RCU_IRC32K);
    rcu_rtc_clock_config(RCU_RTCSRC_IRC32K);

    prescaler_s = 0x13F;
    prescaler_a = 0x63;
   
#elif defined (RTC_CLOCK_SOURCE_LXTAL)
   
    rcu_osci_on(RCU_LXTAL);
    rcu_osci_stab_wait(RCU_LXTAL);
    rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);

    prescaler_s = 0xFF; // ck_spre频率 = ck_apre/(prescaler_s+1) = 256/(255+1)=1HZ
    prescaler_a = 0x7F; // ck_apre频率 = RTC_CLOCK/(prescaler_a+1) = 32768/(127+1)=256HZ
#else
    #error RTC clock source should be defined.
#endif /* RTC_CLOCK_SOURCE_IRC32K */

    rcu_periph_clock_enable(RCU_RTC);
    //等待 同步机制
        while(SUCCESS != rtc_register_sync_wait())
        {
        };       
}

//RTC初始化
void BSP_RTC_Init(void)
{
        /* enable access to RTC registers in Backup domain*/
        rcu_periph_clock_enable(RCU_PMU);       
        pmu_backup_write_enable();
        rtc_pre_config();       
}

/*******************************************************************!
    \brief  使用固定格式设置时间,默认东八区
    \param[in]  year: 0 ~ 99 (十进制) From 2000
                            month: 1 ~ 12 (十进制)
                            date: 1 ~ 31 (十进制)
                            day_of_week:1 ~7 (十进制)
                            hour: 0 ~ 23
                            minute: 0 ~ 59
                            second: 0 ~ 59
    \param[out] none
    \retval     none
*******************************************************************/
void BSP_RTC_time_Set(uint8_t year, uint8_t month, uint8_t date,\
              uint8_t hour, uint8_t minute,  uint8_t second)
{
    if((year > 99) || (month > 12) || (date > 31) )
    {
        return;
    }
    if((month == 0) || (date == 0))
    {
        return;
    }
    if((hour > 59) || (minute > 59) || (second > 59))
    {
        return;
    }  
    //设置RTC时间值
    rtc_parameter_struct rtc_initpara;
    rtc_initpara.factor_asyn = 0x7F;
    rtc_initpara.factor_syn = 0xFF;
    rtc_initpara.year = RTC_ByteToBCD(year);
    rtc_initpara.day_of_week = RTC_SATURDAY;  // 一般用不到

    rtc_initpara.month = RTC_ByteToBCD(month);
    rtc_initpara.date = RTC_ByteToBCD(date & 0x1F);  // 防止超过31
    rtc_initpara.display_format = RTC_24HOUR;
    rtc_initpara.am_pm = RTC_AM;
   
    rtc_initpara.hour = RTC_ByteToBCD(hour);
        rtc_initpara.minute = RTC_ByteToBCD(minute);
        rtc_initpara.second = RTC_ByteToBCD(second);

        //RTC当前时间配置
    if(ERROR == rtc_init(&rtc_initpara))
        {
        printf("\n\r** RTC time configuration failed! **\n\r");
    }
        else
        {
        printf("\n\r** RTC time configuration success! **\n\r");
    }
}

/* 从硬件寄存器中获取时间*/
void BSP_RTC_time_Get( hw_rtc *p )
{
    uint32_t time_subsecond = 0;        
    rtc_parameter_struct rtc_read;  
    rtc_current_time_get(&rtc_read);
    time_subsecond = rtc_subsecond_get();  
    time_subsecond = (1000 - (time_subsecond*1000+1000)/400);   
    p->tm_year = RTC_BCDToByte(rtc_read.year);  // form 2000
    p->tm_mon = RTC_BCDToByte(rtc_read.month);
    p->tm_mday = RTC_BCDToByte(rtc_read.date);
    p->tm_hour = RTC_BCDToByte(rtc_read.hour);
    p->tm_min = RTC_BCDToByte(rtc_read.minute);
    p->tm_sec = RTC_BCDToByte(rtc_read.second);
    p->tm_wday = 1; /*星期预留不使用*/
    p->tm_subsec = time_subsecond;   
}


/*
获取RTC硬件时间,UTC-0时区
*/
void BSP_RTC_str_time_Get(char *buff)
{
    hw_rtc hw_RTC;   
    BSP_RTC_time_Get(&hw_RTC);
    sprintf(buff, "20%02d-%02d-%02dT%02d:%02d:%02d", hw_RTC.tm_year,hw_RTC.tm_mon,\
      hw_RTC.tm_mday,hw_RTC.tm_hour, hw_RTC.tm_min, hw_RTC.tm_sec);   
}


/*************************************************************************
* Function     :从RTC获取时间戳 (UTC-0 Zone)年月日时分秒-->时间戳
* Description  :时间戳:公元1970年1月1日(00:00:00 GMT)(格林威治时间)秒
                  算起至今的UTC时间所经过的秒数.(timestamp没有时区之分)
                  UTC(Coodinated Universal Time),协调世界时,又称世界统一时间
* Input        :void
* Output       :None
* Return       :时间戳
************************************************************************/
uint32_t BSP_RTC_count_Get(void)
{
        struct tm stmT;
    hw_rtc hw_RTC;
    uint32_t count = 0;
    BSP_RTC_time_Get(&hw_RTC);
       
    stmT.tm_year = hw_RTC.tm_year + 100;
    stmT.tm_mon = hw_RTC.tm_mon - 1;
    stmT.tm_hour = hw_RTC.tm_hour;
    stmT.tm_mday = hw_RTC.tm_mday;
    stmT.tm_min = hw_RTC.tm_min;
    stmT.tm_sec = hw_RTC.tm_sec;
    stmT.tm_isdst = 0;
    count = mktime( &stmT );
    return count;
}



使用特权

评论回复
6
trucyw| | 2024-6-3 08:20 | 只看该作者
写的确实不错

使用特权

评论回复
7
caizhiwei|  楼主 | 2024-6-3 08:39 | 只看该作者

哈哈,一起切磋交流,这些代码都是项目级代码,后续还会更新一些,bsp其实不多,但是有了这些通用接口会方便很多

使用特权

评论回复
8
caizhiwei|  楼主 | 2024-6-3 13:06 | 只看该作者
SPI驱动来了
#include "bsp_spi.h"

//3路独立SPI接口:
// 1. SPI0 --> SPI Flash
// 2. SPI2 --> W5500
// 3. SPI3 --> RN8302
// ALL SPI interfaces with a frequency of up to 30 MHz

/*
SPI0_NSS   PA4
SPI0_SCK   PA5
SPI0_MISO PA6
SPI0_MOSI PA7
SPI0 --> APB2 = 120M
*/
void BSP_SPI0_Init(void)
{
        spi_parameter_struct  spi_init_struct;
        rcu_periph_clock_enable(RCU_GPIOA);
        rcu_periph_clock_enable(RCU_SPI0);
               
    /* SPI0 GPIO config AF5 */
    gpio_af_set(GPIOA, GPIO_AF_5, GPIO_PIN_5 | GPIO_PIN_6 |GPIO_PIN_7);
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5 | GPIO_PIN_6 |GPIO_PIN_7);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5 | GPIO_PIN_6 |GPIO_PIN_7);

     /* set SPI0_NSS as GPIO*/
    gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_4);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4);
   
    /* SPI0 parameter config */
    spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;
    spi_init_struct.device_mode          = SPI_MASTER;
    spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE;//FLASH芯片支持SPI模式0及模式3,据此设置CPOL CPHA
    spi_init_struct.nss                  = SPI_NSS_SOFT;
    spi_init_struct.prescale             = SPI_PSC_8 ;  // 15M
    spi_init_struct.endian               = SPI_ENDIAN_MSB;
    spi_init(SPI0, &spi_init_struct);
        spi_enable(SPI0);
}


/*
SPI1_MOSI  PB15
SPI1_MISO  PB14
SPI1_SCK  PB13
SPI1_NSS  PB12
*/
void BSP_SPI1_Init(void)
{
        spi_parameter_struct  spi_init_struct;
        rcu_periph_clock_enable(RCU_GPIOB);
        rcu_periph_clock_enable(RCU_SPI1);
               
    /* SPI1 GPIO config AF5 */
    gpio_af_set(GPIOB, GPIO_AF_5, GPIO_PIN_13 | GPIO_PIN_14 |GPIO_PIN_15);
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13 | GPIO_PIN_14 |GPIO_PIN_15);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13 | GPIO_PIN_14 |GPIO_PIN_15);

    /* set SPI1_NSS as GPIO*/
    gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_12);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);
   
    /* SPI1 parameter config */
    spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;
    spi_init_struct.device_mode          = SPI_MASTER;
    spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;
    spi_init_struct.nss                  = SPI_NSS_SOFT;
    spi_init_struct.prescale             = SPI_PSC_8;
    spi_init_struct.endian               = SPI_ENDIAN_MSB;
    spi_init(SPI1, &spi_init_struct);
        spi_enable(SPI1);
}


/*  For w5500
SPI2_MOSI PC12
SPI2_MISO PC11
SPI2_SCK  PC10
SPI2_NSS  PA15

spi2 form APB1,Fmax=60MHz

*/
void BSP_SPI2_Init(void)
{
        spi_parameter_struct  spi_init_struct;
        rcu_periph_clock_enable(RCU_GPIOC);
        rcu_periph_clock_enable(RCU_SPI2);
               
    /* SPI1 GPIO config AF6 */
    gpio_af_set(GPIOC, GPIO_AF_6, GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12);
    gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12);

    /* set SPI2_NSS as GPIO*/
    gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_15);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_15);
   
    /* SPI1 parameter config */
    spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;
    spi_init_struct.device_mode          = SPI_MASTER;
    spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE; //or SPI_CK_PL_LOW_PH_1EDGE is OK, see datasheet.
    spi_init_struct.nss                  = SPI_NSS_SOFT;
    spi_init_struct.prescale             = SPI_PSC_4;   // 15Mbps
    spi_init_struct.endian               = SPI_ENDIAN_MSB;
    spi_init(SPI2, &spi_init_struct);
        spi_enable(SPI2);
}


/*  For rn8302b
SPI3_NSS  PE11
SPI3_SCK  PE12
SPI3_MISO PE13
SPI3_MOSI PE14
rn8302b Max spi speed = 3.5Mbit/s from datasheet
spi3 APB2 = 120Mhz
*/
void BSP_SPI3_Init(void)
{
    spi_parameter_struct spi_init_struct;
    rcu_periph_clock_enable(RCU_GPIOE);
        rcu_periph_clock_enable(RCU_SPI3);
   
    /* configure SPI3 GPIO AF5*/
    gpio_af_set(GPIOE, GPIO_AF_5, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14);
    gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14);
    gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14);

    /* set SPI3_NSS as GPIO*/
    gpio_mode_set(GPIOE, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_11);
    gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_11);

    /* configure SPI3 parameter */
    spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;
    spi_init_struct.device_mode          = SPI_MASTER;
    spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_2EDGE;
    spi_init_struct.nss                  = SPI_NSS_SOFT;
    spi_init_struct.prescale             = SPI_PSC_64;
    spi_init_struct.endian               = SPI_ENDIAN_MSB;
    spi_init(SPI3, &spi_init_struct);
    spi_enable(SPI3);  
}


uint8_t BSP_SPIX_SendRecv(uint32_t phy_spix, uint8_t Byte)
{
        //int retry = 0;
        /* loop while data register in not empty */
    while(RESET == spi_i2s_flag_get(phy_spix, SPI_FLAG_TBE));
    /* send byte through the SPIx peripheral */
    spi_i2s_data_transmit(phy_spix, Byte);

    /* wait to receive a byte */
    while(RESET == spi_i2s_flag_get(phy_spix, SPI_FLAG_RBNE))
        {
//                retry++;
//                for(int i = 0; i < 0xfff; i++){};
//                if(retry > 8000)
//                {
//                        return 0;
//                }
        };
    /* return the byte read from the SPI bus */
    return(spi_i2s_data_receive(phy_spix));
}

使用特权

评论回复
9
一路向北lm| | 2024-6-17 11:50 | 只看该作者
我感觉可以把rtos那层去掉

使用特权

评论回复
10
yangxiaor520| | 2024-6-19 20:13 | 只看该作者
直接把BSP库复制过来,不用的外设屏蔽掉。

使用特权

评论回复
11
9dome猫| | 2024-6-30 23:18 | 只看该作者
GD32F470支持按页(每页4KB)擦除,这对于文件系统(如FATFS)的移植非常方便。

使用特权

评论回复
12
9dome猫| | 2024-6-30 23:19 | 只看该作者
对于1MB容量的GD32F470VGT型号,没有定义第8、9、10、11扇区,因此软件上兼容1到3MB型号。

使用特权

评论回复
13
caizhiwei|  楼主 | 2024-7-8 11:01 | 只看该作者
9dome猫 发表于 2024-6-30 23:18
GD32F470支持按页(每页4KB)擦除,这对于文件系统(如FATFS)的移植非常方便。 ...

小页对于flash模拟eeprom也方便,对

文件系统底层效率更高

使用特权

评论回复
14
caizhiwei|  楼主 | 2024-7-8 11:02 | 只看该作者
这是以前瞎整的,都没时间弄了,后续有空了,再整理一下,把代码压缩包传上来~

使用特权

评论回复
15
有何不可0365| | 2024-7-31 21:34 | 只看该作者
GD芯片虽然兼容ST,但是不是100%兼容,

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

100

主题

857

帖子

14

粉丝