我们以STM32F030f4p6为例开始研究微控制器



0.阅读文章之前


本文的目标如下:

  1. 展示如何专门使用此板;
  2. 显示仅依靠文档和逻辑即可编写闪烁的LED程序的方法;
  3. 以对微控制器不熟悉的人可以理解的语言展示材料。

就使用其他文件而言,该代码将变得非常简单-我们将不包括单个文件,除了构建空的但有效的固件所需的文件之外。 即 基于固件代码,虽然可以,但是没有任何用处。

我们将需要以下文档:

  1. 数据表STM32F030x4(我使用2017年1月的文档DocID024849 Rev 3);
  2. RM0360参考手册STM32F030x4 / x6 / x8 / xC(我使用2017年4月的文档DocID025023 Rev 4);
  3. 电路板。

您可以从云中下载这些文档。
本文中的计时器将被考虑,并且不会被包含在代码中。
使用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端口

端口设置为标准配置。 您可以自行决定更改COM端口号。 以我的经验,我一生只看过一个程序,只能看到前4个COM端口,如果我没记错的话,那是Windows下的某种蓝牙终端。

COM端口设置


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配置实用程序




运行此应用程序,它实际上是最简单的(包含最少的设置)。 在第一个窗口中,选择:

  1. 接口(我有COM-3);
  2. 计算机和微控制器通信的速度(IMHO,9600正常值);
  3. 数据位数(出于某种原因,此窗口对我不可用,但到目前为止这并不重要);
  4. 奇偶校验(我没有奇偶校验,即无);
  5. 回声(我有关闭);
  6. 等待时间(我有10秒)。

单击下一步,如果一切正常,那么我们将看到绿灯,并且“目标是可读的”。 如果看到红灯,则表明计算机无法连接。

如果成功检测到微控制器,则目标是可读的


总是有帮助的步骤顺序:

  1. 首先,您需要检查板上的BOOT跳线是否已关闭。
  2. 其次,在任何情况下,都应关闭微控制器的电源,最好关闭从适配器到电路板的TX和RX线(您不能断开接地)。
  3. 第三,在程序中,按返回到末尾,即 返回首页,甚至将其关闭然后重新启动(通常来说,它有时会冻结)。 重要的是,始终先从首页开始,然后再通过此程序与主板进行每次连接。
  4. 第四,从适配器到电路板的导线接好,然后尝试再次在程序中连接(请确保从首页开始!)。

如果其他所有方法均失败,则可以尝试关闭所有功能,重新启动计算机,然后尝试重新连接至主板。

因为 我在虚拟机上工作,我必须多次重新连接USB-COM适配器,以便虚拟机可以检测到它,并且主机没有时间安装损坏的驱动程序。

我在撰写本文时发现的另一种选择是按下板上的按钮,而不是不断拉动电线。 但是,无论如何都必须关闭并打开BOOT跳线。 该选项起作用的原因是该按钮位于外部NRST复位的底部。

在下一个窗口中,选择目标设备Target。 顺便说一句,有时在这里您通常可以看到(也许是一个错误)左侧的设备,例如,代替STM32而不是STM8-在发生某种故障的地方,上面已经描述了处理程序。 因此,在此步骤中,您不必急于单击“下一步”,但是请始终注意在Target中选择了所需设备的事实。

目标设备选择


如何确定我们拥有的设备? -我们看一下芯片并重写所有写在芯片上的东西。 我们打开芯片上的数据表 ,“ 订购信息 ”部分描述了哪个字母负责什么。 以我为例:



我在Target中选择我的芯片(16K),然后继续。


芯片提供4种动作选择:

  1. 擦除内存(整个或选择一个特定区域);
  2. 将固件写入设备;
  3. 从设备读取固件;
  4. 启用/禁用写或读保护。

2.3从板上读取固件


当我第一次连接开发板时,我决定保留原始固件,这是一种备份-我们现在就做。 有必要指出该固件的保存位置以及要保存的内存页面,还建议使用hexbins19文件格式进行选择

选择要读取的存储页面


如果您只是将固件上载到板上或从板上读取固件,则这些文件格式之间没有区别。 以下是进度页,有时该进程会长时间冻结99%(不一定是99),但应该在几秒钟后成功完成-实际上,在此之后,开发板没有给出与加载的固件相对应的行为。 简而言之,您需要重新连接所有内容并重新填充固件,对此没有什么要求。

