我们平时在使用 S T M 32 STM32 STM32系列芯片的时候,在编译好程序之后,可能就是一个简单的鼠标操作,单机一下 K e i l M D K I D E Keil\quad MDK\quad IDE KeilMDKIDE上的 D o w n l o a d Download Download就可以了,但是实际上在下载程序到 F l a s h Flash Flash上的时候还需要一个叫做 F l a s h p r o g r a m m i n g a l g o r i t h m Flash\quad programming\quad algorithm Flashprogrammingalgorithm的东西,如图1所示。因为我们知道一般芯片内部的 F l a s h Flash Flash如果要进行烧录的话,需要先擦除,那么这个 F l a s h p r o g r a m m i n g a l g o r i t h m Flash\quad programming\quad algorithm Flashprogrammingalgorithm就相当于给 K e i l M D K I D E Keil\quad MDK\quad IDE KeilMDKIDE提供了一系列操作内部的 F l a s h Flash Flash的接口。
一般情况下,芯片供应商已经在对应的
P
A
C
K
PACK
PACK包里面为你准备好了对应的
F
l
a
s
h
p
r
o
g
r
a
m
m
i
n
g
a
l
g
o
r
i
t
h
m
Flash\quad programming\quad algorithm
Flashprogrammingalgorithm。那我们现在的问题是如何自己去构建
F
l
a
s
h
p
r
o
g
r
a
m
m
i
n
g
a
l
g
o
r
i
t
h
m
Flash\quad programming\quad algorithm
Flashprogrammingalgorithm文件。关于这一点大家可以先看一看
A
R
M
ARM
ARM公司关于这一块的官方介绍。下面我将简单的介绍一下
C
O
R
T
E
X
−
M
CORTEX-M
CORTEX−M系列芯片的
F
l
a
s
h
p
r
o
g
r
a
m
m
i
n
g
a
l
g
o
r
i
t
h
m
Flash\quad programming\quad algorithm
Flashprogrammingalgorithm文件的构建过程,注意这里是
C
O
R
T
E
X
−
M
CORTEX-M
CORTEX−M系列芯片,
A
R
M
ARM
ARM公司其他系列芯片的
F
l
a
s
h
p
r
o
g
r
a
m
m
i
n
g
a
l
g
o
r
i
t
h
m
Flash\quad programming\quad algorithm
Flashprogrammingalgorithm文件的构建过程可能还不同。下面的讲解主要基于
C
M
S
I
S
CMSIS
CMSIS官方介绍。
首先找到你的
K
e
i
l
M
D
K
I
D
E
Keil\quad MDK\quad IDE
KeilMDKIDE的
P
A
C
K
PACK
PACK包的安装目录,然后将到图1(这里是我的
K
e
i
l
M
D
K
I
D
E
Keil\quad MDK\quad IDE
KeilMDKIDE的
P
A
C
K
PACK
PACK包的安装目录,你需要确定你自己的)所示的
F
l
a
s
h
p
r
o
g
r
a
m
m
i
n
g
a
l
g
o
r
i
t
h
m
Flash\quad programming\quad algorithm
Flashprogrammingalgorithm模板工程复制出来。然后可以根据自己的喜好改一下图4和图5中的名字,当然这里不改也是可以的,但是图3的地方还是需要改一下,因为我打算用
S
T
M
32
F
103
STM32F103
STM32F103作为测试的例子,
S
T
M
32
F
103
STM32F103
STM32F103是
M
3
M3
M3的内核,因此这里选择
M
3
M3
M3。最后一步也是最重要的一步是更改图6中的那两个文件,也就是一系列操作内部的
F
l
a
s
h
Flash
Flash的接口,接下来我们会重点讲到。有了这些之后编译这个工程就会生成你所需的
F
l
a
s
h
p
r
o
g
r
a
m
m
i
n
g
a
l
g
o
r
i
t
h
m
Flash\quad programming\quad algorithm
Flashprogrammingalgorithm。
文件 F l a s h D e v . c FlashDev.c FlashDev.c里面主要是一些参数定义,定义在一个结构体中。图7是这个参数结构体的定义,结构体的第1个元素是下载算法的版本,这里自己填写,第2个元素是芯片相关描述,这里自己填写,第3个元素是 F l a s h Flash Flash所在位置,是片上( O N C H I P ONCHIP ONCHIP),还是片外( E X T _ x _ B I T EXT\_x\_BIT EXT_x_BIT),这里自己填写。第4个元素和第5个元素分别是 F l a s h Flash Flash的起始地址和大小(因为我这里的测试芯片是 S T M 32 F 103 Z E T 6 STM32F103ZET6 STM32F103ZET6,因此 F l a s h Flash Flash是 512 K B = 0 x 00080000 512KB=0x00080000 512KB=0x00080000,虽然芯片在启动的时候,地址 0 x 00000000 0x00000000 0x00000000可以自动映射到地址 0 x 08000000 0x08000000 0x08000000,但是这里的起始地址只能写为 0 x 08000000 0x08000000 0x08000000),第6个元素是 F l a s h Flash Flash的一个页的大小, S T M 32 F 103 Z E T 6 STM32F103ZET6 STM32F103ZET6的 F l a s h Flash Flash的一个页的大小为 2 K B = 2048 B Y T E S 2KB=2048BYTES 2KB=2048BYTES,第7个元素保留,第8个元素说明 F l a s h Flash Flash擦除完之后的内容,一般擦除完之后都是全F。第9个元素和第10个元素分别是 F l a s h Flash Flash页擦除和片擦除超时时间。最后一个元素定义了整片 F l a s h Flash Flash的页的分布情况,它是一个结构体数组。这个结构体有两个元素,第一个是一个页的大小,另一个是页的起始地址,要注意的是这里不用把每一个页的起始地址和大小都列出来,比如 S T M 32 F 103 Z E T 6 STM32F103ZET6 STM32F103ZET6的 F l a s h Flash Flash一共有256个页,每一个页的大小都是 2 K B = 2048 B Y T E S 2KB=2048BYTES 2KB=2048BYTES,因此这里结构体数组的第0个元素写为 ( 2048 , 0 x 00000000 ) (2048,0x00000000) (2048,0x00000000)就可以了(注意这里的起始地址只能写为 0 x 00000000 0x00000000 0x00000000而不能写为 0 x 08000000 0x08000000 0x08000000,具体的细节的原因我占时不知道,如果你这里写为 0 x 08000000 0x08000000 0x08000000,在下载程序的时候会报错,程序无法下载成功),这就表明整片 F l a s h Flash Flash的大小都是 2 K B = 2048 B Y T E S 2KB=2048BYTES 2KB=2048BYTES。别忘了最后还要加一个数组元素 S E C T O R _ E N D SECTOR\_END SECTOR_END。那如果出现 F l a s h Flash Flash的所有页不是同样大小的情况怎么样,我们现在举一个简单的例子,假如 F l a s h Flash Flash开头的8页,每一页的大小都是 8 K B 8KB 8KB,接下来的两页,每一页的大小都是 64 K B 64KB 64KB,接下来的所有页,每一页的大小都是 8 K B 8KB 8KB,如图8所示,这个例子也是上面复制的模板工程里面原来的代码。改完之后的参数结构体如图9所示。
下面我们来详细介绍一下 F l a s h p r o g r a m m i n g a l g o r i t h m Flash\quad programming\quad algorithm Flashprogrammingalgorithm需要用到的一些接口函数以及相应的流程,这些函数都在文 F l a s h P r g . c FlashPrg.c FlashPrg.c中,如下所示,其中前面4个是必须的,后面3个可以根据需求可选,这里我在测试的时候只实现了必须要实现的接口函数。
接口函数 B l a n k C h e c k BlankCheck BlankCheck主要的功能是用来检查当前的 F l a s h Flash Flash区域是否是空的,注意对于 F l a s h Flash Flash区域来说空的含义就是这个区域的每一个字节的内容都是全 F F F, 0 x F F 0xFF 0xFF,起始如果对于一个区域是全 F F F的话,我们不去擦除就可以直接去烧录。当然这个函数也可以用来检查一块特定的 F l a s h Flash Flash区域的内容是否等于特定的值,比如每一个字节的内容是否等于 0 x A A 0xAA 0xAA。其中参数 a d r adr adr表示要检查的 F l a s h Flash Flash区域的起始地址,参数 s z sz sz表示要检查的 F l a s h Flash Flash区域的大小,参数 p a t pat pat表示要检查的 F l a s h Flash Flash区域的预期的特定值,比如 0 x F F 0xFF 0xFF。
int BlankCheck (unsigned long adr, unsigned long sz, unsigned char pat);
Parameters
adr Block start address
sz Block size in bytes
pat Pattern to compare
Returns
status information:0 when the block content is equal to the pattern pat.
status information:1 when the block content differs from the pattern pat.
接口函数 E r a s e C h i p EraseChip EraseChip主要的功能是用来擦除整片 F l a s h Flash Flash区域,如果我们在图10中配置下载算法的对话框中勾选了 E r a s e F u l l C h i p Erase\quad Full\quad Chip EraseFullChip的话,在下载程序的过程中, K e i l M D K I D E Keil\quad MDK\quad IDE KeilMDKIDE就会调用下载算法中的这个接口首先将整片 F l a s h Flash Flash区域擦除,这个函数实现起来是比较简单的,一般芯片设计公司提供的库函数里面就有响应功能的接口函数,参考一下就可以了,比如 S T ST ST公司的 S T M 32 F 103 STM32F103 STM32F103提供的擦除整片 F l a s h Flash Flash区域的接口函数如图11所示。如果我们没有实现这个接口函数的话, K e i l M D K I D E Keil\quad MDK\quad IDE KeilMDKIDE会不断调用 E r a s e S e c t o r EraseSector EraseSector接口函数,直到整片 F l a s h Flash Flash区域被擦除。
int EraseChip (void);
Returns
status information: 0 on success.
status information: 1 on failure.
/*
* Erase complete Flash Memory
* Return Value: 0 - OK, 1 - Failed
*/
int EraseChip (void)
{
FLASH_Status status = FLASH_COMPLETE;
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation(EraseTimeout);
if(status == FLASH_COMPLETE)
{
/* if the previous operation is completed, proceed to erase all pages */
FLASH->CR |= CR_MER_Set;
FLASH->CR |= CR_STRT_Set;
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation(EraseTimeout);
/* Disable the MER Bit */
FLASH->CR &= CR_MER_Reset;
}
/* Return the Erase Status */
if(status == FLASH_COMPLETE)
{
return (0);
}
else
{
return (1);
}
}
接口函数 E r a s e S e c t o r EraseSector EraseSector主要的功能是用来擦除 F l a s h Flash Flash区域的一页内容,如果我们在图10中配置下载算法的对话框中勾选了 E r a s e F u l l S e c t o r s Erase\quad Full\quad Sectors EraseFullSectors的话,在下载程序的过程中, K e i l M D K I D E Keil\quad MDK\quad IDE KeilMDKIDE就会调用下载算法中的这个接口。参数 a d r adr adr表示要擦除的 F l a s h Flash Flash区域的页的起始地址,这个函数实现起来是比较简单的,一般芯片设计公司提供的库函数里面就有响应功能的接口函数,参考一下就可以了,比如 S T ST ST公司的 S T M 32 F 103 STM32F103 STM32F103提供的擦除 F l a s h Flash Flash区域的一页接口函数如图12所示。
int EraseSector (unsigned long adr);
Parameters
adr Sector address
Returns
status information:0 on success.
status information:1 on failure.
接口函数 E r a s e S e c t o r EraseSector EraseSector我的具体实现如下所示,因为原本的工程里面没有下面函数中 F L A S H FLASH FLASH等结构体的定义,因此这些结构体的定义,你需要自己添加进去。
/*
* Erase Sector in Flash Memory
* Parameter: adr: Sector Address
* Return Value: 0 - OK, 1 - Failed
*/
int EraseSector (unsigned long adr)
{
FLASH_Status status = FLASH_COMPLETE;
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation(EraseTimeout);
if(status == FLASH_COMPLETE)
{
/* if the previous operation is completed, proceed to erase the page */
FLASH->CR|= CR_PER_Set;
FLASH->AR = adr;
FLASH->CR|= CR_STRT_Set;
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation(EraseTimeout);
/* Disable the PER Bit */
FLASH->CR &= CR_PER_Reset;
}
/* Return the Erase Status */
if(status == FLASH_COMPLETE)
{
return (0);
}
else
{
return (1);
}
}
接口函数 I n i t Init Init用来在操作 F l a s h Flash Flash区域前的一些初始化操作,像最基本的,在操作 F l a s h Flash Flash相关的寄存器前需要先解锁。参数 a d r adr adr表示 F l a s h Flash Flash设备的基地址,参数 f n c fnc fnc表示不同的操作,1表示擦除,2表示烧录,3表示验证,参数 c l k clk clk表示操作时的时钟频率,但是我看官方的模板函数里面这三个参数基本没有用到,这里我也没有细究了。这里我的具体实现也比较简单,也就是给 F l a s h Flash Flash操作解锁了。
int Init (unsigned long adr, unsigned long clk, unsigned long fnc);
Parameters
adr Device base address
clk Clock frequency (Hz)
fnc Function code
Returns
status information:0 on success.
status information:1 on failure.
/*
* Initialize Flash Programming Functions
* Parameter: adr: Device Base Address
* clk: Clock Frequency (Hz)
* fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify)
* Return Value: 0 - OK, 1 - Failed
*/
int Init (unsigned long adr, unsigned long clk, unsigned long fnc)
{
FLASH_Unlock();
return (0);
}
接口函数 P r o g r a m P a g e ProgramPage ProgramPage主要的功能是在下载程序的过程中将编译好的代码文件烧录到 F l a s h Flash Flash区域中,每调用一次该函数只能烧录一页的内容。参数 a d r adr adr表示要烧录的 F l a s h Flash Flash区域的页的起始地址,参数 s z sz sz表示要烧录的 F l a s h Flash Flash区域的页的大小,参数 b u f buf buf存储要烧录到 F l a s h Flash Flash区域的页中的代码的实际内容。这个函数实现起来是比较简单的,但是这里 S T ST ST公司的 S T M 32 F 103 STM32F103 STM32F103的库函数里面没有直接的烧录一页的接口函数,只有烧录一个字或半个字的接口函数, S T M 32 F 103 STM32F103 STM32F103的 F l a s h Flash Flash区域一次只能烧录半个字,我们可以可以调用如图13所示的烧录半个字的接口来实现烧录一页的接口函数。
int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf);
Parameters
adr Page start address
sz Page size
buf Data to be written
Returns
status information: 0 on success.
status information: 1 on failure.
/*
* Program Page in Flash Memory
* Parameter: adr: Page Start Address
* sz: Page Size
* buf: Page Data
* Return Value: 0 - OK, 1 - Failed
*/
int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf)
{
FLASH_Status status = FLASH_COMPLETE;
sz = (sz + 1) & ~1; // Adjust size for Half Words
while (sz)
{
status = FLASH_COMPLETE;
status=FLASH_ProgramHalfWord(adr, *((unsigned short *)buf));
/* Return the Program Status */
if(status != FLASH_COMPLETE)
{
return (1);
}
/* Go to next Half Word */
adr += 2;
buf += 2;
sz -= 2;
}
return (0);
}
接口函数 U n I n i t UnInit UnInit用来进行在操作(擦除,烧录,验证) F l a s h Flash Flash区域后的一些操作,像最基本的,对 F l a s h Flash Flash相关的寄存器重新上锁。参数 f n c fnc fnc表示不同的操作,1表示擦除,2表示烧录,3表示验证,但是我看官方的模板函数里面这个参数基本没有用到,这里我也没有细究了。这里我的具体实现也比较简单,也就是给 F l a s h Flash Flash重新上锁。
int UnInit (unsigned long fnc);
Parameters
fnc Function code
Returns
status information:0 on success.
status information:1 on failure.
/*
* De-Initialize Flash Programming Functions
* Parameter: fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify)
* Return Value: 0 - OK, 1 - Failed
*/
int UnInit (unsigned long fnc)
{
FLASH_Lock();
return (0);
}
接口函数 V e r i f y Verify Verify主要的功能是对比已经下载到 F l a s h Flash Flash区域中的代码和代码文件,也就是看下载到 F l a s h Flash Flash区域中的代码和编译生成的代码是否一致。参数 a d r adr adr表示要对比的 F l a s h Flash Flash区域的起始地址,参数 s z sz sz表示要对比的 F l a s h Flash Flash区域的大小,参数 b u f buf buf存储要对比的代码的实际内容。
unsigned long Verify (unsigned long adr, unsigned long sz, unsigned char *buf);
Parameters
adr Start address
sz Size in bytes
buf Data to be compared
Returns
status information:the sum of (adr+sz) - on success.
status information:any other number - on failure, and represents the failing address.
有了以上修改之后,编译这个模板工程就可以生成你所需的下载算法了,下载算法的文件名后缀为 F L M FLM FLM,可以将这个生成的下载算法替代以前的下载算法,测试看看能否下载成功。最后图14是 F l a s h p r o g r a m m i n g a l g o r i t h m Flash\quad programming\quad algorithm Flashprogrammingalgorithm的擦除流程,图15是 F l a s h p r o g r a m m i n g a l g o r i t h m Flash\quad programming\quad algorithm Flashprogrammingalgorithm的程序烧录流程,图16是 F l a s h p r o g r a m m i n g a l g o r i t h m Flash\quad programming\quad algorithm Flashprogrammingalgorithm的验证流程。我的测试工程在这里。