
0.阅读文章之前
本文的目标如下:
- 展示如何专门使用此板;
- 显示仅依靠文档和逻辑即可编写闪烁的LED程序的方法;
- 以对微控制器不熟悉的人可以理解的语言展示材料。
就使用其他文件而言,该代码将变得非常简单-我们将不包括单个文件,除了构建空的但有效的固件所需的文件之外。 即 基于固件代码,虽然可以,但是没有任何用处。
我们将需要以下文档:
- 数据表STM32F030x4(我使用2017年1月的文档DocID024849 Rev 3);
- RM0360参考手册STM32F030x4 / x6 / x8 / xC(我使用2017年4月的文档DocID025023 Rev 4);
- 电路板。
您可以从
云中下载这些文档。
本文中的计时器将
不被考虑,并且
不会被包含在代码中。
未使用ST-LINK编程器。 为了与开发板一起使用,使用了USB-COM适配器(基于PL2303HX的RS232),该适配器可模拟COM端口。
一切都收集在通过Windows X主机上的VirtualBox版本5.2.22r126460运行的Windows XP Professional 2002 SP3虚拟机上。
1.安装USB-COM适配器的驱动程序
Windows没有帮助,请从官方网站
Prolific (在Google中请求“ prolific driver”的第一个链接)下载驱动程序USB到UART / Serial / Printer
PL2303 Windows Driver (您需要一个
Standard Driver )。 或者,您可以从我的
云下载。
安装驱动程序,重新启动并查看新的COM端口。
端口设置为标准配置。 您可以自行决定更改COM端口号。 以我的经验,我一生只看过一个程序,只能看到前4个COM端口,如果我没记错的话,那是Windows下的某种蓝牙终端。
2.在板上装满固件
2.0下载实用程序,可用于开发板
我们从
STM网站下载了
FLASHER-STM32实用程序(在说明中称为STM32 Flash loader演示程序(UM0462)),您必须注册该程序,但这并不可怕-最终,我们将zip压缩文件与安装程序一起删除; 下一步->下一步->下一步...,所有内容均已安装。 为了方便起见,我在工作文件夹中创建此应用程序的快捷方式。
默认情况下,实用程序
C的路径为
:Program Files \ STMicroelectronics \ Software \ Flash Loader Demo \ STMFlashLoader Demo.exe 。
2.1引导细微差别
板上有一个BOOT跳线。
- 当跳线闭合时 ,微控制器将从其存储器(即,由程序员编写的程序)中加载指令。
- 当跳线打开时 ,微控制器将在RX和TX线上接收信息,即 它将从COM端口(在我的情况下是从适配器)刷新。
2.2配置实用程序

运行此应用程序,它实际上是最简单的(包含最少的设置)。 在第一个窗口中,选择:
- 接口(我有COM-3);
- 计算机和微控制器通信的速度(IMHO,9600正常值);
- 数据位数(出于某种原因,此窗口对我不可用,但到目前为止这并不重要);
- 奇偶校验(我没有奇偶校验,即无);
- 回声(我有关闭);
- 等待时间(我有10秒)。
单击下一步,如果一切正常,那么我们将看到绿灯,并且“目标是可读的”。 如果看到红灯,则表明计算机无法连接。
总是有帮助的步骤顺序:
- 首先,您需要检查板上的BOOT跳线是否已关闭。
- 其次,在任何情况下,都应关闭微控制器的电源,最好关闭从适配器到电路板的TX和RX线(您不能断开接地)。
- 第三,在程序中,按返回到末尾,即 返回首页,甚至将其关闭然后重新启动(通常来说,它有时会冻结)。 重要的是,始终先从首页开始,然后再通过此程序与主板进行每次连接。
- 第四,从适配器到电路板的导线接好,然后尝试再次在程序中连接(请确保从首页开始!)。
如果其他所有方法均失败,则可以尝试关闭所有功能,重新启动计算机,然后尝试重新连接至主板。
因为 我在虚拟机上工作,我必须多次重新连接USB-COM适配器,以便虚拟机可以检测到它,并且主机没有时间安装损坏的驱动程序。
我在撰写本文时发现的另一种选择是按下板上的按钮,而不是不断拉动电线。 但是,无论如何都必须关闭并打开BOOT跳线。 该选项起作用的原因是该按钮位于外部
NRST复位的底部。
在下一个窗口中,选择目标设备Target。 顺便说一句,有时在这里您通常可以看到(也许是一个错误)左侧的设备,例如,代替STM32而不是STM8-在发生某种故障的地方,上面已经描述了处理程序。 因此,在此步骤中,您不必急于单击“下一步”,但是请始终注意在Target中选择了所需设备的事实。
如何确定我们拥有的设备? -我们看一下芯片并重写所有写在芯片上的东西。 我们打开芯片上的
数据表 ,“
订购信息 ”部分描述了哪个字母负责什么。 以我为例:


