打印
[STM32F1]

stm32 Flash读写[库函数]

[复制链接]
1378|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
人丑没人疼|  楼主 | 2016-8-29 00:38 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
通过对stm32内部的flash的读写可以实现对stm32的编程操作。

        stm32 的内置可编程Flash在许多场合具有十分重要的意义。如其支持ICP特性使得开发人员对stm32可以警醒调试开发,可以通过JTAG和SWD接口对stm32进行程序烧写;支持IAP特性使得开发人员可以在stm32运行程序的时候对其内部程序进行更新操作。对一些对数据安全有要求的场合,可编程FLASH可以结合stm32内部唯一的身份标识实现各种各样的防**方案。并且stm32的FLASH在一些轻量级的防掉电存储方案中也有立足之地。

        stm32的FLASH分为主存储块和信息块。主存储块用于保存具体的程序代码和用户数据,信息块用于负责由stm32出厂是放置2KB的启动程序(Bootloader)和512B的用户配置信息区。

    主存储块是以页为单位划分的,一页大小为1KB。范围为从地址0x08000000开始的128KB内。

    对Flash 的写入操作要 “先擦除后写入”的原则;
    stm32的内置flash 编程操作都是以页为单位写入的,而写入的操作必须要以16位半字宽度数据位单位,允许跨页写,写入非16位数据时将导致stm32内部总线错误。
    进行内置flash读写时,必须要打开内部Rc振荡器。


main.c:
#include "stm32f10x.h"#include "stdio.h"#define         PRINTF_ON  1void RCC_Configuration(void);void GPIO_Configuration(void);void USART_Configuration(void);u32 count=0;u16 data[5]={0x0001,0x0002,0x0003,0x0004,0x0005};int main(void){          RCC_Configuration();        GPIO_Configuration();        USART_Configuration();        RCC_HSICmd(ENABLE);        FLASH_Unlock();        FLASH_ClearFlag(FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);        FLASH_ErasePage(0x8002000);        while(count < 5)        {                FLASH_ProgramHalfWord((0x8002000 +count*2),data[count]);  //flash  为一个字节存储,16位数据必须地址加2                count++;                }        FLASH_Lock();        count = 0;        printf("\r\n The Five Data Is : \r\n");        while(count < 5)        {                                printf("\r %d \r",*(u8 *)(0x8002000 + count*2));          //读取方法                count++;                }        while(1);        }void GPIO_Configuration(void){        GPIO_InitTypeDef        GPIO_InitStructure;         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;          GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;          GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;                                  GPIO_Init(GPIOA , &GPIO_InitStructure);                   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;          GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;                                  GPIO_Init(GPIOA , &GPIO_InitStructure); }void RCC_Configuration(void){        /* 定义枚举类型变量 HSEStartUpStatus */        ErrorStatus HSEStartUpStatus;          /* 复位系统时钟设置*/          RCC_DeInit();          /* 开启HSE*/          RCC_HSEConfig(RCC_HSE_ON);          /* 等待HSE起振并稳定*/          HSEStartUpStatus = RCC_WaitForHSEStartUp();        /* 判断HSE起是否振成功,是则进入if()内部 */          if(HSEStartUpStatus == SUCCESS)          {            /* 选择HCLK(AHB)时钟源为SYSCLK 1分频 */            RCC_HCLKConfig(RCC_SYSCLK_Div1);             /* 选择PCLK2时钟源为 HCLK(AHB) 1分频 */            RCC_PCLK2Config(RCC_HCLK_Div1);             /* 选择PCLK1时钟源为 HCLK(AHB) 2分频 */            RCC_PCLK1Config(RCC_HCLK_Div2);            /* 设置FLASH延时周期数为2 */            FLASH_SetLatency(FLASH_Latency_2);            /* 使能FLASH预取缓存 */            FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);            /* 选择锁相环(PLL)时钟源为HSE 1分频,倍频数为9,则PLL输出频率为 8MHz * 9 = 72MHz */            RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);            /* 使能PLL */             RCC_PLLCmd(ENABLE);            /* 等待PLL输出稳定 */            while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);            /* 选择SYSCLK时钟源为PLL */            RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);            /* 等待PLL成为SYSCLK时钟源 */            while(RCC_GetSYSCLKSource() != 0x08);          }           /* 打开APB2总线上的GPIOA时钟*/          RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE);        //RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);        //RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);        //RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP|RCC_APB1Periph_WWDG, ENABLE);                } void USART_Configuration(void){        USART_InitTypeDef USART_InitStructure;        USART_ClockInitTypeDef USART_ClockInitStructure;        USART_ClockInitStructure.USART_Clock = USART_Clock_Disable;        USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low;        USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge;                                                                                                                                                              USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable;        USART_ClockInit(USART1 , &USART_ClockInitStructure);        USART_InitStructure.USART_BaudRate = 9600;        USART_InitStructure.USART_WordLength = USART_WordLength_8b;        USART_InitStructure.USART_StopBits = USART_StopBits_1;        USART_InitStructure.USART_Parity = USART_Parity_No;        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;        USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;        USART_Init(USART1,&USART_InitStructure);         USART_Cmd(USART1,ENABLE);}#if         PRINTF_ONint fputc(int ch,FILE *f){        USART_SendData(USART1,(u8) ch);        while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);        return ch;}#endif

