网上有一篇F系列的远程升级,移植过来的时候可以用,但是缺芯得换成G系列的,使用的方法也不一样,所以移植的时候难免有些坑,所以来记录一下
8 k: t9 I3 Z6 k% Y% N0 O8 u5 f! |8 N6 {5 A: v9 B
不同之处
! m5 v" a3 Q& n) |' a7 R- j" `1 }1.对于flash的操作- l& c, i* H0 Z0 v( A& `6 i
HAL库里,F系列的G系列的flash的操作不太一样,我了解的有以下几点:
5 G- \9 q2 k6 e①.F系列支持最小单字节写入,而G系列最小写入单位是64位或者双32位快速编程。7 g4 r3 g, c+ S: b' U$ B: Q: i
所以在写入的时候,如果是单字节类型,需要转换为64位类型的数组,还要注意的是stm是小端模式,在写入的时候需要把高低字节转换一下,还有经过我实验得到,用keil烧录程序的时候,也是按照64位来写入的,所以就不用担心在远程升级的时候,4字节写入还要自己再转换一下,下面是我写的flash写入函数& x1 Q/ B) t: Q$ b( c
参数:地址必须能被8整除;传入的是单字节的数组;长度是传入数组的长度# p: m- O+ ?$ @
) a7 f% {5 F" l, c- //写flash
- n6 x1 E$ ~# O+ E+ A/ i - //参数:addr:写入地址;buff:单字节数组;word_size:写入数组长度
) c, @4 e$ [- n5 C# S! n% M - uint64_t flash64_buf[30];
: \0 o7 u: ~( U v - void MyWrite_Flash(uint32_t addr, unsigned char * buff, int word_size)+ `3 t0 m+ d6 G) B' o6 y; {2 I; j
- { 2 g% w9 y: }! @/ N7 l! ?
- /* 1/4解锁FLASH*/
) r- \3 I0 |1 ]- t7 h - uint8_t len,i,j;
, `4 S9 k. j3 G! c4 W2 {$ o - HAL_FLASH_Unlock();. L- a' W! L7 ]! n: O
- * G/ A) ?7 Z: e7 r/ k( |
- if(word_size%8)1 L7 C. j8 @, f" g$ q8 ]
- {, R0 p# P4 j* P3 k* y% ]! i [
- len=word_size/8+1;3 j; G {% F1 ?
- }
8 P0 E2 p1 l! K - else9 w4 `) t' ^" b- W# T
- {4 _% Z n3 e, P# B/ R& ^- L
- len=word_size/8;9 S4 N- R7 x5 ?) }( f9 I' b& n
- } ]' Y' R1 ]0 v# g5 A
- . s H7 f0 ~1 t5 I9 }
- for(i=0;i<len;i++)//这个是要转多少组
. \/ b" [2 R7 K - {4 X) R# K+ F: W! x* X' H4 U6 i
- for(j=0;j<8;j++)//这个是转8个字节3 j# |* W( j2 _& |" n
- {, s- Y# D+ v. y* r1 X
- flash64_buf<i>|=(uint64_t)buff[j+i*8]<<(j*8);
/ [) {3 K5 k( O% [ - </i> } : I: w9 y$ C5 m* O$ ]
- }
0 H, {- @7 o- } o7 w t* D1 T - __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);//清除标志位
+ t0 e+ i$ i W% r- \0 g4 N' q* Q - for(int i = 0; i < len; i++) / _% k. g& m& w9 H. |
- {
) v0 A; Z, \9 x+ r% `1 M - /* 3/4对FLASH烧写*/
* A2 ^8 R D) o; u, q& Q# E - //g030是64位双字编程或以32个双字为单位的快速行编程 v/ d! e" n- u1 D' ?, b6 n2 X# {, q6 o
- //HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, (uint32_t)(uint64_t *)addr + 8 * i, *(uint64_t *)&flash_buf[i*2]); //地址要求8字节对齐,地址递增是8的倍数% p6 s9 ~; W% v
- HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, (uint32_t)(uint64_t *)addr + (8 * i), flash64_buf);( J: r+ K6 b, f$ y- g; L" b
- }
9 Z" A7 [; c; r2 _3 y; L - //flash64_buf清0# C9 } f0 x5 P5 x
- for(i=0;i<=29;i++). o$ R3 _6 }, C
- {6 W" V9 I! Y8 M* o, D- Q
- flash64_buf=0;+ u& u, F: P/ _* Q0 ^. x
- }# z4 n: i: K8 F6 n
- /* 4/4锁住FLASH*/8 e- g. |2 }/ N6 ~- k7 Z! ?# D
- HAL_FLASH_Lock();" B8 v+ I$ w: y: g+ F
- }
复制代码 1 j5 f& ^$ s! X, ~ @5 y, A# ]. T
②.不一样的是擦除的结构体,F系列是自定义位置擦除,而G系列擦除flash的结构体的三个成员是:擦除方法;从第几页擦除;擦除多少页
& p( ~/ @; e- PF系列每页1K,而G0是每页2K,我用的是G030,一共64k,所以是32页
& `- P$ |, i/ B- y下面是我的擦除函数1 r; S* a) v/ R5 G+ i
$ ^' X+ S( o3 \+ Z- int MyErase_page(uint32_t pageaddr, uint32_t num)
" B! B8 b- S* H! Z# s# a; Z - {3 O2 e( l$ [3 Z8 r( z% u. t& L8 @
- uint32_t PageError = 0; q' S6 Y- d8 ^0 I( e
- HAL_FLASH_Unlock();
: a& @8 v* J' ?. g, L) _ -
9 t3 o3 j1 Z8 ?; U8 e- T) i! T - /* 擦除FLASH*/5 D; \ v! ~' j! W) ?1 G$ m0 ~7 S
- FLASH_EraseInitTypeDef FlashSet;
7 K: D) C* [* y) G
3 v3 a2 g$ a2 S9 U2 h5 A" Q- FlashSet.TypeErase = FLASH_TYPEERASE_PAGES;//页编辑! }6 |) I }& o, q, j ~
- FlashSet.NbPages = num;//擦除页数
0 V, Z1 r0 i5 x' s9 O/ T - FlashSet.Page=pageaddr;//从哪一页开始
1 Y Z- E" ?% A+ y, c8 `; r* h/ E - __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);//清除标志位" H/ l) e4 ]. S4 t1 y" _6 L2 d- k1 [
- /*设置PageError,调用擦除函数*/
0 i- _; c! x, H4 E8 l5 J -
# ]# j3 n" U O( B+ G" h. K - HAL_FLASHEx_Erase(&FlashSet, &PageError);
v3 b& `4 g' Q9 F- P- {8 i -
3 a' }' c* e' @$ J* ]! x( \2 q' F - HAL_FLASH_Lock();
8 q3 T" e& F) J0 ~3 k( L - return 1;
8 P1 g( ~! C$ V1 W9 }- Y - }
复制代码
0 Z7 z) L( z( M [" i读的话没有什么限制,就是自己想读多少读多少,可以一次读单字节也可以一次读四字节,下面是我的读函数% K+ e! n" |, |
% D I* x+ R6 g c( n# b% g7 T- //读4字节
9 e, w$ D% r" i$ c - static void MyRead32Flash(uint32_t addr, uint32_t * buff, uint16_t word_size)4 r- c! T* D5 a
- {
" }' o+ h+ X' w/ H - for(int i =0; i < word_size; i++)
7 H% J8 c% ]( O6 M - {; D& k5 N: B+ S
- buff<span style="font-style: italic;"><span style="font-style: normal;"> = *(__IO uint32_t*)(addr + 8 * i);( ?6 D3 g9 k9 W% S) `
- }# I6 H2 G* A8 S0 U3 j/ B9 N
- return;7 Z7 r1 H7 c$ U: V- b) H" t6 N7 ?# r
- }</span></span>
复制代码 " i1 r% ~& I6 d. E1 i
2.升级方法不同
) ^) A. e; t5 s6 t% X$ X) @①原先参考的思路是,划分三个区域,一个boot loader,一个APP1,一个APP2,boot loader负责引导程序启动,APP1存放运行的代码,APP2存放升级的代码,这样就很浪费内存,改变为:2 i3 s7 L2 y1 [7 P, n7 ]) M
由boot loader来接收代码,一次接收240个字节,接到进行校验,正确的话直接写到flash,这样就能有很多空间用来挥霍了
+ A7 B; ^/ r& u/ Q9 c②升级的协议不一样。参考的代码是用ymodem协议来做的,这个方法比较稳重,但是为了配合4G模块厂家,还是直接用他们给的协议,解析包头包尾和累加校验。其实我觉得都差不多,目的都是为了接到正确的数据帧,就看校验的方法好不好
* q0 C( S \6 z, [- p) L& @③升级用到的串口通信方法不同。众所周知,串口通信的方式一般有三种,中断,超时,还有空闲中断,然后一般新手入门的时候,在用串口总会面对一个问题,就是接收不定长数据,解决这个的方法我知道的有两个,一个就是空闲中断,另一个方法就是用定时器自己写一个空闲中断,这样的话接收一帧数据的时间就能自己控制,这个在用于modbus非常的友好。参考的程序是用空闲中断,而我用的是中断加定时器, a- o9 ?& I+ Z8 k9 k
0 G1 e# p3 Y) A5 E. w% H遇到的坑* [# ?' i9 e: m
遇到的坑还是挺多的,俗话说的好:_________________。2 u W( D d6 @3 m( O9 z
0 U- ` F. Y& L4 p
1.不能打断点
. z9 s7 i4 [; [, Q0 v做远程升级的,肯定都知道bootloader,写boot loader的,肯定都知道跳转函数__asm void MSR_MSP,也不知道为什么,我加了这个之后,debug的时候,添加有这个函数的源文件里的函数,都不能打断点了,解决方法就是把这个汇编函数和跳转函数放到其他源文件
( D" a1 T8 \( A( E1 A
% E( I6 a- R& R) q2.进不到for循环$ S* W- J8 `& f3 a+ G! D' g' m
这个问题非常的奇怪,程序一步步的执行下去,什么都没有问题,就是进不去,后来在网上找了很久,发现了这个,优化等级,只要把这个改为0,就能执行进去了
# c) |0 N3 T' b m r* w
5 N* ~+ U$ G2 z" a+ b) x% S# K6 [9 W* z8 G& b( e8 [9 x
1 [3 n- S: j1 s$ t$ X) }
3.4G模块Air724UG接收不到正常的数据
8 X2 Q9 J& Y1 D! h$ M+ ~- g' B对于一个刚出来的新手,硬件知识还比较薄弱,对于一些模块就是拿来就用,不会有什么怀疑。在用4G接收第一帧升级文件的时候,总是接不到正常的数据,一直以为是代码的问题,然后用串口直连发现也收不到正确的数据,但是用模块自带的USB来供电的时候就正常,然后请软硬件两个大佬来救场,用示波器看在接收数据的时候电平不正常,判断是在接收的时候电平被拉低了,原因是模块上的两组串口同时共用一个CH340芯片,导致使用的时候电压不够,然后把模块上的CH340芯片挑下来,就能正常接收了。看模块的手册感觉在单片机和模块的串口之间接一个电阻可能也可以,但是还没试过。以后得多补补硬件的知识。, q3 a; V: Z7 V( L1 D
* E7 N$ G Z; w; T& d2 `# Q& T9 T& E( j/ a+ e
|