stm32入门学习笔记

目录

  • 1.芯片
  • 2.调试器使用,程序烧录
  • 3.编程环境
  • 4.调试

手上芯片的型号
STM32F407-ZGT6

1.芯片

了解芯片选型内部资源,系统架构,内部之间的关系,一些相关名词的解释

芯片选型

stm32入门学习笔记_第1张图片
image.png

内核:arm Cortex-M3 (CM3)
体系结构:哈佛结构
32位处理器,内部数据路径32位,寄存器32位,储存接口也是32位
独立的指令总线和数据总线

手上有的核心板

STM32F103C8T6

Cortex-M3
CLOCK:72Mhz
FLASH:64KB
SRAM:20KB
封装:LQFP48
GPIO:37

stm32入门学习笔记_第2张图片
STM32F103C8T6

STM32F407ZGT6

Cortex-M4
CLOCK:168Mhz
FLASH:1024KB
SRAM:192KB
EEPROM:0KB
封装:LQFP114
GPIO:114
小端(简记:低的在那边就是啥端2333)

stm32入门学习笔记_第3张图片
STM32F407ZGT6

stm32入门学习笔记_第4张图片
emm..某宝淘的核心板

内部资源分布(寄存器地址)

STM32F103

SRAM:6k 10k 20K 48k 64k
起始地址:0x1FFF F000

FLASH:16K 32K 64K 128K 256K 384K 512K
0x0800 0000

OptionBytes:0x1FFF F800
其他寄存器 0x4000 0000


2.调试工具

调试仿真器

目前手上有一个JlinkOBstlinkv2,这两个东西都能正常在debian下运作,并且都可以用swd模式进行调试,只是他们用的工具包不一样,使用方法有所差异

支持的模式
JLinkOB: swd
stlink v2 : swim(这个在stm8有用) swd

工具包差异:
JLinkOB: jlink 在non-free分支
stlink:stlink-tools 图形化的烧写工具 stlink-gui

uart串口调试相关的可以使用的工具有
xgcom 图形化的串口调试工具

下面开始记录关于这两个调试器软件的使用


JLINK OB

硬件信息

jlink接口定义,我用的是JlinkOB,只有vcc,gnd,swclk,swdio
对应下图的就是(参考SWD接口的定义)

vcc 不用接
gnd   <->  10号管脚(gnd)

swdio  <->  7号管脚(swdio)
swclk  <->   9号管脚(swclk)

stm32入门学习笔记_第5张图片
image.png

引用 博客图片
stm32入门学习笔记_第6张图片
image.png

官网原版接口说明
手上用的JlinkOB用的是swd模式,四个管脚说明
vcc:提供电压
gnd:公共接地
swdio:串行线跟踪端口
swclk:时钟信号

debian下jlink工具包

aptitude install jlink

工具包信息

软件包:jlink                    
版本号:6.40h
新: 是
状态: 已安装
自动安装: 否
优先级:额外
部分:non-free/devel
维护者:SEGGER 
体系:amd64
未压缩尺寸:59.8 G
依赖于: libncurses5 (>= 5.5), libc6 (>= 2.7)
推荐: libqt4-gui (>= 4.8.6)
描述:SEGGER J-Link tools
 This package provides software tools for SEGGER J-Link debug probes.
主页:https://www.segger.com

注意他是在non-free分支的,要吧non-free分之加到源里去,如果是查看/etc/apt/sources.list
确认项目中有non-free分支

#这里是deepin的,已经包含了non-free分支
deb https://mirrors.aliyun.com/deepin panda main contrib non-free

jlink包一览

/usr/bin/JLinkRTTClientExe


#Jlink 命令行工具
/usr/bin/JLinkExe
/usr/bin/JLinkRemoteServer

#STM32重置option bytes
/usr/bin/JLinkSTM32Exe

/usr/bin/JLinkRemoteServerCLExe
/usr/bin/JLinkLicenseManager
/usr/bin/JLinkGDBServerCLExe
/usr/bin/JLinkRTTClient
/usr/bin/JLinkRTTLogger
#gdbserver
/usr/bin/JLinkGDBServer

