STM32 学习笔记_3 程序编写基础;arm 内核架构

程序编写基础

Keil 编辑器设置

image-20230413000523924

抛开 tab 和空格哪个好看不谈,不同编译器设置格式不同,空格比较保险。

用户关键字:打出来的时候会高亮。

image-20230413001147591

代码提示:(symbols after 是几个字符后开始提示关键字的意思)

image-20230413001314821

以上的四个配置会保存在 global.prop 文件里,以后重装保存这个文件就可以保存配置。

常用操作

shift tab 整体左移,直接 tab 右移。

函数变量快速定位:魔术棒-output-browse information。右键 goto definition 即可跳转。

快速注释:有一个 // 的符号,选中代码段后点击一下即可快速注释。

快速打开头文件:右键头文件名-open document xxx.h

查找:image-20230413011326761

replace 是替换。

build output 窗口里双击 error 是可以快速定位过去的。

窗口视图恢复默认状态的方法:window à Reset View to Defaults + Reset

C语言基础

stdint.h

c99中的标准库文件。

image-20230413020428445

image-20230413020443388

位操作

& | ~ ^ << >> 没啥多说的。

给某一位赋值:1. 先&把其他位保留,该位置0,再|把其他位保留,该位置1. temp &= 0xFFFFFFBF; temp |= 0x00000040;

  1. temp &= ~(1<<6); temp |= 1<<6;

宏定义

#define PI 3.14159

image-20230413131117716

#define __set_task_state(tsk, state_value)   \
  do {
      
       (tsk)->state = (state_value); }  while (0)

一定要用 do while(0) ,这样程序才会按照我们期望的方式执行。

宏定义为什么要使用do{……}while(0)形式_土豆爸爸的博客-CSDN博客

宏定义避免影响控制流程,比如宏定义的函数里面可以 return 就比较危险。

image-20230413131450340

宏定义传参避免参数变化,如 (SQUARE(a++))

image-20230413131533236

image-20230413022443594

extern

用 extern 声明的函数或变量代表是在其他文件定义的。

image-20230413022918348

typedef

typedef unsigned char uint8_t;

struct

image-20230413023046175

image-20230413023139081

指针

char *ptr="234";

代码规范

tab 设置为4个字符。

80列后的字符分行。

image-20230413033058738

image-20230413033117662

相对独立的代码块,变量之间加空行。

多行语句不要写在一行里。if 语句也是,而且 if 语句后面哪怕只有一个语句也不要偷懒,加大括号。while 后面没有执行语句的话可以不用大括号。

左括号另起一行。

if、swich、case、for、do、while 后加空格。

sizeof、typedof、alignof 后面不加空格。

指针的*靠近变量名,而不是数据类型。

= + - < > * / % | & ^ <= >= == != ? : 左右加空格。

一元操作符后不加空格, & * + - ~ ! sizeof typeof alignof __attribute__ defined。

++ – . -> 前后都不加空格。

逗号和分号只在后面加空格。

注释 /* 内容 */ 和内容之间加空格。

注释规范

image-20230413034927343

image-20230413034952309

image-20230413035001669

星数必须100个。和代码开始隔一行。

函数注释:

image-20230413035526319

image-20230413035535796

image-20230413035938271

image-20230413035945902

代码注释:

image-20230413040034734

过长放在代码前。

标识符命名:小写,下划线分割,这是 unix 风格。

常用反义词:

image-20230413120036503

循环变量允许 ijk。可以用 g_ p_ 开头表示是全局变量或指针。

不用 u8 u16 u32,使用:

image-20230413120241744

函数命名同变量。

宏命名全大写,单词间用下划线分隔。

函数:一个函数只实现一个功能。

函数之间空行分隔,export 紧跟着函数。

image-20230413120402318

嵌入式开发 linux 常用 goto,清理操作之类很方便。

函数嵌套不建议>4层。

函数要处理好输入参数的合理性,出现错误也能准确返回错误信息。

只在本文件范围内定义的函数变量 static 修饰。

一个变量最好不要多用途。

image-20230413122654031

少用全局变量,避免全局变量和局部变量重名。

初始化尽量使用初值。直接赋值或者 ?: 赋值。

明确全局变量的初始化位置,且减少不必要的数据类型转化。

内核架构

内核 ARM 厂商授权,外设其他公司添加。板子上黑色的 STM32 一整个就是 arm 公司架构和已发半导体公司合作的结果。

