FLASH应用程序用户数据

本文写的十分草率,许多我自创的函数未做解释。等整个项目完成,我会慢慢整理资料。。。

一、大概实现

大致思路:在flash8M内存的高位划分出4k大小的若干块,每个应用程序占有最多4K的数据,在应用启动时读取数据,在应用结束时擦除这4K的flash并重新写入数据。

例如:

注:Fls_Get函数是从flash中读取数据

函数原型:Fls_Get(uint32_t addr, void* buff, uint32_t cnt)

Fls_Set是写入数据

#ifndef DATA_DINO_H
#define DATA_DINO_H



#include "TableAddress.h"
#include "Table.h"



//用户数据
uint8_t historyScore[100];


void dino_loadconf(void)
{
    //读取数据
    Fls_Get(DATA_DINO+0, historyScore, 100)
}


void dino_saveconf(void)
{
    //保存数据
    W25Q64_SectorErase(DATA_DINO)
    Fls_Set(DATA_DINO+0, historyScore, 100)
}


#endif

TableAddress是所有资源文件在flash中的地址索引(当然也包括这4K的用户数据),只有一个变量时当然好办,直接从首地址开读即可,但是有多个变量时还得考虑变量的存储顺序、字节偏移等问题。 于是这个代码可能变成了这样

#ifndef DATA_SYSTEM_H
#define DATA_SYSTEM_H



#include "TableAddress.h"
#include "Table.h"



//用户数据
uint8_t animaionFadeTime = 10;
uint8_t alertMarginTop = 10;
uint8_t alertMarginBottom = 10;
uint8_t alertMarginLeft = 10;
uint8_t alertMarginRight = 10;
uint16_t a = 10;


void system_loadconf(void)
{
    //读取数据
    Fls_Get(DATA_SYSTEM+0, &animaionFadeTime, 1)
    Fls_Get(DATA_SYSTEM+1, &alertMarginTop, 1)
    Fls_Get(DATA_SYSTEM+2, &alertMarginBottom, 1)
    Fls_Get(DATA_SYSTEM+3, &alertMarginLeft, 1)
    Fls_Get(DATA_SYSTEM+4, &alertMarginRight, 1)
    Fls_Get(DATA_SYSTEM+5, &a, 2)
}


void system_saveconf(void)
{
    //保存数据
    W25Q64_SectorErase(DATA_SYSTEM)
    Fls_Set(DATA_SYSTEM+0, &animaionFadeTime, 1)
    Fls_Set(DATA_SYSTEM+1, &alertMarginTop, 1)
    Fls_Set(DATA_SYSTEM+2, &alertMarginBottom, 1)
    Fls_Set(DATA_SYSTEM+3, &alertMarginLeft, 1)
    Fls_Set(DATA_SYSTEM+4, &alertMarginRight, 1)
    Fls_Set(DATA_SYSTEM+5, &a, 2)
}


#endif

于是我们需要手搓编译工具

 二、python工具

输入数据:我们需要定义的变量。直接给个json,例如:

type:变量类型

value:变量初始值

num:变量长度(如果变量不是数组,不管他)

{
    "animaionFadeTime":{
        "type":"uint8_t",
        "value":10
    },
    "alertMarginTop":{
        "type":"uint8_t",
        "value":10
    },
    "alertMarginBottom":{
        "type":"uint8_t",
        "value":10
    },
    "alertMarginLeft":{
        "type":"uint8_t",
        "value":10
    },
    "alertMarginRight":{
        "type":"uint8_t",
        "value":10
    },
    "a":{
        "type":"uint16_t",
        "value":10
    }
}

python把这个json里面的变量按顺序排布就得到了要往flash中写入的数据的内存布局。注意:数据的存储要分大端小端大端与小端_大小端-CSDN博客

首先要把数据打包成二进制流,放在最终写入flash的数据流中。装二进制数据用python的bytearray再合适不过了。打包的话用struct.pack