/usr/bin/JTAGLoadExe
/usr/bin/JLinkSWOViewerCLExe
/usr/bin/JLinkRegistrationExe
/usr/bin/JLinkRemoteServerExe
/usr/bin/JLinkRegistration
/usr/bin/JLinkLicenseManagerExe
/usr/bin/JLinkGDBServerExe
/usr/bin/JLinkSWOViewer
/usr/bin/JLinkRTTLoggerExe
#图形化烧录工具
/usr/bin/JFlashLite
/usr/bin/JFlashLiteExe

/usr/bin/JFlashSPICLExe
/usr/bin/JFlashSPI_CL

#有一些只相差Exec名称可能是软连接来的,例如
$ls -l `which JFlashLite`

lrwxrwxrwx 1 root root 36 10月 26 21:09 /usr/bin/JFlashLite -> /opt/SEGGER/JLink_V640/JFlashLiteExe

貌似deb包没有包含教程(难道是我没找到?),但是官网倒有基本的user manual,可惜了没找到翻译版的,对着有道凑合着看,包含了一些工具的使用方法,命令参数

重要的章节在J-Link sotfware and documentation package
它有包括JLinkExec软件常用的命令参数说明,里面阐述了大致都有什么软件包,也有对应有图形化的工具,方便使用,例如jflash和jlink gdb都有图形化的工具