image-20230413193540763

F1 架构

image-20230413194651347

image-20230413194954346

主动单元可以给被动单元发起通信。

以总线矩阵为分界线,左边是主动单元,右边是被动单元。

ICode 直接连接到 FLASH 而不需要总线矩阵做中介,里面存储的是程序,这样访问程序速度很快。

image-20230413195408442

ICode 总线对应图中的 IBus。

image-20230413195859793

外设分为不同频率满足不同外设的需求。

cortex-M3 内核:

左上和右上是跟踪调试,左下是下载。

image-20230413200045162

SDIO 和时钟:

image-20230413200248009

APB 外设:

image-20230413200426202

ICode:

image-20230413200506259

以及一些正点课件里没有详细展开说明的内容:从上到下依次是电源,电源管理,外部晶振,看门狗计时器。

image-20230413200708790

此外,互联型的开发板还会添加网络模块的主动单元,不过我们的不是。

F4 架构

image-20230413201012608

在 ARM Cortex-M4 中,FSMC 是一种嵌入式外设,它代表外部存储器接口。FSMC 支持连接到存储器、LCD 显示器和其他外部设备,可以实现高速数据传输和存储器扩展,以满足大多数嵌入式系统的要求。——ChatGPT

image-20230413201237816

CCM 存数据,访问速度快,但是不支持 DMA。

总线时钟频率:

AHB1/2:168/180MHz (Max)

APB1:42/45MHz (Max)

APB2:84/90MHz (Max)

系统原理图基本和 F1 差不多。

F7 架构

image-20230413202136751

image-20230413202154993

image-20230413203743899

寻址范围

32位单片机,32位地址总线。

存储单元按字节编址。就是说有2^32个按字节编址的存储单元。

每个存储单元的寻址范围:0x0000_0000~0xFFFF_FFFF。

存储器映射

存储器自己是没有地址的,是我们给他映射过去的。

image-20230413204709800

前三个是学习的重点。

image-20230413204927606 image-20230413204937494

image-20230413204946174

寄存器映射

寄存器是单片机内部的控制结构,我们的目的归根结底就是控制寄存器,进而控制单片机。

image-20230413210100271 我们主要学习外设寄存器。

给寄存器地址命名的过程叫寄存器映射。如 0x4001080c -> GPIOA_ODR。

stm32 GPIO 每组16个 IO 口,比51多一倍。

image-20230413210802320

名字:GPIOx_ODR。

地址偏移:基于 00 最小的地址加一个偏移值。

寄存器复位值:0x0000_0000.

寄存器位表:查看每个位的偏移量,名称,权限。

image-20230413211248709

位功能描述:比如1亮灯,0灭灯。

操作寄存器:比较逆天的方式是直接操作寄存器地址。

*(unsigned int *)(0x4001_080c)=0xFFFF;

寄存器映射:

#define GPIOA_ODR *(unsigned int *)(0x4001_080c)

GPIOA_ODR=0XFFFF;

但是上百上千个寄存器,怎么计算地址和映射?

寄存器地址计算

  1. 总线基地址/外设基地址,BUS_BASE_ADDR.
  2. 外设基于总线基地址的偏移量 PERIPH_OFFSET.
  3. 寄存器相对于外设地址的偏移量 REG_OFFSET,即上图中每个寄存器的偏移量(0C)。

三者求和计算总线基地址。

总线基地址:APB1 0X4000_0000,APB2 0X4001_0000,AHB 0X4001_8000.

image-20230413212938834

image-20230413212950580

在存储器映像图里可以看到:

image-20230413213233899

比如 GPIOA,外设基地址 0X4001_0000,偏移量 0X800. = 0X4001_0800.

其中的一个寄存器 GPIOA CRH 基于 GPIOA 外设基地址的偏移地址 0X04. = 0X4001_0804.

批量定义寄存器用结构体会很方便。

image-20230414003813622

左边,我们定义了一个7*4的连续空间,因为结构体也是连续存储。

然后通过右边的方式定义了开头指针,则从上到下地址逐渐变大存储。

image-20230414004250651 详细内容可以看 stm32f103xe.h 文件中。

image-20230414004919334

可见前几行的内容对应 block0 block1.

image-20230414005711476

猜你喜欢

转载自blog.csdn.net/jtwqwq/article/details/130143921