三、把编译后的数据烧录到w25q64中

# !/usr/bin/env python
# -*- coding: utf-8 -*-
"""
# ============================================================
# @Date    : 2022/05/16 21:50:12
# @Author  : miles
# @Email   : lishan@st.xatu.edu.cn
# @File    : serial_demo.py
# @IDE     : PyCharm
# @Func    : Describes the function of the file
# @Note    : pip install pyserial
# ============================================================
"""
import time
import serial.tools.list_ports
 
from conf import DB
BSIZE = 256

if __name__ == '__main__':
    # 读取串口列表
    ports_list = list(serial.tools.list_ports.comports())
    if len(ports_list) <= 0:
        print("无串口设备")
    else:
        print("可用的串口设备如下: ")
        print("%-10s %-30s %-10s" % ("num", "name", "number"))
        for i in range(len(ports_list)):
            comport = list(ports_list[i])
            comport_number, comport_name = comport[0], comport[1]
            print("%-10s %-30s %-10s" % (i, comport_name, comport_number))

        i = input("Serial Number\n")
        # 打开串口
        port_num = ports_list[int(i)][0]
        print("默认选择串口: %s" % port_num)
        # 串口号: port_num, 波特率: 115200, 数据位: 8, 停止位: 1, 超时时间: 55.5秒
        ser = serial.Serial(port=port_num, baudrate=115200, bytesize=serial.EIGHTBITS, stopbits=serial.STOPBITS_ONE,
                            timeout=55.5)
        if not ser.isOpen():
            print("打开串口失败")
        else:
            print("打开串口成功, 串口号: %s" % ser.name)
            

            print("等待擦除")
            while True:
                ser_input = ser.read_all()
                if b"!" in ser_input:
                    print(ser_input, end="")
                    break
            


            with open(DB, 'rb') as fp:
                total = 0
                length = BSIZE
                while length > 0:
                    data = fp.read(BSIZE)
                    length = len(data)

                    if length < BSIZE and length != 0:
                        data += b"\0"*(BSIZE-length)
                    length = len(data)
                    # 串口发送字符串数据
                    ser.write(data)
                    while True:
                        ser_input = ser.read_all()
                        if b"Recieve" in ser_input:
                            total += length
                            print(f"收到回复:{ser_input}, 累计写入字节数:{total}")
                            break
                    
                    
            # 关闭串口
            ser.close()
            if ser.isOpen():
                print("串口未关闭")
            else:
                print("串口已关闭")
#include "usart.h"
#include "W25Q64.h"
#include "../GUI/GUI.h"

#define DOWNLOAD_B_SIZE 256
#define DOWNLOAD_CAPACITY 0x800000

uint8_t Download_Buff[DOWNLOAD_B_SIZE];
uint32_t Dowmload_Counter = 0;
uint8_t Download_Cbid;

uint32_t Download_Address = 0;

void Download_Printf(uint32_t addr)
{
    W25Q64_ReadData(addr, Download_Buff, DOWNLOAD_B_SIZE);
    UsartPrintf(USART1, Download_Buff);
}

void Download_Write(void)
{
    W25Q64_PageProgram(Download_Address, Download_Buff, DOWNLOAD_B_SIZE);
    UsartPrintf(USART1, "Recieved!\n");    
    Download_Address += DOWNLOAD_B_SIZE;  
}



void Download_Recieve(char a)
{
    Download_Buff[Dowmload_Counter] = a;
    Dowmload_Counter++;
    if (Dowmload_Counter ==  DOWNLOAD_B_SIZE)
    {
        Download_Write();
        Dowmload_Counter = 0;
    }
}