我在Target中选择我的芯片(16K),然后继续。 芯片提供4种动作选择:
- 擦除内存(整个或选择一个特定区域);
- 将固件写入设备;
- 从设备读取固件;
- 启用/禁用写或读保护。
2.3从板上读取固件
当我第一次连接开发板时,我决定保留原始固件,这是一种备份-我们现在就做。 有必要指出该固件的保存位置以及要保存的内存页面,还建议使用
hex ,
bin或
s19文件格式进行
选择 。
如果您只是将固件上载到板上或从板上读取固件,则这些文件格式之间没有区别。 以下是进度页,有时该进程会长时间冻结99%(不一定是99),但应该在几秒钟后成功完成-实际上,在此之后,开发板没有给出与加载的固件相对应的行为。 简而言之,您需要重新连接所有内容并重新填充固件,对此没有什么要求。
固件文件已保存,以后可以上传到板上。
但是,如果安装了读取保护,则无法读取固件。
2.4刷板
现在填写固件文件,其源代码如下。 展望未来,我会说我们将上传
bin和
hex文件,因为 开发环境将发布它们。
s19和
hex文件的其他设置相同。 与它们不同的是,在
bin文件中,您可以选择将从中记录固件的地址,默认情况下在实用程序中为800万(适用于我们)。
在录制之前,您可以通过选择以下三个选项之一来清除微控制器的闪存:
- 擦除必要的页面(清除存储器的必要部分);
- 无擦除(未经纯化);
- 全局擦除(完全清除)。
实际上,清除是将零写入内存的过程。
仍然有可选的字节,但是到目前为止您无法触摸它们。 单击“下一步”,等待该过程完成,然后完成。
如果要记录我的固件,可以在云中找到它,即
blink.bin文件。 使用此固件时,PA4脚上的内置LED闪烁应闪烁。
3.代码编写
3.0安装CooCox CoIDE开发环境
您可以先从
SoftPedia.com网站下载IDE,然后再从STM网站和IDE网站本身下载IDE,但是由于IDE不再提供支持,因此已成为不可能。 没有关键的不再支持IDE了,不,因为 对于编写代码,主要是编译器。 我下载了两个版本,但我使用的是1.7.8版本。
在
这里很好地描述了环境的首次启动,Next-> Next-> Next ...,没有什么复杂的。 我只补充说,最好先创建一个项目,然后再进行其他所有操作。
但是,如果丢失了“存储库”选项卡,则可以在菜单“
视图”->“存储库”中找到它。
您可以在
此处下载适用于环境的工具(编译器),也可以向Google询问“用于手臂的gnu工具”; 我下载了一个在结尾处带有sha1.exe的选项。
3.1源框架
因此,已经创建了项目,选择了芯片,现在我们将向项目添加最少的资源集,否则将无法使用它。
这就是项目刚创建时的外观,即 只有主main.c文件,仅此而已 选择CMSIS BOOT ,环境将自动
选择M0 Cmsis Core ,因为 依赖关系需要这个。
组装项目(“生成”图标或F7键)。 由于我不知道的原因,未收集十六进制文件(控制台中有警告); 我几次重新安装了IDE和编译器,重新创建了项目,但是由于某种原因在虚拟机上出现了这样的结果。 在另一台计算机(不是虚拟的,而是真实的)上,所有内容都是一对一的,并且输出是十六进制的。 幸运的是,这里有垃圾桶。
我建议您注意文件大小,可以在控制台的输出末尾看到它,也可以通过标准方式看到它(在这里,您可以看到十六进制为空)。 同时,此屏幕快照显示固件文件位于项目文件夹中,然后是Debug / bin / 尽管代码没有执行任何操作,但我将其上传到板上以确保可以上传(例如,实用程序不会拒绝它)。 我建议读者这样做。 如果不起作用,请再试一次,然后写评论。
3.2手指算法
首先,我们从人的角度勾勒出一种算法,微控制器将如何使LED闪烁。 为此,需要进行一些推理。
每种设备都由于存储的能量而工作,例如,某些发动机可以使用不同类型的燃料运行,但是为此,需要将发动机调整为我们将要供给的燃料类型。 同样,微控制器需要针对能源进行调整(调整)-这将是算法的
第一步 。
我们进一步推理。 台式计算机具有监视器,扬声器,键盘,鼠标...,您可以看到某些设备向我们提供了信息,而在其他设备的帮助下,我们向计算机提供了信息,但是它们都连接到所有设备通用的盒子(系统单元)上。 您可以猜测微控制器可以接收并提供信息,这意味着其支路可以接收信号或发出信号-这将是算法的
下一个步骤 。
接下来,微控制器必须打开LED,等待一会儿,关闭LED,等待一会儿,然后将其打开,然后再关闭...
结果,该算法将如下所示

