1 Star 0 Fork 1

thtfcccj/cBootDcl

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
cBootDcl_AppSd.c 16.72 KB
一键复制 编辑 原始数据 按行查看 历史
thtfcccj 提交于 2024-05-31 21:54 . cBootDcl_AppSd.c:
/***********************************************************************
cBootDcl主模块-在boot端(即针对app端编程)通过SD/TF/MMC卡编程实现
//本模块在开机检测到SD卡时:若检测到脚本文件,则执行相应编程
//此模块需FatFS系统支持,直接适配后的版本分支:
https://thtfcccj@gitee.com/thtfcccj/FatFs.git
***********************************************************************/
#ifdef SUPPORT_FLASH3_CLASS//支持外置TF卡时
#include "cBootDcl_AppSd.h"
#include "ff.h"
#include <string.h>
#include "StringEx.h"
#include "math_3.h"
#include "Delay.h"
#include "cBootDcl.h"
#include "cBootDcl_App_CB.h" //CBP_SectorCount[]
#include "Adler32.h"
extern void BootToApp(void); //进APP
#ifdef SUPPORT_LOG_CMD_LINE //支持行提示时
#include "LogCmdLine.h"
#define _LINE_COUNT LOG_CMD_LINE_COUNT
#else //实现为空
#define _LINE_COUNT 1
#define _UpdateLog(str) do{}while(0)
#define _UpdateLog2(line, str, para) do{}while(0)
#endif
//-----------------------------默认配置-----------------------------
//每次读取的cBootLd.txt大小,因FatFS不可重入(同时加载两个文件)
//故缓冲区大小应 > 此文件大小,否则最后将报错!!!
#ifndef CBOOT_LD_PER_READ_SIZE
#define CBOOT_LD_PER_READ_SIZE 512
#endif
//编程错误时,停留时间,0.3s为单位
#ifndef CBOOT_LD_PROGRAM_ERR_NOTICE_OV
#define CBOOT_LD_PROGRAM_ERR_NOTICE_OV (3 * 30)
#endif
//下述参数外部定义,若有定义(如批量烧写),则以最后一次更新为准
//#define CBOOT_LD_DEFAULT_UID 1345730392 //默认UID,不定义时为0
//#define CBOOT_LD_DEFAULT_CPASS 0xa5a5a5a5 //默认连接密钥,不定义时为0
/***********************************************************************
内部结构
***********************************************************************/
struct _AppSd{
//FatFS:
FATFS FS; //挂载的文件系统
FIL File; //打开脚本文件与程序文件用
//数据缓冲相关:
char Buf[CBOOT_LD_PER_READ_SIZE + 1]; //缓冲的一个扇区数据,预留结束字符
//缓冲以换行符"\n"的字符串,\n会被替换为\0
char LogBuf[_LINE_COUNT + 1]; //预留结束字符
//编程相关:
unsigned long UID; //从文件中读取的UID
unsigned long CPass; //从文件中读取的连接密钥
unsigned long FileAdler32; //整个文件的Adler32值
signed char ProgramCount; //开机为-1表示未找到识码头。
unsigned char IsEnd; //程序结束标志
unsigned short DelayMs; //延时时间
unsigned char PrgramBuf[256 + 16];//编程缓冲区
};
struct _AppSd AppSd;
//-----------------------------内部状态提示字符-----------------------------
static const char _unFindCard[] = "No found TF/SD/MMC card";
static const char _FindCard[] = "Found TF/SD/MMC card";
static const char _FindFile[] = "Found cBootLd.txt";
static const char _FindFileErr[] = "cBootLd.txt no found";
static const char _FileErr[] = "cBootLd error:";
static const char _unFindHeader[] = "No found script header";
static const char _unFindProgram[] = "No found Program script";
static const char _FinalProgram[] = "Final program count:";
//行内提示
static const char _FindHeader[] = "Found script header";
static const char _DelayMsUpdate[] = "delayMs Update";
static const char _UidUpdate[] = "UID Update";
static const char _cPassUpdate[] = "cPass Update";
static const char _unScript[] = "Unrecognized script";
static const char _ErrProgramName[]= "Unsupported program format";
static const char _ProgramUnfound[]= "File cannot be found";
static const char _StartProgram[] = "Start program: ";
static const char _EndProgram[] = "Completed program: ";
static const char _ErrPara[] = "Para err: ";
static const char _ErrCheckErr[] = "End Flash Check Err: ";
//static const char _UnPacketErr[] = "Unpacking error: ";
//----------------------------脚本头尾-----------------------------
static const char _HeaderIdent[] = "cBootFsV1.00";//识别码
static const char _HeaderUID[] = "UID";
static const char _HeaderCPass[] = "cPass";
static const char _HeaderDelayMs[] = "delayMs";
static const char _HeaderLoad[] = "load";
static const char _HeaderEnd[] = "end";
static const char _ExtensionBin[] = ".bin";
static const char _ExtensioncEt[] = ".cEt";
/***********************************************************************
相关函数实现
***********************************************************************/
//-----------------------------无参数提示--------------------------
#ifdef SUPPORT_LOG_CMD_LINE //支持行提示时
void _UpdateLog(const char *pStr)
{
LogCmdLine_UpdateLog(pStr);
Delay_Ms(AppSd.DelayMs); //停留1秒以让用户看清
}
#endif
//------------------------带行和尾部参数提示-----------------------
//示例: L1: cBootLd error: 255,或:
#ifdef SUPPORT_LOG_CMD_LINE //支持行提示时
void _UpdateLog2(unsigned char Line, //头部行提示,0时不提示行
const char *pStr,
signed char Para) //尾部参数,9时无尾部参数
{
char *pPos = AppSd.LogBuf;
if(Line){//带行提示时
*pPos++ = 'L';
pPos = Value2StringMin(Line, pPos, 3);
*pPos++ = ':';
*pPos++ = ' ';
}
unsigned char StrLen = strlen(pStr);
memcpy(pPos, pStr, StrLen);
pPos += StrLen;
if(Para){//带尾部参数时
if(Para < 0){//负值时
*pPos++ = '-';
Para = 0 - Para;
}
pPos = Value2StringMin(Para, pPos, 1);
}
*pPos = '\0';
LogCmdLine_UpdateLog(AppSd.LogBuf);
Delay_Ms(AppSd.DelayMs); //停留1秒以让用户看清
}
#endif
//------------------------------编程处理函数------------------------------
//字符串在AppSd.Buf[0]里,且已含结尾字符
//返回非0编程错误
signed char _ProgramPro(unsigned char LinePos,
unsigned long *pPara, //6个参数
char *pName) //程序名称
{
//===========================编程前准备=============================
//检查文件头是否为.bin或.cBt名称
char *pExtension = strchr(pName, '.');
if((pExtension == NULL) ||
memcmp(pExtension, _ExtensionBin, sizeof(_ExtensionBin) - 1) &&
memcmp(pExtension, _ExtensioncEt, sizeof(_ExtensioncEt) - 1)){
_UpdateLog2(LinePos, _ErrProgramName, 0); //不支持的文件格式
return -1;
}
//是否为加密文件标志
signed char IsEncrypted; //加密链接标志
if(*(pExtension + 1) == 'c') IsEncrypted = 1;
else IsEncrypted = 0; //非加密的bin文件
//检查参数是否正确
if(pPara[0]){//此为boot端程序
_UpdateLog2(LinePos, _ErrPara, 1);
return -1;
}
//查找程序文件
FRESULT Result = f_open(&AppSd.File, pName, FF_FS_READONLY);
if(Result){//没找到
_UpdateLog2(LinePos, _ProgramUnfound, 0); //文件未找到
f_close(&AppSd.File);
return -1;
}
//开始编程前提示编程文件
memcpy(AppSd.LogBuf, _StartProgram, sizeof(_StartProgram) - 1);
strcpy(&AppSd.LogBuf[sizeof(_StartProgram) - 1], pName);
_UpdateLog(AppSd.LogBuf);
//===========================模拟通讯机制编程=============================
//一. 先建立连接
if(IsEncrypted) AppSd.PrgramBuf[0] = 0x43; //握手功能码
else AppSd.PrgramBuf[0] = 0x34;
AppSd.PrgramBuf[1] = 0xff - AppSd.PrgramBuf[0];
AppSd.PrgramBuf[2] = pPara[1]; //程序ID
AppSd.PrgramBuf[3] = pPara[2] >> 8; //幻数高位
AppSd.PrgramBuf[4] = pPara[2] & 0xff; //幻数低位
MsbL2Full(AppSd.UID, &AppSd.PrgramBuf[5]);
cBootDcl_RcvPro(AppSd.PrgramBuf, 9);
signed char Resume = AppSd.PrgramBuf[2];//在此返回结果
if(Resume){ //参数异常了
_UpdateLog2(LinePos, _ErrPara, Resume);
f_close(&AppSd.File);
return -1;
}
//二. 编程, 同时计算整个文件的Adler32值
if(IsEncrypted) AppSd.PrgramBuf[0] = 0x5c; //编程功能码
else AppSd.PrgramBuf[0] = 0x5a;
AppSd.PrgramBuf[1] = 0xff - AppSd.PrgramBuf[0];
//AppSd.PrgramBuf[3]至AppSd.PrgramBuf[4]幻数未变
unsigned short FrameNo = 0;
UINT CurSize;
if(IsEncrypted){//加密文件时,读取头信息:
Result = f_read(&AppSd.File, &AppSd.PrgramBuf[8], 16, &CurSize);
if(Result || (CurSize < 16)){//读取或文件错误
_UpdateLog2(LinePos, pName, FrameNo);
f_close(&AppSd.File);
return -1;
}
//8字节cBootDcl+4字节本区域容量+4字节文件Adler32值=16字节
//检查文件头(略)
//检查本区容量(略)
//直接得到整文Adler32值以在最后校验
AppSd.FileAdler32 = MsbFull2L(&AppSd.PrgramBuf[8 + 12]);
}
else AppSd.FileAdler32 = C_BOOT_ADLER32_INIT; //bin文件计算
do{
//读取并填充程序区
Result = f_read(&AppSd.File,
IsEncrypted ? &AppSd.PrgramBuf[6] : &AppSd.PrgramBuf[8],
IsEncrypted ? 256 + (2 + 4): 256, &CurSize); //实测多4个
if(Result){//读取错误
_UpdateLog2(LinePos, pName, FrameNo);
f_close(&AppSd.File);
return -1;
}
if(CurSize == 0) break;//文件结束了
if(IsEncrypted){//加密文件每个包由"2Byte数据大小+数据+4Byte数据Adler32"组成
if(CurSize < 6) break;//文件结束了
CurSize -= 6;
//检查数据大小与数据量是否匹配(略)
}
//普通文件不足时,需填充0xff再行全部计算
else if(CurSize < 256){
memset(&AppSd.PrgramBuf[8 + CurSize], 0xff, 256 - CurSize);
if(CurSize & 0x03) CurSize += 4 - (CurSize & 0x03);//保证4字节对齐
}
//组织头尾并发出烧写程序
AppSd.PrgramBuf[2] = pPara[1]; //程序ID恢复
AppSd.PrgramBuf[5] = CurSize >> 2; //本次程序字长(4字节为单位)
AppSd.PrgramBuf[6] = FrameNo >> 8; //本次程序帧号高位
AppSd.PrgramBuf[7] = FrameNo & 0xff; //本次程序帧号低位
if(!IsEncrypted){//普通数据时需手工加上校验码(加密数据时含了)
AppSd.FileAdler32 = Adler32_Get(AppSd.FileAdler32, &AppSd.PrgramBuf[8], 256); //累计更新
unsigned long Adler32 = Adler32_Get(C_BOOT_ADLER32_INIT,
&AppSd.PrgramBuf[8], CurSize);
MsbL2Full(Adler32, &AppSd.PrgramBuf[8 + CurSize]);
}
cBootDcl_RcvPro(AppSd.PrgramBuf, (8 + 4) + CurSize);
Resume = AppSd.PrgramBuf[2];//在此返回结果
if(Resume){ //异常了
_UpdateLog2(LinePos, _ErrPara, Resume);
f_close(&AppSd.File);
return -1;
}
FrameNo++;//一组数据完成了
}while(CurSize);
//三.写入程序完备描述
AppSd.PrgramBuf[0] = 0xaa;
AppSd.PrgramBuf[1] = 0xff - 0xaa;
AppSd.PrgramBuf[2] = pPara[1]; //程序ID
//AppSd.PrgramBuf[3] = pPara[2] >> 8; //幻数高位未变
//AppSd.PrgramBuf[4] = pPara[2] & 0xff; //幻数低位未变
if(!IsEncrypted){//bin文件时需补齐0xff至整个区域
unsigned char Id = pPara[1] & 0x7F;
unsigned short FrameCount = CBP_SectorCount[Id] *
(CBP_FlashPageSize[Id] / 256);
memset(&AppSd.PrgramBuf[8], 0xff, 256);
for(; FrameNo < FrameCount; FrameNo++){
AppSd.FileAdler32 = Adler32_Get(AppSd.FileAdler32, &AppSd.PrgramBuf[8], 256);
}
}
MsbL2Full(AppSd.FileAdler32, &AppSd.PrgramBuf[5]); //全文校验值
memcpy(&AppSd.PrgramBuf[9], pName, strlen(pName)); //名称
cBootDcl_RcvPro(AppSd.PrgramBuf, 9 + 16);
//四. 提示编程完成(完备标志此程序未去除,故不需要在此写入)
if(AppSd.PrgramBuf[2]){//在此返回结果异常了
memcpy(AppSd.LogBuf, _ErrCheckErr, sizeof(_ErrCheckErr) - 1);
strcpy(&AppSd.LogBuf[sizeof(_ErrCheckErr) - 1], pName);
_UpdateLog(AppSd.LogBuf);
}
else{//编程正确了
memcpy(AppSd.LogBuf, _EndProgram, sizeof(_EndProgram) - 1);
strcpy(&AppSd.LogBuf[sizeof(_EndProgram) - 1], pName);
_UpdateLog(AppSd.LogBuf);
AppSd.ProgramCount++; //写入一个程序了
}
f_close(&AppSd.File);
return 0;
}
//-------------------------------一行字符处理函数------------------------------
//字符串在AppSd.Buf[0]里,且已含结尾字符
void _LineStrPro(unsigned char LinePos) //当前实际行位置
{
//必须先检查数据头
if(AppSd.ProgramCount < 0){
if(memcmp(AppSd.Buf, _HeaderIdent, sizeof(_HeaderIdent) - 1)) return;
//找到了
AppSd.ProgramCount = 0;
_UpdateLog2(LinePos, _FindHeader, 0);
}
//查找=后的参数,并保留数据头
char *pPara = strchr(AppSd.Buf, '=');
if(pPara == NULL) return; //没找到=符号
*pPara++ = '\0'; //=替换为数据头尾部
//检查并更新参数
if(!memcmp(AppSd.Buf, _HeaderUID, sizeof(_HeaderUID) - 1)){
AppSd.UID = String2U32(pPara);
_UpdateLog2(LinePos, _UidUpdate, 0);
return;
}
if(!memcmp(AppSd.Buf, _HeaderCPass, sizeof(_HeaderCPass) - 1)){
AppSd.CPass = String2U32(pPara);
_UpdateLog2(LinePos, _cPassUpdate, 0);
return;
}
if(!memcmp(AppSd.Buf, _HeaderDelayMs, sizeof(_HeaderDelayMs) - 1)){
AppSd.DelayMs = String2U32(pPara);
_UpdateLog2(LinePos, _DelayMsUpdate, 0);
return;
}
if(!memcmp(AppSd.Buf, _HeaderEnd, sizeof(_HeaderEnd) - 1)){
AppSd.IsEnd = String2U32(pPara);//程序结束标志
return;
}
//最后load处理, 格式为:
//装载符 = 配置 id, 幻数, 扇区起始, 完备描述位置, 本区容量, 完整文件名
//load = 0X01, 170, 0x1235, 0x00000000, 0x00000420, 0x4000, cBootApp.bin;
char *pEnd;
unsigned long Para[6];
unsigned char Pos = 0xff; //当前或错误位置
if(memcmp(AppSd.Buf, _HeaderLoad, sizeof(_HeaderLoad) - 1))
goto ErrLineEnd;
//读取各字符参数
for(Pos = 0; Pos < 6; Pos++){
pEnd = strchr(pPara, ',');//查找','间隔符
if(pEnd == NULL) goto ErrLineEnd;
*pEnd++ = '\0'; //=替换为数据头尾部并至下个参数
Para[Pos] = String2U32(pPara);
pPara = pEnd; //下个起始
}
//开始编程
if(_ProgramPro(LinePos, Para, pPara)){//过程有误,后续不再写入了
cBootDcl_Init();//退出后须重新初始化
AppSd.IsEnd = CBOOT_LD_PROGRAM_ERR_NOTICE_OV;//长时间停留提示
return ;
}
//正确了
cBootDcl_Init();//退出后须重新初始化
return;
ErrLineEnd: //错识结束
_UpdateLog2(LinePos, _unScript, Pos);
return;
}
//---------------------------初始化同时执行编程函数------------------------
//调用此模块前,需确保cBoot及相关依赖(如显示屏提示)初初始化完成
void cBootDcl_AppSd_InitAndPrgram(void)
{
//初始化参数
memset(&AppSd, 0, sizeof(struct _AppSd));
AppSd.ProgramCount = -1;
AppSd.DelayMs = 1000;//默认为1秒
#ifdef CBOOT_LD_DEFAULT_UID
AppSd.UID = CBOOT_LD_DEFAULT_UID;
#endif
#ifdef CBOOT_LD_DEFAULT_CPASS
AppSd.CPass = CBOOT_LD_DEFAULT_CPASS;
#endif
//初始化FatFS系统,
FRESULT Result;
Result = f_mount(&AppSd.FS, "0:"/**/, 1);//立即挂载
if(Result){//失败表示未找到SD卡
_UpdateLog(_unFindCard);
return;
}
_UpdateLog(_FindCard);//提示找到SD卡了
//查找“cBootLd.txt”文件
Result = f_open(&AppSd.File, "cBootLd.txt", FF_FS_READONLY);
if(Result){//没找到
_UpdateLog(_FindFileErr);
return;
}
_UpdateLog(_FindFile);
//读取文件到缓冲区中并处理
unsigned char Line = 0; //读取行
//一. 读取数据至缓冲区(因FatFS不可重入,故只读取一次)
UINT CurSize;
Result = f_read(&AppSd.File, &AppSd.Buf, CBOOT_LD_PER_READ_SIZE, &CurSize);
f_close(&AppSd.File);
if(Result){//读取错误
_UpdateLog2(Line, _FileErr, 0);
return;
}
//二.检查文件结束
if(CurSize == 0) goto InitAndPrgram;
AppSd.Buf[CurSize] = '\0';//强制增加结束字符(+1避免覆盖真正结尾)
//三. 找每一行,并以行为单位解析数据
do{
//1. 查找一行结尾
char *pEnd = strchr(AppSd.Buf, '\n');//查找换行符
char *pCurEnd;
//当前行读完了,但当前行不完整,需继续补充文件,本次多行处理完成
if(pEnd == NULL) break;
unsigned short CurLineSize = pEnd - AppSd.Buf + 1; //当前读取的行大小
if((pEnd - AppSd.Buf) < 5) goto _EndLine; //本行字符过短,下一行
//2. WINDOWS系统时,回车字符处理
if(*(pEnd - 1) == '\r') pEnd--; //回车换行结束时移至前一个
*pEnd = '\0'; //结尾处理
//3.去掉"//" 注释,只留下代码
pCurEnd = strchr(AppSd.Buf, '/');
if(pCurEnd != NULL) pEnd = pCurEnd;
if((pEnd - AppSd.Buf) < 5) goto _EndLine; //本行字符过短,下一行
*pEnd = '\0'; //结尾处理
//4. 删除字符串中间的空格
pEnd = StrClrCar(AppSd.Buf, ' ');
if((pEnd - AppSd.Buf) < 5) goto _EndLine; //本行字符过短,下一行
//5. 检查结尾字符是否为“;”
if(*(pEnd - 1) != ';') goto _EndLine; //非程序结束标志
pEnd--; //去“;”
*pEnd = '\0'; //结尾处理
//6. 最后一行处理
_LineStrPro(Line + 1); //实际行位置
if(AppSd.IsEnd) break; //程序主动结束了
//7. 结束行处理:后面的数据往前行移
_EndLine:
memcpy(AppSd.Buf,
&AppSd.Buf[CurLineSize],
CurSize - CurLineSize + 1);//多copy一个结束字符
CurSize -= CurLineSize;
//行计数
Line++;
if(Line > 254){//文件行超了
_UpdateLog2(Line, _FileErr, -1);
return;
}
}while(CurSize);
//四.程序结束处理
InitAndPrgram:
//1. 提示编程次数
if(AppSd.ProgramCount < 0) _UpdateLog(_unFindHeader); //未找到程序头
else if(AppSd.ProgramCount == 0) _UpdateLog(_unFindProgram); //未找到程序脚本
else _UpdateLog2(0, _FinalProgram, AppSd.ProgramCount); //提示编程次数
//2. 返回前处理:
if(AppSd.IsEnd <= 1) return; //直接返回
if(AppSd.IsEnd == 2){//结束后进APP
BootToApp();
return;
}
#ifdef SUPPORT_LOG_CMD_LINE //支持行提示时 >=3: 结束后清屏
for(unsigned char i = AppSd.IsEnd; i > 0; i--){//闪动处理
LogCmdLine_Task();
Delay_Ms(300);
}
LogCmdLine_Init();//开始清屏
for(unsigned short Line = LOG_CMD_LINE_NEXT_START;
Line < LOG_CMD_LINE_COUNT; Line++)
LogCmdLine_cbClrLine(Line);
#endif// SUPPORT_LOG_CMD_LINE;
}
#endif //#ifdef SUPPORT_FLASH3_CLASS//支持外置TF卡时
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C
1
https://gitee.com/thtfcccj/cBootDcl.git
git@gitee.com:thtfcccj/cBootDcl.git
thtfcccj
cBootDcl
cBootDcl
master

搜索帮助