void Download_Init(void)
{
    uint16_t did;
    uint8_t mid;
    uint32_t addr = 0;
    Usart1_Init(115200);
    W25Q64_Init();
    Download_Cbid = Usart1_AddCallback(Download_Recieve);
    Usart1_SetCallback(Download_Cbid);
    W25Q64_ReadID(&mid, &did);
    UsartPrintf(USART1, "Download Inited, MID: %d DID: %d\n", mid, did);
    UsartPrintf(USART1, "Erase full flash\n");
    while (addr < DOWNLOAD_CAPACITY)
    {
        W25Q64_SectorErase(addr);
        addr += 1024*4;
    }
    UsartPrintf(USART1, "Erase full flash end!\n");
}

注意:w25Q64一次擦除4K,一次最多写入256byte,我看错这个256byte的限制,试了三天没结果!!!

改过来以后发现一个灵异事件:每次写入一段时间后会卡住!!!,一开始以为是flash卡在了waitbuzy,开始各种的换芯片,直到做了一个测试后发现是卡在了usart发送!

写入字节256
收到回复:b'Start Program!\n'
收到回复:b'wait busy\nRecieved!\n'
收到回复:b'wait busy\nRecieved!\n', 累计写入字节数:18432
写入字节256
收到回复:b'Start Program!\n'
收到回复:b'wait busy\nRecieved!\n'
收到回复:b'wait busy\nRecieved!\n', 累计写入字节数:18688
写入字节256
收到回复:b'Start Program!\n'
收到回复:b'wait busy\nRecieved!\n'
收到回复:b'wait busy\nRecieved!\n', 累计写入字节数:18944
写入字节256
收到回复:b'Start Program!\n'
收到回复:b'wait busy\nRecieved!\n'
收到回复:b'wait busy\nRecieved!\n', 累计写入字节数:19200
写入字节256
收到回复:b'Start Program!\n'
收到回复:b'wait busy\nRecieved!\n'
收到回复:b'wait busy\nRecieved!\n', 累计写入字节数:19456
写入字节256
收到回复:b'Start Program!\n'
收到回复:b'wait busy\nRec'

我们发现接收到waitbusy之后还收到了Rec,对应到程序上是:

    UsartPrintf(USART1, "Start Program!\n");    

    W25Q64_PageProgram(Download_Address, Download_Buff, DOWNLOAD_B_SIZE);

    Download_Address += DOWNLOAD_B_SIZE;

    UsartPrintf(USART1, "Recieved!\n");    

改大缓冲区

        ser.set_buffer_size(81920, 81920)

还是会卡

也有可能是进中断卡死了,上个锁验证一下

bool is_Write = false;

void Download_Write(void)

{

    if (is_Write)

    {

        UsartPrintf(USART1, "recursively call\n");

        return;

    }

    is_Write = true;

    if (Download_Etimer >= DOWNLOAD_E_T)

    {

        UsartPrintf(USART1, "Start Erase!\n");    

        W25Q64_SectorErase(Download_Address);

        Download_Etimer = 0;

        UsartPrintf(USART1, "end Erase!\n");    

    }

    Download_Etimer++;

    UsartPrintf(USART1, "Start Program!\n");    

    W25Q64_PageProgram(Download_Address, Download_Buff, DOWNLOAD_B_SIZE);

    Download_Address += DOWNLOAD_B_SIZE;

    UsartPrintf(USART1, "Recieved!\n");    

   

    is_Write = false;

}

这就说明可能是芯片卡死了,换芯片尝试