沙发
人丑没人疼|  楼主 | 2016-8-29 00:39 | 只看该作者
  这里使用的stm32型号为 stm32f103c8te,只有GPIOA、B两组通用I/O口,共48个引脚。引脚图如下:



1. VDDA引脚

VDDA用于模拟电路(ADC采样).VDD若直接与之连接,数字电路的干扰会窜入模拟电路的,如果想保证ADC采样的精度可以在VDD和VDDA间加入滤波电路。



很多开发板都是直接和VDD连接,也可以运行。

由于STM32F103 系列单片机的内部高速RC 振荡器(HSI)由VDDA、VSSA 供电,故即使不使用单片机自带的A/D 转换器,也必须保证VDDA、VSSA 的供电,否则STM32F103不能正常启动。


2.VBAT引脚

备用电源引脚,当VDD关闭时,实时时钟(RTC)和 备用寄存器可以从VBAT引脚获得电压。


3.Boot 选择
通过 Boot0 和 Boot1两个引脚可以选择stm32启动模式,参见下图:


3.Jtag引脚接法如下:





4.电压转换

单片机系统中常用 5V和3.3V电压,可以通过电压调节器实现电压转换。

LM1117是一个低压差电压调节器系列。其压差在1.2V输出,负载电流为800mA时为1.2V。它与国家半导体的工业标准器件LM317有相同的管脚排列。LM1117有可调电压的版本,通过2个外部电阻可实现1.25~13.8V输出电压范围。另外还有5个固定电压输出(1.8V、2.5V、2.85V、3.3V和5V)的型号。输出端需要一个至少10uF的钽电容来改善瞬态响应和稳定性。

Lm1117电路如下:






使用特权

评论回复
板凳
人丑没人疼|  楼主 | 2016-8-29 00:41 | 只看该作者
读写SD是嵌入式系统中一个比较基础的功能,在很多应用中都可以用得上SD卡。折腾了几天,总算移植成功了 最新版Fatfs(Fatfs R0.09) ,成功读写SD卡下文件。

   

    FatFs 是一个通用的文件系统模块,用于在小型嵌入式系统中实现FAT文件系统。 FatFs 的编写遵循ANSI C,因此不依赖于硬件平台。它可以嵌入到便宜的微控制器中,如 8051, PIC, AVR, SH, Z80, H8, ARM 等等,不需要做任何修改。

1. SD卡/TF卡 硬件接口

SD卡有两种操作接口,SDIO和SPI。 使用SDIO口的速度比较快,SPI的速度比较慢 。

    SD卡引脚描述如下:                                                            SD卡SPI接法如下:

我使用的是正点原子的开发板,所以采用的是SPI接口的模式。

    TF卡SDIO 模式和SPI模式 引脚定义:



        可以发现Micro SD卡只有8个引脚是因为比SD卡少了一个Vss。使用TF转SD的卡套套在Micro SD卡上,这样一来大小就和SD卡一样大,这时候卡套上的9个引脚就和SD卡一样了,你可以完全当做SD卡来操作。

