ST MCU Finder
安装免费手机应用,
寻找理想的ST MCU

[应用相关] STM32点灯

[复制链接]
419|34
 楼主 | 2019-10-21 11:02 | 显示全部楼层 |阅读模式
本帖最后由 programmable 于 2019-10-21 11:11 编辑

学习stm32有一个多月了,现在开始整理下思路,最大的感受就是,看起来是在学ARM,实际上是在补51,或者说,学习ARM之前真应该好好学学单片机。或者我根本就不应该分这么清楚,学就是了。都说学过了单片机再学ARM就很容易了,自以为单片机学的不错,想当然的认为ARM就不那么难了,刚开始的时候确实是这么想的,买了开发板,打开做好的工程,修修改改,从点灯开始呗,也没觉得有什么难的,不求甚解,达到目的就行了。过了几天,深入的研究下,仔细看下库函数什么的就不明白了,更不用谈*.s的启动文件了,工程下面那么多文件夹也不知道是什么意思,要自己从0写的话根本无从下手,突然觉得有了51的基础怎么学起来还这么难。难道别个说错了?直到看到这篇帖子:从51到ARM这路怎么走?才恍然大悟,当年学51的时候,写个100多行的程序用矩阵键盘和LCD做个密码锁,觉得还行,然后就一直停在那个阶段了,没什么进步,现在看来只是掌握了最基本的开发流程,并没有真正懂51。一直觉得自己不会用到汇编,所以一遇到汇编就skip,至于c语言,也是一知半解。总之,以前欠的太多了,现在只有补呗。

使用特权

评论回复
 楼主 | 2019-10-21 11:02 | 显示全部楼层
就从点灯开始,用库做就是这样

Step1 包含*.h头文件

#include "stm32f10x.h"

使用特权

评论回复
 楼主 | 2019-10-21 11:02 | 显示全部楼层
Step2 定义GPIO初始化结构体

GPIO_InitTypeDef GPIO_InitStructure;

使用特权

评论回复
 楼主 | 2019-10-21 11:03 | 显示全部楼层
Step3 先定义LED的亮灭,我的芯片是STM32F103VET6,LED接的是PB5。

#define LED_ON GPIO_SetBits(GPIOB, GPIO_Pin_5);  

使用特权

评论回复
 楼主 | 2019-10-21 11:03 | 显示全部楼层
Step4 各种声明,时钟配置、LED配置和延时函数。

void RCC_Configuration(void);

void LED_Config(void);

使用特权

评论回复
 楼主 | 2019-10-21 11:03 | 显示全部楼层
Step5 各种配置,初始化函数。

//系统时钟配置为72MHZ

void RCC_Configuration(void)

{   

  SystemInit();

}

使用特权

评论回复
 楼主 | 2019-10-21 11:04 | 显示全部楼层
//LED 控制初始化函数

void LED_Config(void){

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB| , ENABLE);

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

  GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;   

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;   

  GPIO_Init(GPIOB, &GPIO_InitStructure);  

}

使用特权

评论回复
 楼主 | 2019-10-21 11:04 | 显示全部楼层
Step6 终于进main()函数了。

Int main(void)

{

  RCC_Configuration();   //系统时钟配置

  LED_Config(); //LED控制配置

  LED_ON;                     //LED1亮

  while (1)

  {

  

  }

}

使用特权

评论回复
 楼主 | 2019-10-21 11:04 | 显示全部楼层
开发板接好线,编译成功之后load,OK!Led闪烁了。

是不是认为做完了,该收工了。
其实还没开始呢。
分析下每步到底是什么意思,为什么要这样写。

使用特权

评论回复
 楼主 | 2019-10-21 11:05 | 显示全部楼层
Step1 中为什么要包含#include "stm32f10x.h"这样的一个头文件,有什么用?

Stm32f10x.h是控制器专用头文件,包含了STM32F10X全系列所有外设寄存器的定义(寄存器的基地址和布局)、位定义、中断向量表、存储空间的地址映射等。所以要包含这个文件。

使用特权

评论回复
 楼主 | 2019-10-21 11:05 | 显示全部楼层
Step2 GPIO_InitTypeDef GPIO_InitStructure;这是什么意思?

GPIO_InitTypeDef是自己定义的一个数据类型(stm32f10x.h):

typedef struct

{

  __IO uint32_t CRL;

  __IO uint32_t CRH;

  __IO uint32_t IDR;

  __IO uint32_t ODR;

  __IO uint32_t BSRR;

  __IO uint32_t BRR;

  __IO uint32_t LCKR;

} GPIO_TypeDef;

利用关键字typedef和struct,自定义了一个结构体变量,结构体变量的类型名就是GPIO_TypeDef,然后利用这个自定义的数据类型定义了名为GPIO_InitStructure的结构体变量,这个变量在LED 控制初始化函数中将会用到。

使用特权

评论回复
 楼主 | 2019-10-21 11:05 | 显示全部楼层
Step3中是这样定义led的亮:

#define LED_ON GPIO_SetBits(GPIOB, GPIO_Pin_5);

    看下这个函数(在stm32f10x_gpio.c中):

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)