写入字节256
收到回复:b'Start Erase!\n'
收到回复:b'end Erase!\nStart Program!\n'
收到回复:b'end Erase!\nStart Program!\n'
收到回复:b'wait busy\nRecieved!\n'
收到回复:b'wait busy\nRecieved!\n', 累计写入字节数:20736
写入字节256
收到回复:b'Start Program!\n'
收到回复:b'wait busy\nRecieved!\n'
收到回复:b'wait busy\nRecieved!\n', 累计写入字节数:20992
写入字节256
收到回复:b'Start Program!\n'
收到回复:b'wait busy\nRecieved!\n'
收到回复:b'wait busy\nRecieved!\n', 累计写入字节数:21248
写入字节256
收到回复:b'Start Program!\n'
收到回复:b'wait busy\nRecieved!\n'
收到回复:b'wait busy\nRecieved!\n', 累计写入字节数:21504
写入字节256
收到回复:b'Start Program!\n'
收到回复:b'wait busy\nRecieved!\n'
收到回复:b'wait busy\nRecieved!\n', 累计写入字节数:21760
写入字节256
收到回复:b'Start Program!\n'
收到回复:b'wait busy\nRecieved!\n'
收到回复:b'wait busy\nRecieved!\n', 累计写入字节数:22016
写入字节256
收到回复:b'Start Program!\n'
收到回复:b'wait busy\nRe'

 依旧卡死

void USART1_IRQHandler(void)
{

	if (USART_GetFlagStatus(USART1, USART_FLAG_PE) != RESET)
	{
	    USART_ReceiveData(USART1);
	  USART_ClearFlag(USART1, USART_FLAG_PE);
	}
	
	if (USART_GetFlagStatus(USART1, USART_FLAG_ORE) != RESET)
	{
	    USART_ReceiveData(USART1);
	  USART_ClearFlag(USART1, USART_FLAG_ORE);
	}
	
	if (USART_GetFlagStatus(USART1, USART_FLAG_FE) != RESET)
	{
	    USART_ReceiveData(USART1);
	   USART_ClearFlag(USART1, USART_FLAG_FE);
	}
	
	if (USART_GetFlagStatus(USART1, USART_FLAG_NE) != RESET)
	{
	    USART_ReceiveData(USART1);
	   USART_ClearFlag(USART1, USART_FLAG_NE);
	}

	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
	{
		if (Usart1_RecieveCallback[Usart1_CurrentCallback] != NULL)
		{
			Usart1_RecieveCallback[Usart1_CurrentCallback](USART_ReceiveData(USART1));
		}
		USART_ClearITPendingBit(USART1, USART_FLAG_RXNE);
	}

}

依旧卡死,降低为128byte,依旧卡死,将为164byte的缓冲区依旧卡死,最后发现是pyserial的问题

我把read_all换成read_until('\n')成功解决!!!

为什么呢?原来是timeout设置大了

最终找到这位大佬的文章:

2. 介绍
本文需要用到的几种方法和属性,大概看看就好,主要看下面的教程如何使用。

方法    功能
serial.read(size)    读取size字节的数据
serial.readline()    读取一行的数据
serial.readlines()    读取多行的数据,将数据保存到数组里
serial.read_all()    读取一个timeout周期内的全部数据(常用方法)
serial.read_all    读取串口所有的参数信息
serial_timeout(参数)    超时属性,下面具体介绍
3. 快速上手
下面的场景我们需要用用COM4串口发送一串字符串"hello world",波特率9600,如何使用python的serial库进行解析串口发送来的数据?

直接上代码

import serial

ser = serial.Serial("COM2", 9600, timeout=0.01)

while True:
    data = ser.read_all()
    if data:
        rec_str = data.decode('utf-8')
        print(rec_str)
这里我创建了两个虚拟的串口进行模拟,COM2向COM1发送了两次hello world,而Python端实现了COM1,监听来自COM2的消息,用read_all()方法读取接收到的数据,接收到的数据类型是bytes类型的,因此我们需要将bytes数组转成字符串print出来,如下图所示。


而初始化的时候这里的timeout是指在设定的timeout时间范围内,如果读取的字节数据是有效的(就是非空)那就直接返回,否则一直会等到这个设定的timeout时间并返回这段时间所读的全部字节数据。

简单来说你可以理解为每timeout秒读一次数据,而如果timeout太小,数据量太大,可能一次发送不完,会数据未读完的状态。