2. SD卡底层驱动

SD卡的操作比较复杂,需要多看看一些文档 。 这里附上SD底层驱动代码,代码说明详见注释


3. Fatfs 移植

FatFs 软件包中相关文件:

        ffconf.h     FatFs 模块配置文件
        ff.h            FatFs 和应用模块公用的包含文件
        ff.c            FatFs 模块
        diskio.h     FatFs and disk I/O 模块公用的包含文件
        integer.h   数据类型定义
        option      可选的外部功能
        diskio.c     FatFs 与disk I/O 模块接口层文件(不属于 FatFs 需要由用户提供)

FatFs 配置,文件系统的配置项都在 ffconf.h 文件之中:

        (1) _FS_TINY :这个选项在R0.07 版本之中开始出现,在之前的版本都是以独立的文件出现,现在通过一个宏来修改使用起来更方便;
        (2) _FS_MINIMIZE、_FS_READONLY、_USE_STRFUNC、_USE_MKFS、_USE_FORWARD 这些宏是用来对文件系统进行裁剪
        (3) _CODE_PAGE :本选项用于设置语言码的类型
        (4) _USE_LFN :取值为0~3,主要用于长文件名的支持及缓冲区的动态分配:
                0:不支持长文件名;
                1:支持长文件名存储的静态分配,一般是存储在BSS 段;
                2:支持长文件名存储的动态分配,存储在栈上;
                3:支持长文件名存储的动态分配,存储在堆上。
        (5) _MAX_LFN :可存储长文件的最大长度,其值一般为(12~255),但是缓冲区一般占(_MAX_LFN + 1) * 2 bytes;
        (6) _LFN_UNICODE :为1 时才支持unicode 码;
        (7) _FS_RPATH :R0.08a 版本改动配置项,取值范围0~2:
                0:去除相对路径支持和函数;
                1:开启相对路径并且开启f_chdrive()和f_chdir()两个函数;
                2:在1 的基础上添加f_getcwd()函数。
        (8) _VOLUMES :支持的逻辑设备数目;
        (9) _MAX_SS :扇区缓冲的最大值,其值一般为512;
        (10) _MULTI_PARTITION:定义为1 时,支持磁盘多个分区;
        (11) _USE_ERASE :R0.08a 新加入的配置项,设置为1 时,支持扇区擦除;
        (12) _WORD_ACCESS :如果定义为1,则可以使用word 访问;
        (13) _FS_REENTRANT :定义为1 时,文件系统支持重入,但是需要加上跟操作系统信号量相关的几个函数,函数在syscall.c 文件中;
        (14) _FS_SHARE :文件支持的共享数目。


Fatfs 开源文件系统 从R0.07e 之后 版本开始就不再提供底层接口文件 diskio.c 模板,这里附上根据
以上SD卡底层驱动对应的 diskio.c 源码:

#include "common.h"/*-----------------------------------------------------------------------*//* Inidialize a Drive                                                    */DSTATUS disk_initialize (        BYTE drv                                /* Physical drive nmuber (0..) */){    u8 state;    if(drv)    {        return STA_NOINIT;  //仅支持磁盘0的操作    }    state = SD_Init();    if(state == STA_NODISK)    {        return STA_NODISK;    }    else if(state != 0)    {        return STA_NOINIT;  //其他错误:初始化失败    }    else    {        return 0;           //初始化成功    }}/*-----------------------------------------------------------------------*//* Return Disk Status                                                    */DSTATUS disk_status (        BYTE drv                /* Physical drive nmuber (0..) */){    if(drv)    {        return STA_NOINIT;  //仅支持磁盘0操作    }    //检查SD卡是否插入    if(!SD_DET())    {        return STA_NODISK;    }    return 0;}/*-----------------------------------------------------------------------*//* Read Sector(s)                                                        */DRESULT disk_read (        BYTE drv,                /* Physical drive nmuber (0..) */        BYTE *buff,                /* Data buffer to store read data */        DWORD sector,        /* Sector address (LBA) */        BYTE count                /* Number of sectors to read (1..255) */){        u8 res=0;    if (drv || !count)    {            return RES_PARERR;  //仅支持单磁盘操作,count不能等于0,否则返回参数错误    }    if(!SD_DET())    {        return RES_NOTRDY;  //没有检测到SD卡,报NOT READY错误    }                if(count==1)            //1个sector的读操作          {                                                        res = SD_ReadSingleBlock(sector, buff);          }                                                    else                    //多个sector的读操作         {                                                        res = SD_ReadMultiBlock(sector, buff, count);    }                                                        /*    do                               {                                                  if(SD_ReadSingleBlock(sector, buff)!=0)        {                                                  res = 1;                                       break;                                     }                                              buff+=512;                                 }while(--count);                                             */    //处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值    if(res == 0x00)    {        return RES_OK;    }    else    {        return RES_ERROR;    }}/*-----------------------------------------------------------------------*//* Write Sector(s)                                                       */#if _READONLY == 0DRESULT disk_write (        BYTE drv,                        /* Physical drive nmuber (0..) */        const BYTE *buff,        /* Data to be written */        DWORD sector,                /* Sector address (LBA) */        BYTE count                        /* Number of sectors to write (1..255) */){        u8 res;    if (drv || !count)    {            return RES_PARERR;  //仅支持单磁盘操作,count不能等于0,否则返回参数错误    }    if(!SD_DET())    {        return RES_NOTRDY;  //没有检测到SD卡,报NOT READY错误    }    // 读写操作    if(count == 1)    {        res = SD_WriteSingleBlock(sector, buff);    }    else    {        res = SD_WriteMultiBlock(sector, buff, count);    }    // 返回值转换    if(res == 0)    {        return RES_OK;    }    else    {        return RES_ERROR;    }}#endif /* _READONLY *//*-----------------------------------------------------------------------*//* Miscellaneous Functions                                               */DRESULT disk_ioctl (        BYTE drv,                /* Physical drive nmuber (0..) */        BYTE ctrl,                /* Control code */        void *buff                /* Buffer to send/receive control data */){    DRESULT res;    if (drv)    {            return RES_PARERR;  //仅支持单磁盘操作,否则返回参数错误    }        //FATFS目前版本仅需处理CTRL_SYNC,GET_SECTOR_COUNT,GET_BLOCK_SIZ三个命令    switch(ctrl)    {    case CTRL_SYNC:        SD_CS_ENABLE();        if(SD_WaitReady()==0)        {            res = RES_OK;        }        else        {            res = RES_ERROR;        }        SD_CS_DISABLE();        break;            case GET_BLOCK_SIZE:        *(WORD*)buff = 512;        res = RES_OK;        break;    case GET_SECTOR_COUNT:        *(DWORD*)buff = SD_GetCapacity();        res = RES_OK;        break;    default:        res = RES_PARERR;        break;    }    return res;}/*-----------------------------------------------------------------------*//* User defined function to give a current time to fatfs module          *//* 31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */                                                                                                                                                                                                                                          /* 15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */                                                                                                                                                                                                                                                DWORD get_fattime (void){  return 0;}


这里的结构函数为Fatfs提供和SD卡的通信接口。 在 最新版本的Fatfs中还加入了对中文文件名的支持,需要修改  ffconf.h   
    #define _CODE_PAGE    936   //- Simplified Chinese GBK (DBCS, OEM, Windows)
同时应该添加  option/cc936.c文件。但是这个文件有700多K占相当大的ROM, 像stm32F103RBT6这种小FLASH的MCU根本不行 ,加入当前工程文件中代码将增加160KB 左右。

配置好Stm32的串口和SPI等IO口设置后,就可以使用Fatfs做一些文件操作了。

4. Fatfs 文件操作

文件分配表FAT(File AllocationTable)用来记录文件所在位置的表格.它对于硬盘的使用是非常重要的,假若丢失文件分配表,那么硬盘上的数据就会因无法定位而不能使用了。