{

  

  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));

  assert_param(IS_GPIO_PIN(GPIO_Pin));

  

  GPIOx->BSRR = GPIO_Pin;

}

使用特权

评论回复
 楼主 | 2019-10-21 11:06 | 显示全部楼层
传递了两个参数:GPIOx和GPIO_Pin,对应着上面的GPIOB, GPIO_Pin_5, assert_param()这个函数是检查输入参数是否有效,如果无效就产生异常;如果有效,带入参数,执行完GPIOx->BSRR = GPIO_Pin之后就是把PB5设置为1。具体是怎么操作的?先看两个参数的类型,GPIOx是指向GPIO_TypeDef这个自定义结构体类型的指针,类型为指针,指向一个自定义的结构体(在stm32f10x.h中):

typedef struct

{

  __IO uint32_t CRL;

  __IO uint32_t CRH;

  __IO uint32_t IDR;

  __IO uint32_t ODR;

  __IO uint32_t BSRR;

  __IO uint32_t BRR;

  __IO uint32_t LCKR;

} GPIO_TypeDef;

结构体的类型为自己定义的GPIO_TypeDef。其中__IO和uint32_t是什么东东?

使用特权

评论回复
 楼主 | 2019-10-21 11:06 | 显示全部楼层
__IO定义在这里(core_cm3.h):

#define     __IO    volatile      

这个宏定义就是用__IO来替换volatile的,那volatile这个关键字又有什么作用。Volatile是不稳定的意思,说明这种类型的值容易发生变化,使用volatile就是不让编译器进行优化,每次读取或者修改值的时候,都必须通过它的硬件地址重新读取或修改,如果不加volatile关键字,编译器就会进行优化,把定义的常量保存在寄存器中,以后就通过访问这个寄存器来访问这个值,因为访问寄存器的速度比访问内存还要快,就达到了优化的效果,但是这个值可能会在外部发生改变,尤其是在嵌入式与硬件相关或者中断的这些地方,它的值在外部改变了之后,装在寄存器中的那个值并没有随着更新,依旧取的是之前优化的时候存下的值,所以取到的就是错误的值了。

__IO:输入输出口,作为输入的时候,当然不能进行优化,它的值随时都会改变,作为输出的时候;也不能优化,优化之后,输出的始终是同一个值,而这个值是会改变的,如果连续两次输出相同值,编译器认为没改变,就会忽略后面那次输出,这就很严重了。

使用特权

评论回复
 楼主 | 2019-10-21 11:06 | 显示全部楼层
uint32_t定义在这里(stdin.h):

typedef unsigned int uint32_t;

    使用typedef定给unsigned int 类型气的别名,因为不同的平台会有不同的字长,所以利用预编译和typedef可以最有效的维护代码。这样很明显的看出是4个字节。

使用特权

评论回复
 楼主 | 2019-10-21 11:07 | 显示全部楼层
好了,另一个参数自然也就明白了,是2字节的变量了。

再回到这个函数:

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

由于GPIO是个指针,GPIO_Pin为常量,所以

    #define LED1_ON GPIO_SetBits(GPIOB, GPIO_Pin_5); 预编译之后相当于

GPIOB->BSRR = GPIO_Pin_5;就是把GPIOB的BSRR寄存器设置为0x0020,刚好 第五位置为1,由于是指针变量,所以用“->”直接取自定义结构体GPIOB 中的BSRR 变量的地址,然后赋值为GPIO_Pin_5,在stm32f10x_gpio.c中有这样的定义:

        #define GPIO_Pin_5                ((uint16_t)0x0020)  

使用特权

评论回复
 楼主 | 2019-10-21 11:07 | 显示全部楼层
0x0020为0x 0000 0000 0010 0000,然后查STM32参考手册:

483645dad20edcd2b0.png

把GPIOB的BSRR寄存器的第五位置1,这个寄存器为端口为设置/清除寄存器。

使用特权

评论回复
 楼主 | 2019-10-21 11:07 | 显示全部楼层
Step4 声明这个文件需要用到的函数,时钟、LED。

使用特权

评论回复
 楼主 | 2019-10-21 11:07 | 显示全部楼层
Step5

首先是时钟配置,只需要调用系统初始化函数void SystemInit (void)就OK了,在system_stm32f10x.c中,这是微控制器专用系统文件,而函数SystemInit用来初始化为控制器。

然后是LED控制初始化函数,使能APB2口中GPIOB的时钟,设置为通用推挽输出模式,最大输出速度为50MHz,然后向PB5口送数据,调用GPIO_Int函数,OK。(至于推挽模式和最大输出速度为什么这样设置,现在还不是很清楚,研究中···)

使用特权

评论回复
 楼主 | 2019-10-21 11:08 | 显示全部楼层
Step6 进入main函数,时钟和led配置完了就停在while(1)中,成功点亮led。

使用特权

评论回复
扫描二维码,随时随地手机跟帖
*滑动验证:
您需要登录后才可以回帖 登录 | 注册

本版积分规则

我要发帖 投诉建议 创建版块 申请版主

快速回复

您需要登录后才可以回帖
登录 | 注册
高级模式

论坛热帖

在线客服 快速回复 返回顶部 返回列表