该流程图的目的是清楚地显示算法的作用。 首先,该方案是为您自己编写的,因此每个人都可以随意(自己)编写/绘制该方案。 我认为,该方案应旨在尽可能简单,易读和直观,以具有较高的抽象水平。
按照此算法,我们将编写代码。
3.3处理文档
建议您使用打开文件
stm32f0xx.h (位于我们项目的
cmsis_boot文件夹中)和打开文档阅读本文的这一部分。
3.3.1选择时钟源
首先,您需要为微控制器供电。 微控制器从适配器接收5伏电压(用万用表测量),但是出现的问题是“微控制器以什么频率工作”,因为已知电子设备以不同的频率工作。 首先,打开
数据表 ,在目录中,您可以看到两个有意义的部分:
电源管理 ,
时钟和启动 。 首先是关于电压和低功耗模式。 第二部分隐藏了我们目前感兴趣的内容。 在第一句话中已经说过“内部RC 8 MHz振荡器被选为复位时的默认CPU时钟”,这意味着
默认情况下,复位MC后,内部8 MHz RC链被选
为主时钟源 。
接下来是一些难以理解的时钟树方案,我们稍后再考虑。
严格来说,您可以依靠短语“在重置MK之后默认情况下...”并斜着阅读本文的这一部分。
现在,您需要分散电路板的注意力,并寻找内部LED。 我知道电路中的二极管由
D1 ,
D2 ...表示,即
D ==二极管 ,在我的电路板上靠近电阻
R7的是二极管
D1 。
也许,在仔细检查电路板之后,您可以找到二极管挂在哪条腿上,但我将转向电路板。 不幸的是,电路板的元件在其位置上与电路中的元件并不完全匹配。 但是我很高兴在互联网上找到了这样的方案(否则我很长时间都找不到了)。

在该图中,我们看到二极管的阴极通过跳线
J2接地,而阳极通过电阻器连接到
PA4引脚。
PA4表示端口
A的第四个输出,这意味着要点亮和关闭LED,必须向
PA4的输出端提供电压。
接下来,您需要确定如何向该输出施加电压。 对我而言,这根本不是直观的,并且很长一段时间以来,我一直在仔细阅读文档,直到在数据表最开始的“
描述”部分遇到了
框图 。 在其中我看到了珍贵的轨道
PA [15:0] <=> GPIO端口A <=> AHB解码器<=>总线矩阵<=> Cortex-M0 ,即 端口
A是通用I / O端口,并连接到
AHB总线。
方块图(图片可点击)