Fatfs 文件系统减轻了操作SD卡的工作量,调用其提供的函数就可以方便的操作文件,读写删改等。
这里提供一个main.c 示例:
#include "common.h"#include <string.h>FRESULT scan_files (char* path);#define  F_PUTS                 1                //测试向文件写入字符串#define  F_READ         1       //测试从文件中读出数据#define  F_UNLINK       0       //测试删除文件#define  SCAN_FILES         1       //测试目录扫描FATFS fs;FRESULT res;FIL file;                                                   UINT br;BYTE buffer[4096];                                //以上变量作为全局变量 可以避免一些Bugint main(void){        u16 i,n;                        //stm32 初始化                        RCC_Configuration();        NVIC_Configuration();                        USART_Configuration();        SPI_Configuration();        GPIO_Configuration();        //fatfs 操作        f_mount(0, &fs);        //如果data.txt存在,则打开;否则,创建一个新文件        res = f_open(&file, "0:/data.txt",FA_OPEN_ALWAYS|FA_READ|FA_WRITE );         if(res!=FR_OK)        {                printf("\r\n f_open() fail .. \r\n");        }else{                printf("\r\n f_open() success .. \r\n");        }#if F_READ        while(1){                                                                         //使用f_read读文件                res = f_read(&file, buffer, 1, &br);     //一次读一个字节知道读完全部文件信息                if (res == FR_OK )                {                        printf("%s",buffer);                }else{                    printf("\r\n f_read() fail .. \r\n");                        }                if(f_eof(&file)) {break;}        }        /*if( f_gets(buffer,sizeof(buffer),&file) != NULL)         //使用f_gets读文件        ,存在 Bugs 待调试        {                printf("%s",buffer);        }else{            printf("\r\n f_gets() fail .. \r\n");                } */ #endif#if F_PUTS        //将指针指向文件末        //res = f_lseek(&file,(&file)->fsize);         res = f_lseek(&file,file.fsize);                n = f_puts("\r\n hello dog ..\r\n", &file) ;  //向文件末写入字符串                                              if(n<1)  //判断写是否成功                                {                                                                      printf("\r\n f_puts() fail .. \r\n");                                                     }else{                printf("\r\n f_puts() success .. \r\n");        } #endif#if F_UNLINK        res = f_unlink("test.jpg");           //前提SD下存在一个test.jpg        if(res!=FR_OK)        {                printf("\r\n f_unlink() fail .. \r\n");        }else{                printf("\r\n f_unlink() success .. \r\n");        }#endif#if SCAN_FILES        printf("\r\n the directory files : \r\n");        scan_files("/");              //扫描根目录#endif           f_close(&file);        f_mount(0, NULL);           while(1);}FRESULT scan_files (    char* path        /* Start node to be scanned (also used as work area) */){    FRESULT res;    FILINFO fno;    DIR dir;    int i;    char *fn;   /* This function is assuming non-Unicode cfg. */#if _USE_LFN    static char lfn[_MAX_LFN + 1];    fno.lfname = lfn;    fno.lfsize = sizeof lfn;#endif    res = f_opendir(&dir, path);                       /* Open the directory */    if (res == FR_OK) {        i = strlen(path);        for (;;) {            res = f_readdir(&dir, &fno);                   /* Read a directory item */            if (res != FR_OK || fno.fname[0] == 0) break;  /* Break on error or end of dir */            if (fno.fname[0] == '.') continue;             /* Ignore dot entry */#if _USE_LFN            fn = *fno.lfname ? fno.lfname : fno.fname;#else            fn = fno.fname;#endif            if (fno.fattrib & AM_DIR) {                    /* It is a directory */                sprintf(&path, "/%s", fn);                res = scan_files(path);                if (res != FR_OK) break;                path = 0;            } else {                                       /* It is a file. */                printf("\r\n %s/%s \r\n", path, fn);            }        }    }    return res;}

其中 目录扫描函数 scan_files( char * path) 参数格式如下:



这里使用到了f_puts()函数,所以必须在ffconf.h 中修改 #define _USE_STRFUNC  1

使用特权

评论回复
地板
mmuuss586| | 2016-8-29 10:21 | 只看该作者
谢谢分享

使用特权

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

本版积分规则

55

主题

97

帖子

0

粉丝