本文写的十分草率,许多我自创的函数未做解释。等整个项目完成,我会慢慢整理资料。。。
另外,本文所提供代码已经过改正,经过验证,请放心使用。
建议波特率不要太高,传数据时加上校验,实测1152000的波特率会造成部分乱码
一、大概实现
大致思路:在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中写入的数据的内存布局。注意:数据的存储要分大端小端大端与小端_大小端-优快云博客
首先要把数据打包成二进制流,放在最终写入flash的数据流中。装二进制数据用python的bytearray再合适不过了。打包的话用struct.pack
三、把编译后的数据烧录到w25q64中
1、上位机和stm32代码
# !/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
if __name__ == '__main__':
from conf import DB, pack
else:
from .conf import DB, pack
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=50.5)
ser.set_buffer_size(81920, 81920)
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)
break
with open(DB, 'rb') as fp:
total = 0
length = BSIZE
while length > 0:
data = fp.read(BSIZE)
length = len(data)
if length == 0:
break
if length < BSIZE:
data += b"\0"*(BSIZE-length)
length = len(data)
# 串口发送字符串数据
ser.write(data)
print(f"写入字节{length}")
while True:
ser_input = ser.read_until()
if b"Program" in ser_input:
print(f"收到回复:{ser_input}")
if b"Erase" in ser_input:
print(f"收到回复:{ser_input}")
if b"busy" in ser_input:
print(f"收到回复:{ser_input}")
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"
#include "Delay.h"
#define DOWNLOAD_B_SIZE 256
#define DOWNLOAD_E_T 16
#define DOWNLOAD_CAPACITY 0x800000
uint8_t Download_Buff[DOWNLOAD_B_SIZE];
uint32_t Dowmload_Counter = 0;
uint8_t Download_Cbid;
uint8_t Download_Etimer = DOWNLOAD_E_T;
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)
{
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");
}
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;
Usart1_Init(1152000);
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, "end init!\n");
}
2.注意每次擦除和写入的限制
注意: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)
3.上位机的坑
但还是会卡
也有可能是进中断卡死了,上个锁验证一下
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.youkuaiyun.com/linZinan_/article/details/114804811
4.pyserial注意事项:
最好使用read_until,然后把超时时间改大,否则超时了,但还没读到‘\n",程序就会卡在while循环中
四、测试
图片&文字
#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();
}
}