文章目录
- 嵌入式开发常识汇总
- 1、嵌入式Linux和stm32之间的区别和联系
- 2、stm32程序下载方式
- 3、Keil5安装芯片包
- 4、芯片封装种类
- 5、STM32命名
- 6、数据手册和参考手册
- 7、什么是寄存器、寄存器映射和内存映射
- 8、芯片引脚顺序
- 9、stm32芯片里有什么
- 10、存储器空间的划分
- 11、如何理解寄存器说明
- 12、如何操作寄存器的某一位
- STM32F407芯片学习
- 1、stm32单片机启动流程
- stm32启动模式选择
- 启动流程
- 2、新建一个工程
- 1、本地目录设置
- 2、Keil新建工程
- 3、编译项目,看有无错误
- 3、GPIO寄存器分析和使用
- 1、GPIO寄存器
- 2、 使用GPIO点亮LED灯
嵌入式开发常识汇总
1、嵌入式Linux和stm32之间的区别和联系
1)嵌入式MPU:Miroc Processor Unit 微处理器单元,一般指性能更高的芯片,可以运行Linux+ARM架构,一般跑Linux或者Android系统,可以移植Qt。
2)嵌入式MCU:Micro Control Unit 微控制器单元,常见的有stm32、51、ESP8266等,芯片资源较少,性能较低。
3)嵌入式DSP:Digital Signal Procrssor :专门用于数字信号处理的芯片。
MPU和MCU都是芯片,只不过性能和资源不同,在MCU比如stm32Fxx系列的单片机性能较低,不能支持Linux这些较大的系统运行,只能运行FreeRTOS等实时系统,这些系统内核较为简单,运行起来并不需要太多的资源。在MCU上可以使用emXGUI、emWIN等图形框架。在MPU上可以运行Linux、Android等系统,界面开发一般选用Qt。在stm系列中有也有性能强大的芯片可以支持Linux系统。所以,MCU和MPU并不是以芯片公司或名称区分,只看芯片的性能。
2、stm32程序下载方式
1)串口下载:此方法需要USB转TTL下载器。stm32从系统存储器启动,boot0=1,boot1=0,需要串口转TTl,电脑安装CH340驱动,使用串口下载助手,将USB 转 TTL下载器(CH340)的TXD、RXD、GND与开发板的RXD、TXD、GND连接好,下载器另一头插电脑,下载文件即可(工具软件:FlyMcu)
2)STLINK:此方法需要STLINK下载器。电脑安装STLINK驱动,将ST-Link V2的SWDIO、GND、SWCLK、3.3V接到开发板的DIO、GND、CLK、3.3引脚上,有些板的丝印标法不同,但都能对应的看出来,然后将ST-Link插电脑上,在Keil软件上设置好Debug选项,直接下载。此方法下载程序到stm32 的Flash,所以boot引脚和串口下载不同。
3、Keil5安装芯片包
为了可以支持对相应芯片的开发,在安装好Keil软件之后还需要自己安装芯片包,否则在新建工程时找不到对应芯片。keil软件官方提供各种芯片所需要的包。地址:http://www.keil.com/dd2/pack/
4、芯片封装种类
参考文档:https://blog.csdn.net/ffdia/article/details/116222777
5、STM32命名
6、数据手册和参考手册
数据手册主要用于芯片选型和设计原理图时参考,参考手册主要用于在编程的时候查阅外设的功能和寄存器说明
7、什么是寄存器、寄存器映射和内存映射
STM32有32根地址线,理论上其寻址空间为2的32次方,所以STM32的有效地址最多有4G个,每个地址代表1B,STM32的存储地址就有4GB。
然而上述都是理论上的,STM32可以使用的存储空间远远没有4GB。
映射的意思就是将理论上存在的地址对应到具体的设备,如:0x1234 5678 ~ 0x2234 5678 ==>flash,在硬件上将对应的线路连接至Flash,这样在访问这些地址时就是在访问Flash设备,通过指针对这些地址读写就是对Flash的读写。
内存映射(存储器映射): 将理论上的地址对应到具体的外设上。
寄存器映射 :给对应的内存地址取个别名,便于记忆和访问内存地址 例如:define REGISTER 0x1111 2222
寄存器:为不同内存地址取的别名,别名与内存映射的外设有关,如上面的REGISTER,若定义 (uint_16*)REGISTER,则REGISTER寄存器就代表 0x1111 2222 往后的16个地址,对寄存器的操作就是对这16个地址操作,一般使用定义结构体,这样只需要将结构体首地址对应到寄存器基地址,结构体中的成员就可以按照对应的基地址往后占位,操作结构体成员,就是在对基地址之后的地址操作。
在存储器 Block2 这块区域,设计的是片上外设,它们以四个字节为一个单元,共 32bit,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。我们可以找到每个单元的起始地址,然后通过 C 语言指针的操作方式来访问这些单元,如果每次都是通过这种地址的方式来访问,不仅不好记忆还容易出错,这时我们可以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。
8、芯片引脚顺序
一般在芯片的一角有一个缺口或者小圆点,此处为1脚,引脚按逆时针排序。
9、stm32芯片里有什么
1)ContexM内核====>相当于CPU,由ARM公司设计
2)片上外设====>ISP、USART、ADC、DAC、FSMC、Flash、RAM等,这些由ST厂家设计
3)系统总线矩阵==>连接外设和内核,由ST厂家设计。
10、存储器空间的划分
存储器空间被ARM公司打开划分为8块(block0~block7),每块大小为512MB。最重要就是Block0 ~ 2 这三块,Block0 为内部Flash,用作程序存放,Block1为内部RAM,Block2为外设地址,ST公司将Block2划分为AHB、APB,APB又划分为APB1、APB2,AHB又划分为AHB1、AHB2.
11、如何理解寄存器说明
12、如何操作寄存器的某一位
使用逻辑或或逻辑与,例如操作寄存器32位reg的第六位
置1:( * reg)|=( 0x01<<6);
置0:( * reg)&=~(0x01<<6)
STM32F407芯片学习
1、stm32单片机启动流程
stm32启动模式选择
复位后,在 SYSCLK 的第四个上升沿锁存 BOOT 引脚的值。BOOT0 为专用引脚,而 BOOT1 则与 GPIO 引脚共用。一旦完成对 BOOT1 的采样,相应 GPIO 引脚即进入空闲状态,可用于其它用途。BOOT0与BOOT1引脚的不同值指向了三种启动方式:
从主Flash启动。主Flash指的是STM32的内置Flash。选择该启动模式后,内置Flash的起始地址将被重映射到0x00000000地址,代码将在该处开始执行。一般我们使用JTAG或者SWD模式下载调试程序时,就是下载到这里面,重启后也直接从这启动。
从系统存储器启动。系统储存器指的是STM32的内置ROM,选择该启动模式后,内置ROM的起始地址将被重映射到0x00000000地址,代码在此处开始运行。ROM中有一段出厂预置的代码,这段代码起到一个桥的作用,允许外部通过UART/CAN或USB等将代码写入STM32的内置Flash中。这段代码也被称为ISP(In System Programing)代码,这种烧录代码的方式也被称为ISP烧录。关于ISP、ICP和IAP之间的区别将在后续章节中介绍。
从嵌入式SRAM中启动。显然,该方法是在STM32的内置SRAM中启动,选择该启动模式后,内置SRAM的起始地址将被重映射到0x00000000地址,代码在此处开始运行。这种模式由于烧录程序过程中不需要擦写Flash,因此速度较快,适合调试,但是掉电丢失。
原文链接
启动模式只决定程序烧录的位置,加载完程序之后会有一个重映射(映射到0x00000000地址位置);真正产生复位信号的时候,CPU还是从开始位置执行。
值得注意的是STM32上电复位以后,代码区都是从0x00000000开始的,三种启动模式只是将各自存储空间的地址映射到0x00000000中。
启动流程
1)进入启动文件(厂家提供)
2)设置内存地址
3)设置向量表
4)设置时钟
5)初始化寄存器
6)设置堆栈
7)设置各指针值
8)进去main函数
以上操作在启动文件中的顺序可以参考启动文件中的汇编代码。
2、新建一个工程
1、本地目录设置
1)在本地新建项目文件夹
2)新建子目录用于存放对应文件
3)将厂家提供的文件放至对应新建目录
CMSIS下的文件
启动文件
寄存器配置文件、管理文件
内核外设文件,拷贝至Lib
2、Keil新建工程
1)创建项目
2)设置项目目录
3)向项目目录添加文件
4)设置项目,使用C库文件,在使用串口时可以使用printf函数。
默认宏
设置USE_STDPERIPH_DRIVER,STM32F40XX这两个宏之后,在编译程序时会根据宏添加所需要的文件。
设置Debug,选择要使用的调试器,如STLINK等
设置调试器选项
3、编译项目,看有无错误
根据错误添加头文件路径或头文件
再次编译,出现错误,f407没该外设,项目中删除该外设的文件
再次编译,报错
TimingDelay_Decrement();函数在系统SysTick的中断服务函数中调用,但是并没有实现此函数,所以报错,该函数应该提供的内容可以是操作全局变量计时。先在还没有定义此函数,注释即可。再次编译。
未定义SystemInit函数,该函数在启动文件中被引用,但是并没有实现,可以在其他地方实现该函数,后者在启动文件中删除SystemInit函数相关语句。在使stm32f4xx_it.c中实现该函数。再次编译。
使用MDK5.XX以后的版本,用户不需要写上面的预定义,因为在选择相应器件以后,编译器已经将相应的头文件加入了。取消默认宏,再次编译。
无错误,无告警。下载程序至开发板成功
至此,新建项目完成。
3、GPIO寄存器分析和使用
1、GPIO寄存器
参考手册中又详细内容
1)端口模式寄存器 GPIOx_MODER:配置端口模式,有四种:输入、输出、复用模拟
2) 端口输出类型寄存器 GPIOx_OTYPER:输出模式时需要配置,推挽或开漏
开漏输出,通过一个NMOS管和上拉电阻实现
推挽输出,通过两个mos管实现
3)GPIO 端口输出速度寄存器 GPIOx_OSPEEDR:配置端口电平转换速度,即IO口输出速度
4) 端口上拉/下拉寄存器 GPIOx_PUPDR:无上拉下拉、上拉、下拉、保留。
当GPIO处于output模式,一般选择no pull,引脚能够正确地输出输出高电平低电平信号
当GPIO处于input模式,需要根据默认的输入值来确定配置模式,如果默认输入的值为1时,最好配置为pull up,默认值为0,则选择下拉。
所以上下拉就是选择在默认情况下gpio端口的电平高低。
5)端口输入数据寄存器 GPIOx_IDR:端口输入的数据,即电平高低,该寄存器只能读,低16位有效。
6)端口输出数据寄存器 GPIOx_ODR:端口输出寄存器,低16位有效,可读可写,用以获取或设置端口的输出电平。
7) 端口置位/复位寄存器 GPIOx_BSRR:只能写,高16位BRy(复位),这些位置1,则对应端口复位。低16位BSy(置位),这些位置1,则对应端口置位。复位时,复位位复位值,置位值与复位值相反。对ODR操作。同时对BSx和BRx操作,BSx优先级更高,在对BSSR写后会自动清零。
8)端口配置锁定寄存器 GPIOx_LCKR:可读可写,低17位有效。由于锁定某GPIO端口的配置,每一位锁定某个寄存器的配置。锁定后在复位之前无法更改该端口位的值。
LCKR[16] = 0,未锁定,可以对LCKR[15:0]操作;
LCKR[15:0]:对应一个GPIO端口位,对应位置1,则锁定配置。
9)复用功能低位寄存器 GPIOx_AFRL:四位一组,确定一个端口的复用功能(0~7位端口)
10)复用功能高位寄存器 GPIOx_AFRL:四位一组,确定一个端口的复用功能(8~15位端口)
2、 使用GPIO点亮LED灯
1)确定LED的端口和连接方式
共阳极连接,GPIO默认输出高电平,则灯灭,控制使出低电平则灯亮,连接端口位PF6、7、8(红、蓝、绿)
2)配置GPIOF6、7、8位上拉、推挽、高速模式(对于LED并不重要)
3)写头文件和C文件
问题记录:
问题1:写好后,LED无反应
原因:再新建工程时编译报错未定义SystemInit函数,所以自己在stm32f4xx_it.c中自己写了一个空函数,实际上该函数在system_stm32f4xx.c中,但新建文件时并没有将该文件加入工程,SystemInit函数中设置了时钟等系统资源,将此函数变为空函数则系统时钟等系统资源未初始化,所以代码无法执行。
解决:将system_stm32f4xx.c文件加入项目,删除自己写的空函数
问题2:初始化GPIO后,LED点亮.
原因:ODR复位值为0,LED为共阳极连接,所以导致电压一侧高,GPIO端口处电压低,导致电流流过LED。
解决:初始化GPIO后将将端口置位。
代码
#include "bsp_led.h"
void BSP_LEDInit(void)
{
RCC_AHB1PeriphClockCmd(LED_RAD_CLK,ENABLE);
RCC_AHB1PeriphClockCmd(LED_BLUE_CLK,ENABLE);
RCC_AHB1PeriphClockCmd(LED_GREE_CLK,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = LED_RAD_PIN;
GPIO_InitStruct.GPIO_Mode = LED_RAD_Mode;
GPIO_InitStruct.GPIO_Speed = LED_RAD_Speed;
GPIO_InitStruct.GPIO_OType = LED_RAD_OType;
GPIO_InitStruct.GPIO_PuPd = LED_BLUE_PuPd;
GPIO_Init(LED_RAD_PORT,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = LED_GREE_PIN;
GPIO_InitStruct.GPIO_Mode = LED_GREE_Mode;
GPIO_InitStruct.GPIO_Speed = LED_GREE_Speed;
GPIO_InitStruct.GPIO_OType = LED_GREE_OType;
GPIO_InitStruct.GPIO_PuPd = LED_BLUE_PuPd;
GPIO_Init(LED_GREE_PORT,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = LED_BLUE_PIN;
GPIO_InitStruct.GPIO_Mode = LED_BLUE_Mode;
GPIO_InitStruct.GPIO_Speed = LED_BLUE_Speed;
GPIO_InitStruct.GPIO_OType = LED_BLUE_OType;
GPIO_InitStruct.GPIO_PuPd = LED_BLUE_PuPd;
GPIO_Init(LED_BLUE_PORT,&GPIO_InitStruct);
LED_SetSatus(LED_RAD,DISABLE);
LED_SetSatus(LED_GREE,DISABLE);
LED_SetSatus(LED_BLUE,DISABLE);
}
void LED_SetSatus(LEDx led,FunctionalState status)
{
if(status)
{
GPIO_ResetBits(GPIOF,(uint32_t)(0x01<<led));
}
else{
GPIO_SetBits(GPIOF,(uint32_t)(0x01<<led));
}
}
头文件
#ifndef __BSP_LED_H
#define __BSP_LED_H
#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#define LED_RAD_PORT GPIOF
#define LED_RAD_PIN GPIO_Pin_6
#define LED_RAD_Mode GPIO_Mode_OUT
#define LED_RAD_Speed GPIO_Low_Speed
#define LED_RAD_OType GPIO_OType_PP
#define LED_RAD_PuPd GPIO_PuPd_NOPULL
#define LED_RAD_CLK RCC_AHB1Periph_GPIOF
#define LED_GREE_PORT GPIOF
#define LED_GREE_PIN GPIO_Pin_7
#define LED_GREE_Mode GPIO_Mode_OUT
#define LED_GREE_Speed GPIO_Low_Speed
#define LED_GREE_OType GPIO_OType_PP
#define LED_GREE_PuPd GPIO_PuPd_UP
#define LED_GREE_CLK RCC_AHB1Periph_GPIOF
#define LED_BLUE_PORT GPIOF
#define LED_BLUE_PIN GPIO_Pin_8
#define LED_BLUE_Mode GPIO_Mode_OUT
#define LED_BLUE_Speed GPIO_Low_Speed
#define LED_BLUE_OType GPIO_OType_PP
#define LED_BLUE_PuPd GPIO_PuPd_UP //猜想:设置为下拉,初始化后LED亮
#define LED_BLUE_CLK RCC_AHB1Periph_GPIOF
typedef enum
{
LED_RAD = 6,
LED_GREE = 7,
LED_BLUE = 8
}LEDx;
void BSP_LEDInit(void);
void LED_SetSatus(LEDx led,FunctionalState status);
#endif /*__BSP_LED_H*/
在头文件中将端口参数重定义是为了以后移植方便