暂时用到的有三个(JLinkExec JFlashLiteExe JLinkGDBServer

J-Link Commander(JLink命令行工具)

JLinkExec
它支持一系列基本调试命令,clrBP(清除断点),go(启动cpu),halt(停止cpu),mem(读取内存),等等,大致包含
basic(前面几个也属于基本命令),
Flasher I/O(flash文件读写,他说需要支持,大概就是在flash里对文件进行基本操作把),
Connection(连接命令,可以通过网络连接jlink或者usb,我这里使用的是usb的JLinkOB)
三部分命令,主要用的是basic部分的命令

紧接着就是列出命令的用法了

J-Link GDB Server

JLinkGDBServer
gdb调试服务,大致的调试方式是,J-Link这边的gdb服务开始了,监听端口,然后arm-none-eabi-gdb输入target remote :端口号,然后把elf文件加载,读取符号,进行调试

J-Flash

JFlashLiteExe这个工具可以对flash进行烧写,支持hexbinary格式,binary格式可以指定内存地址烧录,arm-none-eabi-gcc编译出来的是elf文件,需要用arm-none-eabi-objcopy 把elf文件转成其他格式(hex或是binary)

stlink v2

JLink一样,stlink也是一个仿真器,它也有好几个版本,手上的是stlink v2

image.png

linux下的工具包是stlink-tools,以及图形工具stlink-gui

stlink源码的项目地址https://github.com/texane/stlink
包括一些使用方法,说明,问题,可以到里面去看
他主要有三个工具

st-flash

烧写flash命令,参数指定编程器类型,文件名,起始地址,手上stm32f407的flash起始地址是0x08 00 00 00

st-info
st-util

需要用root权限去执行他们,执行命令的时候,要在前面加sudo ,然后输入密码后以root用户的身份去执行
或者可以设置uid,让他们用所有者的身份(uid)去执行,这样就不用每次sudo输密码,当然这样是有隐患的,并不推荐

sudo chmod u+s `which st-info`

stlink-gui使用了gtk,并不能这样设置,否则会有警告

安装:

sudo aptitude install stlink-tools
sudo aptitude install stlink-gui

这个软件感觉还是问题挺多的,烧录的时候,坑多,新手不知道怎么回事把flash锁了,只读,用jlink读写正常,用这个st-flash各种毛病,也能上项目的地址找人家遇上的问题了,大概解决办法都是说先切换boot模式,重置读写,然后再切回来,脑瓜痛,知识限制了我的行动2333


3.开发环境

编译器

gcc-arm-none-eabi支持ARM Cortex-A/R/M 系列的处理器
它适用于没有操作系统的

binutils-arm-none-eabi
gcc-arm-none-eabi
gdb-arm-none-eabi

//相关的库

libnewlib-arm-none-eabi
libstdc++-arm-none-eabi-newlib

aptitude install 安装即可

IDE

sw4stm32
linux下有基于eclipse的sw4stm32,支持jlinkstlink调试,然而我都失败了...jlink使用的是openocd,还没开始就死在了起跑线上...一堆error脑瓜痛,手上的stlink没有被他识别(它到底怎么调用stlink的),反正最终就没搞..基本的编译倒没问题,也正常生产binary文件,编译通过,它其实就是集成了arm-none-eabi-gcc,也可以用Makefile+arm-none-eabi交叉编译链替代

openocd

使用Jlink JTAG模式的调试工具...emm...我怎么在配置文件里还看到了stlink...它的说明很迷...而且用起来也很迷...没看懂怎么配置的..只能先放下了

#openocd安装
sudo aptitude install openocd

头文件

单纯编译器是不够的,还需要头文件支持,定义寄存器位置方便在程序里调用,STM32提供了库,如果不用库,对应的头文件还是可以用的,而且这个头文件挺有意思的:

文件名:stm32f4xx.h
头文件获得的方式:下载官方的提供的库,官网下载起来比较麻烦...去淘宝里看卖stm的宝贝也有资料连接的2333...或者入手开发板的时候,都能找到相关资料,copy到某个文件夹就行了2333,

include前要先定义对应型号cpu的宏,在头文件中有找到这样的说明

 #if !defined(STM32F40_41xxx) && !defined(STM32F427_437xx) && !defined(STM32F429_439xx) && !defined(STM32F401xx)       && !defined(STM32F410xx) && \
   69     !defined(STM32F411xE) && !defined(STM32F412xG) && !defined(STM32F413_423xx) && !defined(STM32F446xx) && !def      ined(STM32F469_479xx)
   70   /* #define STM32F40_41xxx */   /*!< STM32F405RG, STM32F405VG, STM32F405ZG, STM32F415RG, STM32F415VG, STM32F415      ZG,
   71                                       STM32F407VG, STM32F407VE, STM32F407ZG, STM32F407ZE, STM32F407IG, STM32F407      IE,
   72                                       STM32F417VG, STM32F417VE, STM32F417ZG, STM32F417ZE, STM32F417IG and STM32F      417IE Devices */

后面还有很长一段,大概就是对不同的cpu型号进行了分类,可能是寄存器的地址和数量有所差异把,一些外设之类的,例如我的cpu是STM32F407ZG,那么对应属于STM32F40_41xxx这个宏,使用前我就需要

#define STM32F40_41xxx
#include "stm32f4xx.h"

这样就能保证我的头文件正确的被包含了,如果我不用stm提供的库函数,寄存器的方式编程,那么这个头文件够用了。但是这个头文件定义的东西比较多,而且有个比较有意思的

 1471 typedef struct
 1472 {
 1473   __IO uint32_t MODER;    /*!< GPIO port mode register,               Address offset: 0x00      */
 1474   __IO uint32_t OTYPER;   /*!< GPIO port output type register,        Address offset: 0x04      */
 1475   __IO uint32_t OSPEEDR;  /*!< GPIO port output speed register,       Address offset: 0x08      */
 1476   __IO uint32_t PUPDR;    /*!< GPIO port pull-up/pull-down register,  Address offset: 0x0C      */
 1477   __IO uint32_t IDR;      /*!< GPIO port input data register,         Address offset: 0x10      */
 1478   __IO uint32_t ODR;      /*!< GPIO port output data register,        Address offset: 0x14      */
 1479   __IO uint16_t BSRRL;    /*!< GPIO port bit set/reset low register,  Address offset: 0x18      */
 1480   __IO uint16_t BSRRH;    /*!< GPIO port bit set/reset high register, Address offset: 0x1A      */
 1481   __IO uint32_t LCKR;     /*!< GPIO port configuration lock register, Address offset: 0x1C      */
 1482   __IO uint32_t AFR[2];   /*!< GPIO alternate function registers,     Address offset: 0x20-0x24 */
 1483 } GPIO_TypeDef;

这里,因为GPIO相关的寄存器按分组都放在相邻的位置了,他可以通过base+offset计算得出寄存器位置,在C中,结构体的成员就表示的是偏移量嘛...这个GPIO_TypeDef结构体定义方便了后续操作寄存器的方式

再看头文件是怎么定义IO口的

 2385 #define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)
 2386 #define GPIOB               ((GPIO_TypeDef *) GPIOB_BASE)
 2387 #define GPIOC               ((GPIO_TypeDef *) GPIOC_BASE)
 2388 #define GPIOD               ((GPIO_TypeDef *) GPIOD_BASE)
 2389 #define GPIOE               ((GPIO_TypeDef *) GPIOE_BASE)
 2390 #define GPIOF               ((GPIO_TypeDef *) GPIOF_BASE)
 2391 #define GPIOG               ((GPIO_TypeDef *) GPIOG_BASE)
 2392 #define GPIOH               ((GPIO_TypeDef *) GPIOH_BASE)
 2393 #define GPIOI               ((GPIO_TypeDef *) GPIOI_BASE)
 2394 #define GPIOJ               ((GPIO_TypeDef *) GPIOJ_BASE)
 2395 #define GPIOK               ((GPIO_TypeDef *) GPIOK_BASE)

GPIOx_BASE

#define PERIPH_BASE           ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region 
#define AHB1PERIPH_BASE       (PERIPH_BASE + 0x00020000)
#define GPIOF_BASE            (AHB1PERIPH_BASE + 0x1400)

通过base+偏移的方式,一层一层嵌套,GPIOx_BASE是一个具体的数值,也就是GPIO相关寄存器的地址啦,刚好可以套到GPIO_TypeDef这个结构体。使用指针取成员内容的方式就可以操作一组寄存器

GPIOF->ODR |= 0x01;
//上面的就等价于
((GPIO_TypeDef *) GPIOF_BASE)->ODR |= 0x01;
//或者可以写成
(*((GPIO_TypeDef *) GPIOF_BASE)).ODR |= 0x01;

GPIOF这个符号并不是指针,他只是计算出来一个具体的地址

大概编译后是成这样的(删除了部分,保留关键部分)

main:
       ;前面一大段删了
       ;这里是 GPIOF->ODR |= 0x01; 的汇编
       ;++++++++++++++++++++++++++++++++++++++++++++++++++
       ;arm的cpu不能直接访问内存,需要借助ldr str指令交换内存和寄存器的数据
       ;ldr(load register)    ldr dst,src      把src的数据放到dst,右边的放到左边去(str刚刚操作数顺序相反)
                 ;ldr r2,.L3相当于  把地址为1073878016的内存取4字节(32)位放到r2寄存器,下面r3的同理
        ldr     r2, .L3
        ldr     r3, .L3
        
        ;因为我们用到的成员ODR的偏移量是0x14 也就是20啦,ldr r3,[r3,#20]相当于r3 = *(r3+20)   原来r3放的是地址,通过ldr把r3+20的地址上的数据放到r3,这是个32位寄存器
        ldr     r3, [r3, #20]

        ;orr是位或指令,
        orr     r3, r3, #1

       ;因为r3已经是数据了,之前还把地址放到了r2去,现在要把数据放回去
               ;str  src,dst        
        str     r3, [r2, #20]
        ;+++++++++++++++++++++++++++++++++++++++++++++++

        ;while (1) ;汇编部分
       ;++++++++++++++++++++++++++++
       ;b是跳转指令,这里相当于while (1) ;
.L2:
        b       .L2
       ;++++++++++++++++++++++++++++

.L4:
        .align  2

    ;L3是个标签,记录了常数1073878016所在的地址,1073878016的十六进制是:0x40021400,就是GPIOF的值,.word是个伪指令,指示当前位置(.L3)存放的数据是一个数值,并非指令
.L3:
        .word   1073878016
        .size   main, .-main
        .ident  "GCC: (15:6.3.1+svn253039-1+b1) 6.3.1 20170620"

编译

编译主要要注意指定cpu类型,以及包含stm32f4xx.h头文件路径。

#Makefile
CROSS=arm-none-eabi
CC=$(CROSS)-gcc
OBJ_COPY=$(CROSS)-objcopy

#编译参数这里的--specs=nosys.specs有个梗
#如果不指定,会报这个错误:undefined reference to `_exit'...emm没搞懂,-g是生产调试符号, -mcpu指定CPU类型(target),支持cortex-m4,
#更多target支持通过arm-none-eabi-gcc --target-help去看
CFLAGS= -g -mcpu=cortex-m4 --specs=nosys.specs

#头文件所在目录
INCLUDE=~/.arm/include

#源文件
SRC=main.c
#elf文件
DEST=main

#指定烧录文件格式
HEX_FORMAT=binary
#烧录文件
HEX=main.bin

all:$(DEST)
        @make $(HEX)

$(HEX):$(DEST)
        @echo "生成hex文件..."
        @$(OBJ_COPY)  -O $(HEX_FORMAT) $(DEST) $(HEX) && echo "成功!"


$(DEST):$(SRC)
        $(CC) $(CFLAGS) -g -I $(INCLUDE) -o $(DEST) $(SRC)


clean:
        @-rm $(HEX)
        @-rm $(DEST)
        @echo "clean!"


2019.1.15笔记:
make就可以生成了,虽然生成的代码可以被stm32f4执行,然这样生成的并不能正确执行,O(∩_∩)O哈哈~,惊喜不惊喜,意外不意外(MMP...),为什么,,因为我并没有初始化堆栈呀,在有系统的情况下,系统帮你做了这个操作,初始化了相应的堆栈指针,但是如果是这种裸机,只能自己动手丰衣足食了,在arm-none-eabi交叉编译链中,有个demo(位于/usr/share/doc/gcc-arm-none-eabi/examples),有介绍到,需要用到startup.s,以及设置相关的脚本设定相关的ram和flash大小,以及起始地址,查阅了一些资料,起始地址有映射到0x0000 0000(原来的0x0800 0000还是可以读取和写入的,也就是说,如果是flash启动的时候,往0x0000 0000写和往0x0800 0000,效果是一样的),程序的刚开始并不就是大概是一些什么鬼中断向量(后续在看看吧),并不是直接就是main函数开始,如果要生成被正确读取执行的代码,需要参考它提供的demo,暂时没折腾来编译参数,向sw4stm32低头了23333...这大概就是菜吧,那就暂时先用sw4stm32

烧录和调试

根据上面介绍的工具,可以通过st-flash(stlink) JFlashLite(Jlink图形化flash编程工具)烧录,烧录的格式是hex或者binary

把程序写入flash的几种方式

Flash起始地址:0x0800 0000

JLink

JFlashLiteExe图形化烧录工具,或者JLinkExec调试过程中使用loadfile命令把文件载入指定地址(0x0800 0000)

JFlashLiteExec

stm32入门学习笔记_第7张图片
JFlash选择芯片型号和Jlink模式

stm32入门学习笔记_第8张图片
软件界面

JLinkExec
载入文件的方式

#JLink后进入命令行模式

$ JLinkExe 
SEGGER J-Link Commander V6.40 (Compiled Oct 26 2018 15:08:38)
DLL version V6.40, compiled Oct 26 2018 15:08:28

Connecting to J-Link via USB...O.K.
Firmware: J-Link ARM-OB STM32 compiled Aug 22 2012 19:52:04
Hardware version: V7.00
S/N: 20090928
License(s): RDI,FlashDL,FlashBP,JFlash,GDB
VTref=3.300V


Type "connect" to establish a target connection, '?' for help
J-Link>

#输入connect连接到JLink

J-Link>connect
Please specify device / core. : STM32F407ZG
Type '?' for selection dialog
Device>
#选择芯片型号,他读取到我的是STM32F407ZG,默认回车

#选择通讯的模式,我的JLinkOB只能用SWD
Please specify target interface:
  J) JTAG (Default)
  S) SWD
  T) cJTAG
TIF>S

#选择速率,默认4000Khz,回车就行了
Specify target interface speed [kHz]. : 4000 kHz
Speed>

#连接成功后一堆信息刷出来,最后提示
Cortex-M4 identified.
J-Link>

#使用loadfile载入文件到stm32
J-Link>loadfile OLED.hex 0x08000000
Downloading file [LED.hex]...
Comparing flash   [100%] Done.
Erasing flash     [100%] Done.
Programming flash [100%] Done.
Verifying flash   [100%] Done.
J-Link: Flash download: Bank 0 @ 0x08000000: 1 range affected (16384 bytes)
J-Link: Flash download: Total time needed: 0.414s (Prepare: 0.035s, Compare: 0.002s, Erase: 0.346s, Program: 0.025s, Verify: 0.000s, Restore: 0.004s)
O.K.
#下载成功,复位就可以正常运行了,注意BOOT模式要是从flash开始执行

stlink

st-flash或者stlink-gui提供命令行/图形烧录工具

stlink-gui
需要root权限运行

stm32入门学习笔记_第9张图片
stlink-gui

st-flash

#st-flash --flash=flash大小 write 文件名 起始地址
st-flash --flash=1024k write OLED.ihex 0x08000000

关于烧录

烧录文件的格式可以使用ihex,也就是intel hex的格式,通过arm-none-eabi-objcopy把elf格式的文件转成ihex格式,后缀名是.ihx .hex .ihex都行

格式转换 -O 指定输出文件格式,支持什么格式的--help最后有说明,-I (大写i)可以指定输入文件格式

arm-none-eabi-objcopy -O ihex main main.hex

点灯

2019.1.15:还没学会配置编译脚本,暂时得先用sw4stm32吧,在点灯的过程中,顺便把大致如何创建一个项目记录下来

原理图位置

stm32入门学习笔记_第10张图片
这个LED属于低电平驱动

对应的是PF9和PF10管脚

对应的管脚是PF9PF10
管脚按ABCD...分组,根据参考手册说明,它一组IO口有16个引脚(当然编号是从0-15),刚开始接触stm32,我并不打算从库入手,而是先从寄存器入手了解它的工作和寄存器配置顺序,所以使用官方库的头文件进行翻阅,对于一些寄存器的宏定义,还是需要自己慢慢搜索官方的库,同时配合一些视频教程进行学习,毕竟寄存器太多了,自己闭门查手册无疑增加了学习难度

emm...咋这么多寄存器,脑壳痛

//四个配置相关的,这些寄存都是32位的
GPIOx_MODER
GPIOx_OTYPER
GPIOx_OSPEEDR
GPIOx_PUPDR

//两个数据寄存器
GPIOx_IDR
GPIOx_ODR

//一个复位寄存器
GPIOx_BSRR

//两个复用功能选择寄存器,看名字应该是高低4字节的
GPIOx_AFRH
GPIOx_AFRL

//锁定寄存器,冻结IO配置的
GPIOx_LCKR

在开始之前,首先确定我的IO口需要什么类型的输出

GPIO口的套路基本都一样,输出的话有:
开漏输出:可以理解为1的时候是断路(阻值很高),0的时候是接地gnd,也只是只输出低电平
推挽输出:1的时候输出高电平,0的时候输出低电平,也就是高低电平都可以输出
他还有个功能就是可以输出的时候,有带上拉/下拉电阻,刚开始点灯,就不管这个啦

stm32入门学习笔记_第11张图片
GPIO口输出的两mos以及上下拉示意

我这个led是低电平驱动的,开漏输出满足我的需求,那么,我只需要开漏输出就行了
那么配置的GPIO寄存器相关的有
GPIOF_MODER 端口模式寄存器

stm32入门学习笔记_第12张图片
MODER寄存器

GPIOF_OTYPER 输出类型寄存器


stm32入门学习笔记_第13张图片
OTYPER

stm32入门学习笔记_第14张图片
OTYPER

GPIOF_ODR 输出数据寄存器


stm32入门学习笔记_第15张图片
ODR

那么对于PF9口
MODER要设置我通用输出状态:[1:0]=0x01
OTYPER要设置成0x1
ODR要设置成0x0

sw4stm32创建一个工程

1.File>New>Project>C/C++分支下的C Project

stm32入门学习笔记_第16张图片
项目类型

2.填写项目名称和选择项目编译链

stm32入门学习笔记_第17张图片
image.png

3.因为没有什项目相关的要配置,advanced settings在创建项目后也能配置

stm32入门学习笔记_第18张图片
image.png

4.选择CPU型号,因为他要设定特定的宏,以及不同的内存大小和起始地址(flash和ram的),在标准库中,包含stm32f4xx.h头文件需要指定不同的cpu型号有对应分类的宏

stm32入门学习笔记_第19张图片
设定cpu类型

5.开发方式的选择(库选择)

stm32入门学习笔记_第20张图片
选择库

6.项目界面

stm32入门学习笔记_第21张图片
项目界面

他已经提供好基本的模板了,那么就开始吧

在手册中有说到,cm4刚上电的时候,外设时钟是关闭的,ram和flash是打开的,所以有用到gpio.还得吧他的时钟使能打开,通过查头文件和手册,找到这样的一个结构体

//1632行
typedef struct
{
  __IO uint32_t CR;            /*!< RCC clock control register,                                  Address offset: 0x00 */
  __IO uint32_t PLLCFGR;       /*!< RCC PLL configuration register,                              Address offset: 0x04 */
  __IO uint32_t CFGR;          /*!< RCC clock configuration register,                            Address offset: 0x08 */
  __IO uint32_t CIR;           /*!< RCC clock interrupt register,                                Address offset: 0x0C */
  __IO uint32_t AHB1RSTR;      /*!< RCC AHB1 peripheral reset register,                          Address offset: 0x10 */
  __IO uint32_t AHB2RSTR;      /*!< RCC AHB2 peripheral reset register,                          Address offset: 0x14 */
  __IO uint32_t AHB3RSTR;      /*!< RCC AHB3 peripheral reset register,                          Address offset: 0x18 */
  uint32_t      RESERVED0;     /*!< Reserved, 0x1C                                                                    */
  __IO uint32_t APB1RSTR;      /*!< RCC APB1 peripheral reset register,                          Address offset: 0x20 */
  __IO uint32_t APB2RSTR;      /*!< RCC APB2 peripheral reset register,                          Address offset: 0x24 */
  uint32_t      RESERVED1[2];  /*!< Reserved, 0x28-0x2C                                                               */
  __IO uint32_t AHB1ENR;       /*!< RCC AHB1 peripheral clock register,                          Address offset: 0x30 */
  __IO uint32_t AHB2ENR;       /*!< RCC AHB2 peripheral clock register,                          Address offset: 0x34 */
  __IO uint32_t AHB3ENR;       /*!< RCC AHB3 peripheral clock register,                          Address offset: 0x38 */
  uint32_t      RESERVED2;     /*!< Reserved, 0x3C                                                                    */
  __IO uint32_t APB1ENR;       /*!< RCC APB1 peripheral clock enable register,                   Address offset: 0x40 */
  __IO uint32_t APB2ENR;       /*!< RCC APB2 peripheral clock enable register,                   Address offset: 0x44 */
  uint32_t      RESERVED3[2];  /*!< Reserved, 0x48-0x4C                                                               */
  __IO uint32_t AHB1LPENR;     /*!< RCC AHB1 peripheral clock enable in low power mode register, Address offset: 0x50 */
  __IO uint32_t AHB2LPENR;     /*!< RCC AHB2 peripheral clock enable in low power mode register, Address offset: 0x54 */
  __IO uint32_t AHB3LPENR;     /*!< RCC AHB3 peripheral clock enable in low power mode register, Address offset: 0x58 */
  uint32_t      RESERVED4;     /*!< Reserved, 0x5C                                                                    */
  __IO uint32_t APB1LPENR;     /*!< RCC APB1 peripheral clock enable in low power mode register, Address offset: 0x60 */
  __IO uint32_t APB2LPENR;     /*!< RCC APB2 peripheral clock enable in low power mode register, Address offset: 0x64 */
  uint32_t      RESERVED5[2];  /*!< Reserved, 0x68-0x6C                                                               */
  __IO uint32_t BDCR;          /*!< RCC Backup domain control register,                          Address offset: 0x70 */
  __IO uint32_t CSR;           /*!< RCC clock control & status register,                         Address offset: 0x74 */
  uint32_t      RESERVED6[2];  /*!< Reserved, 0x78-0x7C                                                               */
  __IO uint32_t SSCGR;         /*!< RCC spread spectrum clock generation register,               Address offset: 0x80 */
  __IO uint32_t PLLI2SCFGR;    /*!< RCC PLLI2S configuration register,                           Address offset: 0x84 */
  __IO uint32_t PLLSAICFGR;    /*!< RCC PLLSAI configuration register,                           Address offset: 0x88 */
  __IO uint32_t DCKCFGR;       /*!< RCC Dedicated Clocks configuration register,                 Address offset: 0x8C */
  __IO uint32_t CKGATENR;      /*!< RCC Clocks Gated Enable Register,                            Address offset: 0x90 */ /* Only for STM32F412xG, STM32413_423xx and STM32F446xx devices */
  __IO uint32_t DCKCFGR2;      /*!< RCC Dedicated Clocks configuration register 2,               Address offset: 0x94 */ /* Only for STM32F410xx, STM32F412xG, STM32413_423xx and STM32F446xx devices */

} RCC_TypeDef;
//同时RCC是这样被定义的
#define RCC                 ((RCC_TypeDef *) RCC_BASE)

再在手册中找到,RCC_AHB1ENR中有对GPIOF时钟使能相关的配置

stm32入门学习笔记_第22张图片
GPIOFEN

stm32入门学习笔记_第23张图片
GPIOFEN配置信息

那么到此为止,所需要的寄存器就全部找完了

#include "stm32f4xx.h"
        
int main(void)
{
    //外设的时钟打开
    RCC->AHB1ENR |= (0x01 << 5);
    //PF9低电平的时候驱动LED
    //设置0x01是通用输出状态
    //擦除//设置为0x01 通用输出
    GPIOF->MODER &= ~(0x03 << (2 * 9));
    GPIOF->MODER |= (0x01 << (2 * 9));
    GPIOF->ODR &= ~(0x01 << 9);
    for(;;);
}

编译项目生成elf和bin文件

stm32入门学习笔记_第24张图片
生成elf文件和binary

通过JFlashLite将bin文件烧录

顺利点亮

stm32入门学习笔记_第25张图片
led

调试

在项目Debug目录下,如下

$ls
LED2.bin  LED2.elf  makefile  objects.list  objects.mk  output.map  sources.mk  src  startup  StdPeriph_Driver

LED2.elf是elf格式的文件,包含调试符号,LED2.bin是烧录到stm32f4的flash里的程序,已经是机器码来的,我们要用arm-none-eabi-gdb进行硬件调试,首先,确保gdb-arm-none-eabi软件包已经安装

sudo aptitude install gdb-arm-none-eabi

1.启动JLinkGDBServer

新开一个终端窗口启动JLink提供图形化的GDBServer工具

$JLinkGDBServerExe
stm32入门学习笔记_第26张图片
gdbserver配置

stm32入门学习笔记_第27张图片
启动成功,等待gdb连接
#现在可以启动gdb了,使用target remote,上图中监听的端口是2331
$arm-none-eabi-gdb 
(gdb) target remote :2331
Remote debugging using :2331
warning: No executable has been specified and target does not support
determining executable automatically.  Try using the "file" command.
0x00000000 in ?? ()
(gdb) 
#因为此时并没有载入elf文件,没有符号,所以gdb并不知道它现在的状态,用load载入elf文件

(gdb) load LED2.elf 
Loading section .isr_vector, size 0x1a8 lma 0x8000000
Loading section .text, size 0x3b4 lma 0x80001a8
Loading section .rodata, size 0x4 lma 0x800055c
Loading section .init_array, size 0x8 lma 0x8000560
Loading section .fini_array, size 0x4 lma 0x8000568
Loading section .data, size 0x42c lma 0x800056c
Start address 0x800038c, load size 2456
Transfer rate: 2398 KB/sec, 409 bytes/write.
(gdb) 
#载入成功后就可以进行调试了


未完...

你可能感兴趣的:(stm32入门学习笔记)