我注意到在电子产品中,通常将微控制器的输出分成端口,通常该端口有16个输出。 该图显示端口
A ,
B和
C只有16个,但是端口
D和
F却较少(少于16个引脚,更多-否)。
让我们回到“
时钟树”方案,找到
AHB签名的输出。 我们将弄清楚该输出的工作频率。
HCLK信号到
AHB ,离开
HPRE分频器。 该分频器从开关
SW接收
SYSCLK信号。 通过
编程设置 SW输入中的哪个信号将用作
SYSCLK ,然后在代码中进行设置。 提供选择:
- HSI-来自内部高频发生器的信号,它是由一个8 MHz石英谐振器产生的,在使用该板之前我已将其焊接;
- PLLCLK-来自倍频器PLLMUL的信号;
- HSE-来自外部高频发生器的信号。
任何选项都适合我们的任务,我建议选择其中最简单,最实惠的
HSI 。
我们将进入
参考手册并打开第
7节“
复位和时钟控制(RCC)” ,特别是
7.2.6系统时钟选择 ,在这里我们再次遇到数据表中类似的措辞:“系统复位后,选择
HSI振荡器作为系统时钟”-即 我们甚至不需要做任何事情,MK将从
HSI开始。
为了确保MK确实可以从此源工作,我将在程序中明确编写此代码。 滚动到负责复位和时钟的
寄存器 (第
7.4节
RCC寄存器 )。 文档中描述的第一个寄存器是
时钟控制寄存器(RCC_CR) ; 下面是对位的描述,这些位负责什么。
我们对
HSION零位感兴趣,该位负责打开谐振器(
0-关闭,
1-开启)。
因此,有必要向
RCC_CR寄存器写入1。 (零位为1,或2
0 = 1)。
现在,我们在
stm32f0xx.h文件中找到
RCC的定义(
#define RCC )。
如您所见,这是位于
RCC_BASE的结构; 地址
0x40021000 ,如果您全部展开
define ,则可以在《
参考手册》中的
2.2.2节“
内存映射和寄存器边界地址”以及在数据表的“第
5章内存映射 (
AHB区域)”中看到相同的地址。
要在
RCC块
CR寄存器中写入一个启用
HSI的单元,需要一行代码
RCC->CR |= 0x1;
3.3.2设置腿
向微控制器的脚发送信号以点亮LED并停止信号,以使LED熄灭是简单的操作,因此适用于
GPIO功能(通用输入输出端口)。
默认情况下,MK支脚未连接,即 输出是不确定的。 必须连接端口,该端口的脚将为LED供电。 之前,我们确定
GPIO端口已连接到
AHB总线-您需要调整该总线。 继续翻阅第
7.4节
RCC寄存器 (复位和控制控制寄存器),我们发现第
7.4.6节
AHB外设时钟使能寄存器 (
RCC_AHBENR和
AHB总线
时钟使能寄存器 )。 早些时候,我确定我的LED已连接到
PA4引脚-因此,我需要将一个单元写入寄存器的第17位,以便固定端口
A。因此,代码应为
RCC->AHBENR |= (1 << 17);
或者,这是同一回事
RCC->AHBENR |= 0x20000;
使用
#define文件
stm32f0xx.h写入
RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
我们已经为端口
A供电,现在我们需要通知MK
PA4将在
出口工作-我们将阅读第
8节
通用I / O(GPIO) ; 本节的引言已经说到“每个通用I / O端口都有四个32位配置寄存器(
GPIOx_MODER ,
GPIOx_OTYPER ,
GPIOx_OSPEEDR和
GPIOx_PUPDR ),两个32位数据寄存器(
GPIOx_IDR和
GPIOx_ODR )...”-
每个GPIO端口中有4个调整寄存器和2个数据寄存器 -这就是我们所需要的(配置端口
A或
PA4输出,并定期向其发送
0和
1 )。 为了更好地理解所发生的情况(理论),您可以阅读本节,但我向下滚动至
8.4节
GPIO寄存器并根据说明配置端口。
- 端口模式 -退出。 根据文档,有必要在相应寄存器( GPIOA_MODER )的相应区域( MODER4 )中写入01 ,即 第9位和第8位:第9位应为零,以第8位为单位:
GPIOA->MODER |= (1 << 8); //
GPIOA->MODER |= 0x100; //
GPIOA->MODER |= GPIO_MODER_MODER4_0;
- 输出类型 。 老实说,我仍然没有完全弄清楚这种情况的电路(我会理解,再次阅读论坛等),但是研究有关MK输出配置的其他资源以及逻辑和直觉,建议应该有所作为-pull ,之后应上拉 。 无论如何,代码都已编写,一切正常,没有任何内容耗尽。 如果选择漏极开路类型并将此输出与另一个设备短路,则存在烧伤的真正风险,例如 这是一个开放的出口,不受任何保护。 另外,我们在二极管的前面有一个限流电阻-它肯定不会在这里燃烧。
根据文档,有必要在第四位写零; 该文档还指出,重置后将为零。
GPIOA->OTYPER &= ~(1 << 4); //
GPIOA->OTYPER &= ~0x10; //
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_4;
- 输出速度 。 在我们的情况下,这并不重要,但是为了保真,我将在此处写入零。
GPIOA->OSPEEDR &= ~(1 << 8); //
GPIOA->OSPEEDR &= ~0x100; //
GPIOA->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR4_0;
- 电梯 。 因为 输出将为LED供电,您需要将其上拉至电源,即 上拉
必须拧紧端口A的第4针; 该文档说,为此,有必要分别在9和8位中写入0和1。
GPIOA->PUPDR |= (1 << 8); //
GPIOA->PUPDR |= 0x100; //
GPIOA->PUPDR |= GPIO_PUPDR_PUPDR4_0;
3.3.3 LED开/关和延迟
前面我们读到每个端口都有寄存器,包括
IDR和
ODR数据寄存器-分别是输入和输出数据寄存器。 MK分支上的逻辑零和一-这是数据吗? -是的,数据。 数据可以来自微控制器外部(作为
输入 ),然后退出微控制器并进入另一个设备(作为
输出 )。 MK支路上的单元是存在高电压电平,即 如果将一个带到输出,则将有电压,并且此LED可以为我们的LED供电。 单元向微控制器支路的输出不同于将单元写入
ODR输出寄存器。
根据文档,我们看到每个端口(
A ,
B ,
C ,
D ,
F )都有一个32位寄存器。 端口的引脚数不能超过16个,则仅使用寄存器的前16位。 每一位对应一个端口号(引脚)。 要将一个单元输出到
PA4支路,需要将一个单元写入第4位,以输出零-将一个零写入第4位,即 从输出端去除电压。

开启LED的代码如下所示
GPIOA->ODR |= (1 << 4); //
GPIOA->ODR |= 0x10; //
GPIOA->ODR |= GPIO_ODR_4;
代码关闭LED
GPIOA->ODR &= ~(1 << 4); //
GPIOA->ODR &= ~0x10; //
GPIOA->ODR &= ~GPIO_ODR_4;
但是,如果您在打开电源线之后写了关闭LED的线,则LED不会闪烁(如果您对发生的事情感兴趣-您可以尝试;什么都不会燃烧,这已经在上面进行了讨论)-那么您需要进行延迟。 计时器用于延迟,但是计时器值得一提(由于复杂性),因此我们将延迟使用拐杖:我们将驱动空闲周期。 有一点:
如果启用了
编译 器优化 ,则
编译器将减少我们的空闲周期,并且不会有延迟。 确保未启用优化。 为此,让我们进入项目配置(右键单击项目树中的项目名称),然后检查“
编译 ”选项卡中的“
编译控制字符串”行:它必须具有
-O0参数(“约零”表示禁用优化)。 如果您按照我的指示收集了所有内容,那么很可能还会有
-O0 ,因为 默认情况下,我在这里什么都没碰。 参数
-O1 -O2 -O3表示启用相应级别的优化。
空闲周期可以这样写:
int t = 4000000; while(t > 0) t--;
我没有这样设置
t的值,我这样推断:如果微控制器以8MHz运行,那么它将在一秒钟内执行大约8,000,000条指令,如果您夸大其词,那么半秒的延迟将需要运行4,000,000次。
空闲周期将需要在打开LED之后,关闭LED之后运行,并且所有这些共同循环。
3.4编写代码并运行
让我们将之前编写的所有代码行放在一起。 您还需要包括
stm32f0xx.h头文件,如下所示 我们依靠它并从中获取结构,地址和值的定义。 结果应为:
#include "stm32f0xx.h" int main(void) { int t; // '' RCC->CR |= 0x1; // HSI RCC->AHBENR |= RCC_AHBENR_GPIOAEN; // A GPIOA->MODER |= GPIO_MODER_MODER4_0; // PA4 GPIOA->OTYPER &= ~GPIO_OTYPER_OT_4; // push-pull PA4 GPIOA->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR4_0; // PA4 GPIOA->PUPDR |= GPIO_PUPDR_PUPDR4_0; // pull-up PA4 while(1) { GPIOA->ODR |= GPIO_ODR_4; // PA4 t = 4000000; while(t > 0) t--; // GPIOA->ODR &= ~GPIO_ODR_4; // PA4 t = 4000000; while(t > 0) t--; // } }
单击重建,然后通过实用程序在板上填写代码。

为了使开发板能够启动新固件,请不要忘记关闭BOOT跳线并进行复位(RESET)。
4.结论
代码已编写,一切正常。 部队消耗得无法估量。 我很高兴基于文档,事实证明编写了工作代码,主要是因为STM拥有高质量的文档。
计划是写一篇关于如何在没有IDE的情况下通过控制台
真正手工组装所有东西的文章,理想情况下,这一切都可以在Linux下完成。 现在,我正在研究PWM和ADC(也在该板上)-我也会在它们上写一篇文章。