固件文件已保存,以后可以上传到板上。

但是,如果安装了读取保护,则无法读取固件。

进度窗口


2.4刷板


现在填写固件文件,其源代码如下。 展望未来,我会说我们将上传binhex文件,因为 开发环境将发布它们。 s19hex文件的其他设置相同。 与它们不同的是,在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。 我知道电路中的二极管由D1D2 ...表示,即 D ==二极管 ,在我的电路板上靠近电阻R7的是二极管D1

板照片


也许,在仔细检查电路板之后,您可以找到二极管挂在哪条腿上,但我将转向电路板。 不幸的是,电路板的元件在其位置上与电路中的元件并不完全匹配。 但是我很高兴在互联网上找到了这样的方案(否则我很长时间都找不到了)。



在该图中,我们看到二极管的阴极通过跳线J2接地,而阳极通过电阻器连接到PA4引脚。 PA4表示端口A的第四个输出,这意味着要点亮和关闭LED,必须向PA4的输出端提供电压。

接下来,您需要确定如何向该输出施加电压。 对我而言,这根本不是直观的,并且很长一段时间以来,我一直在仔细阅读文档,直到在数据表最开始的“ 描述”部分遇到了框图 。 在其中我看到了珍贵的轨道PA [15:0] <=> GPIO端口A <=> AHB解码器<=>总线矩阵<=> Cortex-M0 ,即 端口A是通用I / O端口,并连接到AHB总线。

方块图
(图片可点击)


我注意到在电子产品中,通常将微控制器的输出分成端口,通常该端口有16个输出。 该图显示端口ABC只有16个,但是端口DF却较少(少于16个引脚,更多-否)。

让我们回到“ 时钟树”方案,找到AHB签名的输出。 我们将弄清楚该输出的工作频率。 HCLK信号到AHB ,离开HPRE分频器。 该分频器从开关SW接收SYSCLK信号。 通过编程设置 SW输入中的哪个信号将用作SYSCLK ,然后在代码中进行设置。 提供选择:

  1. HSI-来自内部高频发生器的信号,它是由一个8 MHz石英谐振器产生的,在使用该板之前我已将其焊接;
  2. PLLCLK-来自倍频器PLLMUL的信号;
  3. HSE-来自外部高频发生器的信号。

任何选项都适合我们的任务,我建议选择其中最简单,最实惠的HSI

我们将进入参考手册并打开第7节“ 复位和时钟控制(RCC)” ,特别是7.2.6系统时钟选择 ,在这里我们再次遇到数据表中类似的措辞:“系统复位后,选择HSI振荡器作为系统时钟”-即 我们甚至不需要做任何事情,MK将从HSI开始。

为了确保MK确实可以从此源工作,我将在程序中明确编写此代码。 滚动到负责复位和时钟的寄存器 (第7.4RCC寄存器 )。 文档中描述的第一个寄存器是时钟控制寄存器(RCC_CR) ; 下面是对位的描述,这些位负责什么。

时钟控制寄存器


我们对HSION零位感兴趣,该位负责打开谐振器( 0-关闭, 1-开启)。

因此,有必要向RCC_CR寄存器写入1。 (零位为1,或2 0 = 1)。

现在,我们在stm32f0xx.h文件中找到RCC的定义( #define RCC )。

RCC-> RC


如您所见,这是位于RCC_BASE的结构; 地址0x40021000 ,如果您全部展开define ,则可以在《 参考手册》中的2.2.2节“ 内存映射和寄存器边界地址”以及在数据表的“第5章内存映射AHB区域)”中看到相同的地址。

要在RCCCR寄存器中写入一个启用HSI的单元,需要一行代码

RCC->CR |= 0x1; 

3.3.2设置腿


向微控制器的脚发送信号以点亮LED并停止信号,以使LED熄灭是简单的操作,因此适用于GPIO功能(通用输入输出端口)。

默认情况下,MK支脚未连接,即 输出是不确定的。 必须连接端口,该端口的脚将为LED供电。 之前,我们确定GPIO端口已连接到AHB总线-您需要调整该总线。 继续翻阅第7.4RCC寄存器 (复位和控制控制寄存器),我们发现第7.4.6AHB外设时钟使能寄存器RCC_AHBENRAHB总线时钟使能寄存器 )。 早些时候,我确定我的LED已连接到PA4引脚-因此,我需要将一个单元写入寄存器的第17位,以便固定端口A。