也就是说,如果我的timeout设置为0.01,现在我有一个串口通信的传感器,传感器通过串口使用COM2发送一次数据,传感器如果在0.01秒内将所有数据都传输过来,就保存到缓存buffer中,到了0.01秒后会调用一次read_all()就可以读取到传感器发送到buffer中的数据了;如果传感器在0.01秒内没有将数据发送全的话,buffer中的数据就是不完全的,就会返回不完全的数据;如果传感器在0.01秒内发送了数据的话,buffer也会返回两组合在一起的数据。

注意:timeout的设置至关重要,如果不是特别高频的数据的话,timeout=0.01的100Hz完全够了,当然timeout最小支持多少要看你的波特率。如果我0.3秒发送了一次数据,timeout设置0.01,就可以返回一次完整的数据;如果我0.01秒发送一次数据,timeout设置0.3,那么一次就会返回30次数据,所以这就是timeout的作用。

前面引入了buffer的概念,这里你应该可以知道,你可以直接把buffer理解为一个文件夹,读数据的方式是和file是一样的,只是引入的timeout的概念,使得buffer 内的数据会以 1/timeout 的频率进行刷新。
如果你把buffer当做file理解,那么readline()就是读取buffer中的一行,如果你的数据中存在\n这样的换行,那么用readline()先让不合适,很容易产生漏读的问题,而且如果你在一个timeout周期内没有用readline()处理完所有你想要的数据的话,buffer就刷新了,因此通过笔者的实践和项目经历读数据只推荐用read_all()来读取。
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/linZinan_/article/details/114804811

四、测试

图片&文字

#include "stm32f10x.h"
#include "usart.h"
#include "AD.h"
#include "Delay.h"
#include "Draw.h"
#include "Time.h"
#include "Key.h"
#include "../GUI/GUI.h"
#include "../GUI/Widgets.h"
#include "Download.h"
#include "dino.h"
#include "yuan.h"
#include "../UI/video.h"
#include "Home.h"

void Init_Apps(void)
{
    Home_Register();
    Video_Register();
    Dino_Register();
    Yuan_Register();
}


int main(void)
{
    Usart1_Init(115200);
    Key_Init();
    GUI_Init();
    Time_Init();
    W25Q64_Init();

    GUI_Image img;
    Fls_GetImage(&img, PICTURES_APPICOS_ABOUT);
    GUI_DrawPicture(img.picture, 0, 0, img.size.w, img.size.h, BLIT_NORMAL);
    Fls_FreeImage(&img);
    GUI_DrawText("Hello World!", 0, 0, FONTS_FONT_BAHNSCHRIFT_12, BLIT_NORMAL);
    GUI_DrawText("Hello world", 0, 20, FONTS_FONT_SQUARE_24, BLIT_NORMAL);
    GUI_Update();
    Init_Apps();

    while (1)
    {
        Widget_Update();
    }
}

 预读取器

#include "stm32f10x.h"
#include "usart.h"
#include "AD.h"
#include "Delay.h"
#include "Draw.h"
#include "Time.h"
#include "Key.h"
#include "../GUI/GUI.h"
#include "../GUI/Widgets.h"
#include "Download.h"
#include "dino.h"
#include "yuan.h"
#include "../UI/video.h"
#include "../Storage/pictures_appicos.h"
#include "../Storage/pictures_maze.h"


int main(void)
{
    Usart1_Init(115200);
    Key_Init();
    GUI_Init();
    Time_Init();
    W25Q64_Init();

    GUI_Rect rect = {
        .size.h = 0, 
        .size.w = 0, 
        .topleft.x = 0,
        .topleft.y = 0
    };
    pictures_appicos_load();
    pictures_maze_load();
    rect.size = pictures_appicos_osc.size;
    GUI_DrawImage(&pictures_appicos_osc, rect);
    pictures_appicos_clear();
    pictures_maze_clear();
    GUI_Update();

    while (1)
    {
        Widget_Update();
    }
}


 

  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值