AHB外设时钟使能寄存器


因此,代码应为

 RCC->AHBENR |= (1 << 17); 

或者,这是同一回事

 RCC->AHBENR |= 0x20000; 

使用#define文件stm32f0xx.h写入

 RCC->AHBENR |= RCC_AHBENR_GPIOAEN; 

RCC-> AHBENR


我们已经为端口A供电,现在我们需要通知MK PA4将在出口工作-我们将阅读第8通用I / O(GPIO) ; 本节的引言已经说到“每个通用I / O端口都有四个32位配置寄存器( GPIOx_MODERGPIOx_OTYPERGPIOx_OSPEEDRGPIOx_PUPDR ),两个32位数据寄存器( GPIOx_IDRGPIOx_ODR )...”- 每个GPIO端口中有4个调整寄存器和2个数据寄存器 -这就是我们所需要的(配置端口APA4输出,并定期向其发送01 )。 为了更好地理解所发生的情况(理论),您可以阅读本节,但我向下滚动至8.4GPIO寄存器并根据说明配置端口。

  1. 端口模式 -退出。 根据文档,有必要在相应寄存器( GPIOA_MODER )的相应区域( MODER4 )中写入01 ,即 第9位和第8位:第9位应为零,以第8位为单位:

     GPIOA->MODER |= (1 << 8); //  

     GPIOA->MODER |= 0x100; //  

     GPIOA->MODER |= GPIO_MODER_MODER4_0; 

    GPIO端口模式寄存器


    GPIOA->模块


  2. 输出类型 。 老实说,我仍然没有完全弄清楚这种情况的电路(我会理解,再次阅读论坛等),但是研究有关MK输出配置的其他资源以及逻辑和直觉,建议应该有所作为-pull ,之后应上 。 无论如何,代码都已编写,一切正常,没有任何内容耗尽。 如果选择漏极开路类型并将此输出与另一个设备短路,则存在烧伤的真正风险,例如 这是一个开放的出口,不受任何保护。 另外,我们在二极管的前面有一个限流电阻-它肯定不会在这里燃烧。

    根据文档,有必要在第四位写零; 该文档还指出,重置后将为零。

     GPIOA->OTYPER &= ~(1 << 4); //  

     GPIOA->OTYPER &= ~0x10; //  

     GPIOA->OTYPER &= ~GPIO_OTYPER_OT_4; 

    GPIO端口输出类型寄存器


  3. 输出速度 。 在我们的情况下,这并不重要,但是为了保真,我将在此处写入零。

     GPIOA->OSPEEDR &= ~(1 << 8); //  

     GPIOA->OSPEEDR &= ~0x100; //  

     GPIOA->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR4_0; 

    GPIO端口输出速度寄存器


  4. 电梯 。 因为 输出将为LED供电,您需要将其上拉至电源,即 上拉

    必须拧紧端口A的第4针; 该文档说,为此,有必要分别在98位中写入0和1。

     GPIOA->PUPDR |= (1 << 8); //  

     GPIOA->PUPDR |= 0x100; //  

     GPIOA->PUPDR |= GPIO_PUPDR_PUPDR4_0; 

    GPIO端口上拉/下拉寄存器


    GPIOA-> PUPDR



3.3.3 LED开/关和延迟


前面我们读到每个端口都有寄存器,包括IDRODR数据寄存器-分别是输入和输出数据寄存器。 MK分支上的逻辑零和一-这是数据吗? -是的,数据。 数据可以来自微控制器外部(作为输入 ),然后退出微控制器并进入另一个设备(作为输出 )。 MK支路上的单元是存在高电压电平,即 如果将一个带到输出,则将有电压,并且此LED可以为我们的LED供电。 单元向微控制器支路的输出不同于将单元写入ODR输出寄存器。

GPIO端口输出数据寄存器


根据文档,我们看到每个端口( ABCDF )都有一个32位寄存器。 端口的引脚数不能超过16个,则仅使用寄存器的前16位。 每一位对应一个端口号(引脚)。 要将一个单元输出到PA4支路,需要将一个单元写入第4位,以输出零-将一个零写入第4位,即 从输出端去除电压。



GPIOA-> ODR


开启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(也在该板上)-我也会在它们上写一篇文章。

Source: https://habr.com/ru/post/zh-CN437234/


All Articles