OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 4907|回复: 34

F103精英板学习笔记

[复制链接]

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
发表于 2018-8-12 22:17:08 | 显示全部楼层 |阅读模式
第一次记录学习的笔记,以前在查询各种资料的时候都可以看到很多大神的笔记,不管是C语言,51,还是32。看笔记的同时不仅解决了自己的问题,同时也激发了写笔记的兴趣。本人刚刚开始学习32开发板,以前仅仅学习过一段时间的51。前一段买了一块F1的板子来学习,写这个笔记的目的不仅仅是为了记录自己的学习过程,也是对自己的一个鞭策。
由于本人也是刚刚开始学习,文中有错误的地方希望大家指正,同时欢迎大家一起讨论交流,共同提升。


正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
 楼主| 发表于 2018-8-12 22:20:10 | 显示全部楼层
本帖最后由 新乡吴彦祖 于 2018-8-12 22:21 编辑

由于刚开始的几章没有进行笔记的记录,笔记是从按键扫描电路章节开始,本笔记是结合F1开发板的教学视频,开发指南,以及资料中所包含的例程,以及查询的各种资料。加以摘录总结,并在其中加入了自己的理解,希望可以给大家带来帮助
回复 支持 反对

使用道具 举报

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
 楼主| 发表于 2018-8-12 22:21:39 | 显示全部楼层
1.按键扫描电路:
1)        支持连续按 低电平持续有效 (类似于换台按住不松 一直换台)
2)        不支持连续按 低电平只有第一次有效 (实际记录的是上一次按下按键的电平,即上一次是松开的,那么按下就是有效的,上一次是按下的下一次检测到的时候就是无效的)
3)        无论哪一种模式都需要记得写一条延时函数防抖
4)        不支持连续按的模式下我们需要定义一个带有static(下面有详细介绍)关键字的变量 static u8 key_up=1代表前一次按键是松开的;而=0则是按下的。由于加了static在第一次调用函数时执行一次该语句,以后再调用时这条语句不会再被执行;当确实按下语句被执行后我们将Key_up置0;那么以后即使检测到了按键按下,由于Key_up被置0那么就不会进入按下的if语句中,从而达到不支持连续按的效果;当我们松手再按下时由于函数体中最后一个if语句中将key_up置为了1相当于又回到了上述的情况。
5)        结合上述两种模式我们可以把两种模式合二为一,其中mode指模式选择mode=1为支持连续按;mode=0为不支持连续按;在函数体中加入if(mode==1) key_up=1;这条判断语句。每次执行一次都要将key_up置1相当于在这个函数中key_up没有起到任何作用,函数就简化为了支持连续按模式。
2. C语言关键字:static
1)  在修饰变量的时候,static修饰的静态局部变量只执行一次,而且延长了局部变量的生命周期,也就是值可以一直保留下来,直到程序运行结束以后才释放。
2)  static修饰全局变量的时候,这个全局变量只能在本文件中访问,不能在其它文件中访问,即便是extern外部声明也不可以。
3)  static修饰一个函数,则这个函数的只能在本文件中调用,不能被其他文件调用。Static修饰的局部变量存放在全局数据区的静态变量区。初始化的时候自动初始化为0。
以下是一个加了static 两个函数的对比
3.对上拉输入和下拉输入的理解
上拉输入可以在没按下的时候检测到高电平而当KEY0和KEY1按下的时候由于接地会检测到低电平信号从而区分,同理可以理解WK_UP的下拉输入。
4.KEY_scan函数设置一个入口参数mode来选择模式。
5.PEin()映射进行位操作,可以go to Definition 来查看,我个人目前将其理解是库将某个IO口帮你取了一个通俗易懂的名字,以后操作IO口时方便输入,而不用打很长的一串代码。
回复 支持 反对

使用道具 举报

31

主题

1955

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4522
金钱
4522
注册时间
2018-5-11
在线时间
947 小时
发表于 2018-8-12 22:39:41 | 显示全部楼层
新乡吴彦祖 发表于 2018-8-12 22:21
1.按键扫描电路:
1)        支持连续按 低电平持续有效 (类似于换台按住不松 一直换台)
2)        不支持连续按 低电平 ...

不错,记笔记是一个督促自己学习的好方法,
希望楼主继续下去。
感觉“由于加了static在第一次调用函数时执行一次该语句,以后再调用时这条语句不会再被执行”这句楼主的理解有偏差, 那一句不是一条真正的程序语句,只是变量的初始化,应该是在编译的时候就分配好了一个空间并赋初值,并不是第一次调用函数时执行的。
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2018-8-13 01:20:53 | 显示全部楼层
不错,建议多**坛。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
 楼主| 发表于 2018-8-13 09:13:33 | 显示全部楼层
warship 发表于 2018-8-12 22:39
不错,记笔记是一个督促自己学习的好方法,
希望楼主继续下去。
感觉“由于加了static在第一次调用函数 ...

谢谢!
回复 支持 反对

使用道具 举报

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
 楼主| 发表于 2018-8-13 09:15:46 | 显示全部楼层
warship 发表于 2018-8-12 22:39
不错,记笔记是一个督促自己学习的好方法,
希望楼主继续下去。
感觉“由于加了static在第一次调用函数 ...

谢谢!
回复 支持 反对

使用道具 举报

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
 楼主| 发表于 2018-8-13 09:21:46 | 显示全部楼层
以下是一个加了static 两个函数的对比图 昨晚写了这句话在文中忘记了添加图片
QQ图片20180813091812.jpg
QQ图片20180813091840.png
回复 支持 反对

使用道具 举报

57

主题

1680

帖子

3

精华

资深版主

Rank: 8Rank: 8

积分
4307
金钱
4307
注册时间
2018-6-30
在线时间
808 小时
发表于 2018-8-13 10:33:45 | 显示全部楼层
本帖最后由 1208 于 2018-8-14 12:29 编辑

楼主,static 两个函数的对比图的结果也可以分析因果来
[size=13.3333px]第一个if的条件key_up=1执行完后,第二个key_up=0可以给key_up赋值,因为静态变量在修饰变量的时候,只执行一次,接下来key_up=0

业精于勤荒于嬉;行成于思毁于随!
回复 支持 反对

使用道具 举报

20

主题

227

帖子

0

精华

高级会员

Rank: 4

积分
605
金钱
605
注册时间
2017-7-14
在线时间
116 小时
发表于 2018-8-13 10:43:53 | 显示全部楼层
加油            
回复 支持 反对

使用道具 举报

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
 楼主| 发表于 2018-8-13 14:52:27 | 显示全部楼层
18-1C语言复习
按位与& :只要有0 结果即为0
按位或| 只要要有1 结果即为1
按位取反~ :对每一位取反0->1,1->0
()<<(>>) :可以直接按照字面意思理解 最高位移出去最低位补0
按位异或 ^ : 异:代表不一样,不一样时按照或运算结果为1;则剩下两者一样的情况时就不按照或运算,规定结果为0.



“#define 标识符字符串”:用于宏定义例:#define SYSCLK_FREQ_72MHz 72000000 定义标识符SYSCLK_FREQ_72MHz的值为72000000,之后出现的SYSCLK_FREQ_72MHz将被替代为所定义的标记72000000。
这样做的好处是在源码中不用写很多看不懂的数字,从而让程序可读性更高,而且更重要的一点当数值需要更改时,我们只需要改变宏定义中的数值,程序中的数值随之更改不需要一一更改所有的数值。


条件编译是根据实际定义宏(某类条件)进行代码静态编译的手段。可根据表达式的值或某个特定宏是否被定义来确定编译条件。
#define           定义一个预处理宏
#undef           取消宏的定义
#if                  编译预处理中的条件命令,相当于C语法中的if语句
#ifdef             判断某个宏是否被定义,若已定义,执行随后的语句
#ifndef           #ifdef相反,判断某个宏是否未被定义
#elif               #if, #ifdef, #ifndef或前面的#elif条件不满足,则执行#elif之后的语句,相当于C语法else-if
#else             #if, #ifdef, #ifndef对应, 若这些条件不满足,则执行#else之后的语句,相当于C语法中的else
#endif            #if, #ifdef, #ifndef这些条件命令的结束标志.
defined         与#if, #elif配合使用,判断某个宏是否被定义

最常见的条件编译是防止重复包含头文件的宏,形式跟下面代码类似:

[mw_shl_code=c,true]#ifndef ABCD_H
2 #define ABCD_H
3
4 // ... some declaration codes
5
6 #endif // #ifndef ABCD_H
[/mw_shl_code]




# ifdef  #ifndef 等用法(转)
  头件的中的#ifndef,这是一个很关键的东西。比如你有两个C文件,这两个C文件都include了同一个头文件。而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。
还是把头文件的内容都放在#ifndef和#endif中吧。不管你的头文件会不会被多个文件引用,你都要加上这个。一般格式是这样的:

[mw_shl_code=applescript,true]#ifndef <标识>
#define <标识>
......
......
#endif
[/mw_shl_code]

<标识>在理论上来说可以是自由命名的,但每个头文件的这个标识都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:stdio.h
[mw_shl_code=applescript,true]#ifndef _STDIO_H_
#define _STDIO_H_
......
#endif
[/mw_shl_code]



我在打开了几个源码之后发现了确实是这样的写的,而且至少main.ckey.c中都有#include "sys.h",但是当我把按键输入实验中ifndefdefine以及endif都注释掉以后程序仍然可以编译,当时认为是否是因为没有使用这个sys.h中的函数,
结果在sys.h源码中找到了定义io口映射的PXin(n)位操作语句,在思考了好久之后我才发现应该是我理解错意思了,上面说两个.c文件都include同一个头文件的意思应该是include你所写的这个文件所形成的.h,并不是你写的头文件的其中引用的文件。
然而当我以为我解决了问题之后,突然看到了main.cled.c都包含了led.h文件,我又试着把led.h的文件中几行条件编译语句注释掉,发现竟然同样可通过编译,这个问题直到最后我也没有想明白

extern变量声明
     C语言中extern可以置于变量或者函数前,以表变量或函数的定义在别的文件中。提示编译器遇到此变量和函数时在其他模块中寻找其定义,视频中例子调用test()函数由于在test.c中需要用到id但没有定义,
并且C规定了不可以多次定义。所以在test.c中加入extern.(串口实验)

typedef类型别名 还是相当于顶层头文件为你取好了一个简单的名字,用时直接使用即可。

结构体
       结构体的好处就是程序看起来规整,并减少调用函数的入口参数。例如调用GPIO_Init只需要提前将结构体设置好mode,pin,speed。至于为什么设置完结构体需要调用GPIO_Init这里我理解为每次设置好结构体都要调用初始化,
让我们设置的东西起到作用。并且结构体扩展性很强,指的是如果我们不使用结构体不仅仅我们要修改函数内部还要修改函数的定义与声明,并且在所有调用该函数的地方都进行相应的修改,工作量非常大。相反的如果使用结构体,
外部加入多少参数我们只需要修改所调用函数内部与之相对应即可。GPIO_InitTypeDef就是系统写好的一个结构体go to definition可以看到里面包含了pin,mode,speed



回复 支持 反对

使用道具 举报

9

主题

796

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
2038
金钱
2038
注册时间
2017-8-2
在线时间
522 小时
发表于 2018-8-13 14:54:14 | 显示全部楼层
加油!
回复 支持 反对

使用道具 举报

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
 楼主| 发表于 2018-8-13 21:44:18 | 显示全部楼层
MDK中寄存器地址名称映射分析(开发指南4.6节)
对MCU,一切底层配置,最终都是配置寄存器
库函数是对底层寄存器封装了一遍,位操作也是最终操作的寄存器





51中映射的方法:sfr P0=0x80;//P0映射到地址0x80 ,以后只需要直接写P0=0x00即代表往寄存器地址0x80所代表的寄存器赋值,0x00相当于给P0的每一位IO口都赋值0。
这样的好处是不需要记住每个寄存器的地址,需要用对应的寄存器时直接写寄存器相应的名称即可。最常用的P0、P1、P2、P3,四组输入输出(I/O)寄存器,以及TH1, TH0, TL1,TL0,SCON,TCON,TMOD,SBUF等等





相比于51,32的寄存器要多得多,32采用了结构体来表示相应的寄存器
GPIOA->ODR=0x00000000。32中每组IO口都有7个寄存器控制这与51稍有不同。每组IO对应一个基地址,寄存器根据基地址进行偏移取的地址,GPIOA的基地址又是根据APB2的地址来偏移,同理得到其他地址。
其中外设基地址是一个比较高的地址,他被定义为一个常量,作为参考。再回头说结构体,我们打开stm32f10x.h 定位到 GPIO_TypeDef 定义处:GPIOA被强制转换为结构体指针,就意味着接下来的地址代表了结构体中的七个寄存器。
[mw_shl_code=c,true]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;[/mw_shl_code]
然后我们找到#define  GPIOA  ((GPIO_TypeDef *) GPIOA_BASE)。这句话的意思是GPIOA 将 GPIOA_BASE 强制转换为 GPIO_TypeDef 指针,GPIOA 指向地址 GPIOA_BASE,GPIOA_BASE 存放的数据类型为 GPIO_TypeDef,然后再找到#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800),可以看出GPIOA_BASE就是GPIO基地址的意思。总的来说意思就是访问GPIOA相当于访问了与之对应的结构体,那么32是如何找到所对应的寄存器的?
由于每个寄存器是32位,每个地址设置八位(类似于51),并且结构体存储的成员他们的地址是连续的。所以GPIOA下的寄存器相对应的寄存器之间的地址都是相差了4,例如我们访问ODR寄存器就是访问了ODR相对于GPI_的基地址的偏差的四个地址。


回复 支持 反对

使用道具 举报

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
 楼主| 发表于 2018-8-15 12:53:09 | 显示全部楼层
19时钟系统
时钟源:
HSI High Speed Internal高速内部时钟  内置RC振荡器 产生约8MHz
HSE 高速外部时钟 主要外接晶振产生 8MHz(4-16MHZ均可产生)
LSI   低速内部时钟 RC振荡器 40KHz 。独立看门狗的时钟源,也可作为RTC的时钟源
LSE 低速外部部时钟 32.768kHz 的石英晶体,主要是 RTC(实时时钟)的时钟源
PLL —为锁相环倍频输出,其时钟输入源可选择为 HSI/2、HSE 或者 HSE/2(开发指南时钟系统图),由选择器1,2选择所需要的。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。(就是把进来的频率乘以一个系数,得到你要的频率)

系统时钟:1、由HSE直接产生2、由HIS直接产生3、来源于PLL;(系统时钟里还有一个叫CSS的时钟监视系统,一旦HSE失效,自动切换置HSI
RTC时钟:来源于LSELSIHSE128分频
独立看门狗时钟:来源于LSI
MCO:内部输出的一个引脚(PA8)用来输出内部时钟。可以输出系统时钟SYSCLKHISHSEPLLCLK/2
USB时钟:STM32 中有一个全速功能的 USB模块(48MHz),其串行接口引擎需要一个频率为 48MHz 的时钟源。该时钟源只能从 PLL 输出端获取,可以选择为 1.5 分频或者 1 分频当需要使用USB模块时,PLL 必须使能,并且时钟频率配置为 48MHz 或 72MHz,当PLL设置为48,采用一分频,当PLL设置为72,采用1.5分频。

其他所有外设的时钟最终来源都是 SYSCLKSYSCLK 通过 AHB 分频器分频后送给各模块使用。
1HCLK up to 72MHz)供AHB 总线、内核、内存和 DMA使用
2APB1(低速up to36MHz)总线时钟:分出一路供APB1外设使用(低速up to 36MHz),另一路给定时器Timer2.3.4倍频器使用
3APB2(高速up to72MHz)总线时钟: 分出一路供 APB2 外设使用(PCLK2最大频率 72MHz),另一路送给定时器(Timer)1 倍频器使用
4、直接送给 Cortex 的空闲运行时钟 FCLK
5、通过 8 分频后送给 Cortex 的系统定时器时钟,也就是systick
理解的是 APB1 APB2 的区别,APB1 上面连接的是低速外设,包括电源接口、备份接口、CANUSBI2C1I2C2UART2UART3 等等,APB2 上面连接的是高速外设包 UART1SPI1Timer1ADC1ADC2、所有普通 IO (PA~PE)、第二功能 IO 口等。当需要使用某模块时,记得一定要先使能对应的时钟

回复 支持 反对

使用道具 举报

29

主题

338

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1181
金钱
1181
注册时间
2018-4-13
在线时间
170 小时
发表于 2018-8-15 18:17:35 | 显示全部楼层
加油,共同进步!
回复 支持 反对

使用道具 举报

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
 楼主| 发表于 2018-8-15 22:14:51 | 显示全部楼层
20 SystemInit 系统时钟初始化

void SystemInit (void) 初始化系统时钟 层层寻找
在System_Stm32F10X.c开头看到系统默认设置为了72Mhz(以下代码在1000行左右)
static void SetSysClockTo72(void)设置RCC_RC寄存器,先让HSEON位置1,即打开,然后等待其稳定(do,while语句)判断HESRDY就绪位是否为1

/* HCLK = SYSCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;  AHB预分频,不分频,使得HCLK=72MHz,AHB总线频率
      
/* PCLK2 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; HCLK不分频,高速。HCLK是一个数这个数经过APB1,或APB2分频器进行分频,就产生了APB2总线的频率
   
/* PCLK1 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; HCLK两分频,低速.就产生了APB1总线的频率

/*  PLL configuration: PLLCLK = HSE * 9 = 72 MHz */  查看其他频率其他都一样,这个地方不一样导致设置成了不同的频率
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
                                        RCC_CFGR_PLLMULL));
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9); HSE经倍频产生72MHz的PLL

/* Enable PLL */ 打开PLL
    RCC->CR |= RCC_CR_PLLON;

    /* Wait till PLL is ready */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }
   
    /* Select PLL as system clock source */  // 把PLL作为系统时钟
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;    //go to definition 看到设置到CFGR 1:0位为10 这两位的作用就是选择系统时钟的来源于PLL


/* Wait till PLL is used as system clock source */ 等待完成


总结 先打开HSE 再设置 AHB APB1 APB2的分频系数确定 再确定 PLL 倍频系数 再将PLL作为系统时钟

其中  uint32_t SystemCoreClock         = SYSCLK_FREQ_72MHz;        /*!< System Clock Frequency (Core Clock) */ 可以来获取当前你所设置的时钟频率

我们的主函数从来没写过始终初始化函数,但是由于再startup启动文件中,有一段代码含义为每次在运行著函数前先运行初始化时钟。
回复 支持 反对

使用道具 举报

31

主题

1955

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4522
金钱
4522
注册时间
2018-5-11
在线时间
947 小时
发表于 2018-8-16 15:29:56 来自手机 | 显示全部楼层
包含头文件,也就包含了该头文件中包含的头文件
回复 支持 反对

使用道具 举报

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
 楼主| 发表于 2018-8-16 15:32:01 | 显示全部楼层
本帖最后由 新乡吴彦祖 于 2018-8-16 15:35 编辑

Systick定时器(内核级别)cortex M3 权威指南

一个24位的倒计数定时器,记到0,将自动从RELOAD寄存器中自动重装载定时初值。常用来做延时,实时系统的心跳时钟。一般在STM32+UCOS系统中都采用SystickUCOS心跳时钟

四个Systick寄存器

CTRL Systick控制和状态寄存器
名称 类型 复位值描述
16
COUNTFLAG
R
     0
  如果在上次读取本寄存器后,SysTick 已经数到了
    0,则该位为 1。如果读取该位,该位将自动清零
2
CLKSOURCE
R/W
0
  0=外部时钟源(STCLK) AHB总线的1/8
1=内核时钟(FCLK) HCLK时钟
(SysTick_CLKSourceConfig();)
1
TICKINT
R/W
     0
       1=SysTick 倒数到 0 时产生 SysTick 异常请求
       0=数到0时无动作(用于决定是否产生中断)
0
ENABLE
R/W     0
SysTick
定时器的使能位


LOAD Systick 自动重装载初值寄存器 这里记录着要装载的初值

VAL Systick      当前值寄存器,从这里不断减1,减到0,从LOAD寄存器重装初值
                      读会返回当前倒计数的值,写会使之清0,同时清除COUNTFLAG标志

CALIB Systick  校准值寄存器


回复 支持 反对

使用道具 举报

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
 楼主| 发表于 2018-8-17 21:53:39 | 显示全部楼层
23 端口复用 重映射

内置外设 串口,ADC,DAC,外部引脚和GPIO是复用的,当这个GPIO作为内置外设使用的时候,就叫做复用。

使用的时候先打开GPIO时钟,再打开外设时钟,初始化IO为对应的模式,根据中文参考手册。8.1.11

重映射 1,使能GPIO时钟(重映射后的IO)
           2,使能功能外设时钟
           3,使能AFIO时钟(在对寄存器AFIO_MAPR配置复用功能重映射,AFIO_EXTICRX配置外部中断线映射,AFIO_EVCR配置EVENTOUT事件输出)在操作这三个寄存器时必须打开AFIO时钟  
           4,开启重映射
回复 支持 反对

使用道具 举报

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
 楼主| 发表于 2018-8-20 18:20:18 | 显示全部楼层
NVIC中断优先级 misc.c
1、对STM32进行分组SCB->AIRCR中配置 108配置分组配置好分组后在IP寄存器74的四位就会自动规定有几位抢占优先级和响应优先级。例如2 101  22 两位抢占优先级,两位响应优先级
抢占优先级,响应优先级(数值设置的越小,优先级越高。)
抢占优先级高的可以打断正在进行的低抢占优先级,抢占优先级相同,高响应优先级不可以打断低响应优先级,但当两个中断同时发生,那么先执行响应优先级高的,若抢占、响应优先级均相同,看那个中断先发生就顺序执行。
中断优先级分组函数
void NVIC_PriorityGroupConfig(uint32_tNVIC_PriorityGroup)
void NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2)
2、设置单个中断的抢占优先级和响应优先级
中断优先级控制寄存器组:IP[240] 240个八位寄存器每个中断使用一个寄存器来确定优先级。F1精英有60个可屏蔽中断,使用IP[59]- IP[0] 并且每个IP寄存器用来设置抢占和响应优先级只有高四位,低四位没有用到 void NVIC_Init(NVIC_InitTypedDef*NVIC_InitStruct)
3、中断使能寄存器组 ISER[8] 32位寄存器,每个位控制一个中断的使能,ISER[0]bit0~bit31对应中断0~31.ISER[1]bit0~27对应中断32~59  





例子:

void NVIC_Init(NVIC_InitYTypeDef* NVIC_InitStruct)

typedef struct{
uint8_t NVIC_IRQchannel;设置好中断通道
uint8_t NVIC_IRQchannelPreemptionPriority;//设置抢占优先级
uint8_t NVIC_IRQchannelSubProprity;//响应优先级
FunctionalState NVIC_IRQchannelCmd; // 使能
}NVIC_InitTypeDef; // 系统内置结构体

NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQchannel=USART1_IQRn;//串口1中断
NVIC_InitStructure.NVIC_IRQchannelPreemptionPriority=1
NVIC_InitStructure.NVIC_IRQchannelSubProprity=2;
NVIC_InitStructure.NVIC_IRQchannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);//根据上面指定的参数初始化NVIC寄存器



回复 支持 反对

使用道具 举报

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
 楼主| 发表于 2018-8-24 20:46:40 | 显示全部楼层
25-27.串口通讯原理 (开发指南5.3usart串口文件夹介绍,第九章串口实验)
                                   (中文参考手册25章通用同步异步收发器USART
ALIENTEK 精英 STM32 开发板所使用的STM32F103ZET6 最多可提供 5 路串口,有分数波特率发生器、支持同步单线通信和半双工单线通讯、支持 LIN、支持调制解调器操作、智能卡协议和 IrDA SIR ENDEC 规范、具有 DMA

并行通信:数据各个位同时传输,快,占用引脚多。
串行通信:数据按位顺序传输,慢,占用引脚少。
  串行通信:
    单工:发——>单向通道——>收
    半双工:通道是双向的,但在某个时刻只能发或者收。
    全双工:特点是发的同时还可以接收。

同步通信 : SPI,I2C,伴随着时钟信号
异步通信 :USART(通用同步异步收发器),单总线,双方事先约定好波特率

常用串行通信接口:
通信标准
引脚说明
通信方式
通信方向
UART
TXD发送端,RXD接收端,GND共地
异步
全双工
单总线 1—wire
DQ(发送/接收端)
异步
半双工
SPI
SCK:同步时钟,MISO:主机输入,从机输出,MOSI:从机输入主机输出
同步
半双工
I2C
SCL:同步时钟,SDA:数据输入,输出端
同步
半双工

STM32串口通信5个
RXD TXD
PA10 PA9
PA3 PA2
PB11 PB10
PC11 PC10
PD2 PC12

通信过程
外部设备发送数据—>串行输入移位寄存器—>将数据按寄存器位号一次传给输入数据缓冲器—>MCU内核

串口配置的一般步骤
1、串口时钟使能,GPIO时钟使能 RCC——APB2PeriphClockCmd();
2、串口复位 不必须
3、GPIO端口模式设置:GPIO_Init(); 8.1.11外设的GPIO配置
4、串口参数初始化:USART_Init();
5、开启中断并且初始化NVIC,需要中断才进行这一步的配置NVIC_Init(); USART_Cmd();
6、使能串口,USART_Cmd;
7、编写中断处理函数:USARTx_IRQHander();
8、串口数据收发:void USART_SendData(); uint16_t USART_ReceiveDate();
9、串口传输状态获取: FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx,uint16_t USART_FLAG); void USART_ClearITPendingBit(USART_TypeDef* USARTx,uint16_t USART_IT);
如果使用中断的话,一般还要在主函数靠前一些位置,写NVIC_PriorityGroupConfig(NVIC_PriorityGroup_x); //设置中断优先级分组
void USART1_IRQHandler(void) 中断服务函数,名字是定义好的,不能随便取名



回复 支持 反对

使用道具 举报

14

主题

58

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
441
金钱
441
注册时间
2018-7-9
在线时间
56 小时
发表于 2018-8-27 18:26:12 | 显示全部楼层
新乡吴彦祖 发表于 2018-8-24 20:46
25-27.串口通讯原理 (开发指南5.3usart串口文件夹介绍,第九章串口实验)                                ...

写的不错,向你学习,&#17403;一起交流学习吗
回复 支持 反对

使用道具 举报

1

主题

37

帖子

0

精华

高级会员

Rank: 4

积分
592
金钱
592
注册时间
2017-6-15
在线时间
75 小时
发表于 2018-8-28 09:18:39 | 显示全部楼层
鼓励。
回复 支持 反对

使用道具 举报

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
 楼主| 发表于 2018-8-29 18:03:37 | 显示全部楼层
克里斯丁 发表于 2018-8-27 18:26
写的不错,向你学习,&#17403;一起交流学习吗

可以啊
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2018-8-30 01:27:52 | 显示全部楼层
新乡吴彦祖 发表于 2018-8-24 20:46
25-27.串口通讯原理 (开发指南5.3usart串口文件夹介绍,第九章串口实验)                                ...

加油
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
 楼主| 发表于 2018-8-30 11:38:15 | 显示全部楼层
28、外部中断实验:(开发指南第十章、中文手册第九章中断和事件)

1、STM32的每个IO口都可以作为外部中断输入。
2、F1支持19个外部中断/事件请求:
线0-15 对应外部IO的输入中断 GPIOx0->EXTI0(x=A、B...G)就相当于线0上的中断  7组IO*每组IO16个=112个IO口
线16:连接PVD输出
线17:连接RCT闹钟事件
线18:连接USB唤醒
每个中断线都可以独立配置触发方式(上升沿,下降沿...)
注意:每一个时间中断线上只能有一个IO口映射,比如PA0和PB0不能同时映射到EXTI0;查看(AFIO_EXTICRx)寄存器就可以看到x取定时每一个EXTI的[3:0]只能设置某个特定的IO口

服务函数 EXTIx_IQRHandler 对应线x中断
              ...9_5_IQR...         对应线9-5中断 这里意味着线5-9会进入同一个中断服务函数
              ...15_10_IQR...     对应线15-10中断

外部中断的一般配置步骤

1.初始化IO口为输入[mw_shl_code=c,true]GPIO_Init(); //假如你要使用按键作为外部中断,而你也写过按键初始化函数就可以直接使用[/mw_shl_code]
2.开启IO口复用时钟 [mw_shl_code=applescript,true]RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE) // 使用到有关于AFIO寄存器的时钟的时候要开启AFIO时钟[/mw_shl_code]  
       跟 AFIO 相关的寄存器有:
  1、 事件控制寄存器(AFIO_EVCR)
  2、 复用重映射和调试I/O 配置寄存器(AFIO_MAPR)
  3、 外部中断配置寄存器1(AFIO_EXTICR1)
  4、 外部中断配置寄存器2(AFIO_EXTICR2)
  5、 外部中断配置寄存器3(AFIO_EXTICR3)
  6、 外部中断配置寄存器4(AFIO_EXTICR4)


3.设置IO口与中断线的映射关系:[mw_shl_code=applescript,true]GPIO_EXTILineConfig();[/mw_shl_code]

4.初始化线上中断,设置触发条件:[mw_shl_code=applescript,true]EXTI_Init(&EXTI_InitStruc);[/mw_shl_code]

5.配置中断分组,使能中断:[mw_shl_code=c,true]NVIC_Init(&NVIC_InitStruc);[/mw_shl_code]

6.编写中断服务函数:[mw_shl_code=applescript,true]void EXTIx_IRQHandler();[/mw_shl_code]

7.清除中断标志位:写在对应的中断服务函数下面[mw_shl_code=applescript,true]EXTI_ClearITPendingBit();[/mw_shl_code]


回复 支持 反对

使用道具 举报

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
 楼主| 发表于 2018-8-30 20:26:16 | 显示全部楼层
固件库与自己的函数之间如何连接的:


1.main中#include "stm32f10x.h"

2.stm32f10x.h中
#ifdef USE_STDPERIPH_DRIVER
  #include "stm32f10x_conf.h"
#endif
3.stm32f10x_conf.h中
#include "stm32f10x_adc.h"
#include "stm32f10x_bkp.h"
#include "stm32f10x_can.h"
#include "stm32f10x_cec.h"
#include "stm32f10x_crc.h"
#include "stm32f10x_dac.h"
#include "stm32f10x_dbgmcu.h"
#include "stm32f10x_dma.h"
#include "stm32f10x_exti.h"
#include "stm32f10x_flash.h"
#include "stm32f10x_fsmc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_i2c.h"
#include "stm32f10x_iwdg.h"
#include "stm32f10x_pwr.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_rtc.h"
#include "stm32f10x_sdio.h"
#include "stm32f10x_spi.h"
#include "stm32f10x_tim.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_wwdg.h"
回复 支持 反对

使用道具 举报

31

主题

1955

帖子

3

精华

论坛元老

Rank: 8Rank: 8

积分
4522
金钱
4522
注册时间
2018-5-11
在线时间
947 小时
发表于 2018-8-30 20:38:19 | 显示全部楼层
新乡吴彦祖 发表于 2018-8-30 20:26
固件库与自己的函数之间如何连接的:

简洁明了
我的开源链接 https://github.com/ShuifaHe/STM32.git  请关注,点赞支持哦。
回复 支持 反对

使用道具 举报

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
 楼主| 发表于 2018-8-30 22:53:18 | 显示全部楼层
看门狗(参考手册17章,开发指南11章):检测和解决由软件错误引起的故障

芯片内置2个看门狗: 1、独立看门狗,IWDG—>LSI驱动 ,特点:独立工作,对时间精度要求低
                                2、窗口看门狗,WWDG—>APB1分频后得到时钟驱动,特点:适合要求看门狗在精确计时窗口起作用的应用程序

看门狗如何解决故障? 在系统正常运行时,系统不复位。而系统跑飞(程序异常执行),系统复位,程序重新执行

独立看门狗功能描述:在键值寄存器(IWDG_KR)中写入0xCCCC,开始启用IWDG,计数器开始从复位值0xFFF递减计数。当计数器计数到末尾0x000会产生IWDG_RESET复位信号。
无论何时在IWDG_KR中写0xAAAA(喂狗),IWDG_RLR都会给计数器重装重载值,避免系统复位,软件必须以一定的间隔写入0xAAAA。正常时不断喂狗,异常时不喂狗,系统复位。
注:WDG处于VDD供电区,停机或待机模式时仍能正常工作。

独立看门狗溢出时间计算:(产生复位的时间)Tout=[(4*2^prer)*rlr]/40     4*2^pre为分频系数   (4*2^prer)/40为分频过后频率的倒数,即为周期。周期再*rlr所装载的值=时间

写看门狗一般步骤:void IWDG_Init(u8 prer,u16 rlr)
{        
          IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); //第一步取消写保护 接下来就要往 PR RLR写东西
         IWDG_SetPrescaler(prer); // 设置预分频系数 从外部传入
         IWDG_SetReload(rlr); // 设置重装载值  从外部传入
         IWDG_ReloadCounter();          //以一定的间隔写入0xAAAA go to definition否则,当计数器为0时,看门狗会产生复位。
         IWDG_Enable(); //使能看门狗函数
         
}
//喂独立看门狗
void IWDG_Feed(void)
{   
         IWDG_ReloadCounter();//reload                                                                                   
}

QQ截图20180830192857.jpg
回复 支持 反对

使用道具 举报

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
 楼主| 发表于 2018-8-30 22:54:39 | 显示全部楼层
本帖最后由 新乡吴彦祖 于 2018-8-31 15:05 编辑

窗口看门狗 WWDG
特点:喂狗有上下限时间。
窗口看门狗有两种情况产生复位:
1.当喂狗的时候如果计数器的值大于某一设定数值W[6:0]时,此设定值在WWDG_CFR寄存器定义。
2.当计数器的数值从0x40减到0x3f时,即T6位跳变到0.
理解:设定值相当于上限,0x3f相当于下限,计数值比上限大时喂狗则满足了1的情况,系统会复位,一直不喂计数器到达下限,满足2系统产生复位。

如果启动了看门狗并且允许中断,当递减计数器等于0x40时,产生早期唤醒中断(EWI)配置寄存器(WWDG_CFR),它可以用于喂狗,以避免WWDG复位。(上下限限制了喂狗时间,所以在计数器0x40时产生中断,在中断内喂狗,可以避免WWDG复位)

超时时间的计算和IWDG类似: Twwdg=Tpclk*4096*2^WDGTB*(T[5:0]+1) 因为pclk频率太高,先要内置分频4096再让用户自己选的分频和IWDG同样道理这里的时间就是上下限之间的时间,当使用了EWI时可以看作是这么多时间的间隔中断服务一次。
回复 支持 反对

使用道具 举报

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
 楼主| 发表于 2018-9-1 09:58:50 | 显示全部楼层
本帖最后由 新乡吴彦祖 于 2018-9-1 15:27 编辑

31.通用定时器

F1含有四个通用定时器,两个高级定时器,两个基本定时器
定时器种类位数 计数器模式 产生DMA请求 捕获比较通道 互补输出 特殊应用场景
高级定时器TIM1,TIM8 16 向上,向下,向上/下 可以  4 死区控制紧急刹车,PWM电机控制
通用定时器TIM2-TIM516 向上,向下,向上/下 可以 4定时计数,PWM输出,输入捕获,输出比较
基本定时器TIM6,TIM716 向上,向下,向上/下 可以 0 驱动DAC



通用定时器:●APB1总线上16位可编程(实时更改)预分频器,含有四个独立通道:1、输入捕获(测量输入信号的脉冲宽度) 2、输出比较  3、PWM生成  4、单脉冲模式输出。

                   ●  可使用外部信号(TIMx_ETR)控制控制定时器和定时器互连的同步电路(用一个定时器控制另一个定时器)

                   ● 如下事件发生时产生中断/DMA:

                     ─  更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
                     ─  触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
                     ─  输入捕获
                  ●    ─  输出比较
                  ●   支持针对定位的增量(正交)编码器和霍尔传感器电路

                  ●  触发输入作为外部时钟或者按周期的电流管理

计数器时钟可由下列时钟源提供:1、内部时钟(CK_INT)(APB1倍频)2、外部时钟模式1:外部输入脚(TIx) 3、外部时钟模式2:外部触发输入(ETR) 4、内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,

                                                如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。


通用定时器工作过程:1、时钟发生器(产生CKPSC时钟)  2、时基单元   3、输入捕获  4、输出比较


通用定时器框图.jpg

回复 支持 反对

使用道具 举报

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
 楼主| 发表于 2018-9-10 23:21:27 | 显示全部楼层

33 PWM输出实验

本帖最后由 新乡吴彦祖 于 2018-9-15 23:09 编辑

33 PWM输出实验
开发指南:14章 PWM输出 ;参考手册 14章 通用定时器
几个比较重要的寄存器和相应的位:
PWM的周期主要由ARR寄存器决定;占空比由CCRx决定
TIMx_CCMR1 输入/比较模式寄存器 中的OC1M设置该位 对应相应的PWM模式1或模式2,同时要设置相应的通道预装载使能
TIMxCNT 计数器的值
比较寄存器 TIMx_CCRx 当前捕获/比较寄存器参与同计数器TIMx_CNT的比较
CCER:CC1P,CC1E 决定那个电平是有效电平
控制寄存器 TIMx_CRx 中的ARPE可以决定是当前重装立即生效还是下一个周期生效

PWM输出配置步骤
1,因为定时器3挂在APB1 所以需要使能定时器3时钟,使相应的IO口实现功能 所以需要使能 GPIO时钟 它挂在ABP2下;RCC_APB1PeriphClockCmd();RCC_APB2PeriphClockCmd();
2,初始化IO作为复用功能 主要修改MODE参数为 GPIO_Mode_AF_PP
3, 如果是被重映射的引脚还要 重映射配置,主要由开启AFIO时钟,以及设置重映射。 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); GPIO_PinRemapConfig();
4,初始化定时器 因为是用定时器计数比较 TIM_TimeBaseInit
5,初始化输出比较参数 TIM_OC2Init
6,使能预装载寄存器 TIM_OC2PreloadConfig
7,使能定时器 TIM_Cmd
8,不断改变比较值CCRX达到不同的占空比 TIM_SetCompare2

我们写函数的时候 前旗步写成一个初始化函数 TIMx_PWM_Init 在我们需要用pwm的地方 用TIM_SetCompare2 达到不同的占空比
回复 支持 反对

使用道具 举报

0

主题

13

帖子

0

精华

新手上路

积分
33
金钱
33
注册时间
2018-9-11
在线时间
6 小时
发表于 2018-9-11 14:30:20 | 显示全部楼层
asdasdasf
回复 支持 反对

使用道具 举报

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
 楼主| 发表于 2018-9-15 23:09:33 | 显示全部楼层
本帖最后由 新乡吴彦祖 于 2018-9-21 00:22 编辑

输入捕获实验输入捕获模式可以用来测量脉冲宽度或者测量频率。STM32的定时器,除了TIM6和TIM7,其他定时器都有输入捕获功能。STM32 的输入捕获,简单的说就是通过检测 TIMx_CHx 上的
边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的通道的捕获/比较寄存器(TIMx_CCRx)里面,完成一次捕获。同时还可以配置
捕获时是否触发中断/DMA 等






工作过程

工作过程


ICPSC:设置预分频系数;其实是设置为几个事件触发一次捕获,设置为01就是两个上升沿捕获一次(假设为上升沿)
fDTS由TIMx_CR1的CKD[1:0]位设置,指的是采样的时间间隔,再配合TIMx_CCMR的 IC1F[3:0]匹配你想要的连续的采样次数,例如N=8就是滤波长度,也是连续采样次数
例如CKD设置位00,那么fDTS=fCK_INT (采样的频率就等于初始信号的频率),相应的TIMx_CCMR的 IC1F[3:0]频率和时间间隔要匹配,假如设置为0011,那么就是以fDTS=fCK_INT
为频率,捕获到上升沿时连续采样到8次通道1的电平如果都是高电平(假设为高电平触发)则说明是一个有效的触发,就会触发输入捕获实验。,这样做的好处是可以滤掉抖动。
CC1S[1:0]:捕获/比较1 选择 (Capture/Compare 1 selection)  位1:0这2位定义通道的方向(输入/输出),及输入脚的选择:主要功能配置映射在IC映射在哪个TI上,看上期大图。


注意:我们提前应该查芯片手册确定IO口对应的引脚是哪个定时器的哪个通道
回复 支持 反对

使用道具 举报

1

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2018-8-12
在线时间
18 小时
 楼主| 发表于 2018-10-12 09:17:27 | 显示全部楼层
本帖最后由 新乡吴彦祖 于 2018-10-12 16:58 编辑

35 电容触摸按键实验

原理:当我们没有按下TPAD的时候,充电时间为T1。按下TPAD,相当于增加了一个电容并联在原本的电路中,电容变大,所以充电时间为T2.我们可以通过检测冲放电时间,来判断是否按下。如果T2-T1大于某个值,(就是指充电时间边长的时候)就可以判断有按键按下。



过程:
TPAD引脚设置为推挽输出,输出0,实现电容放电到0。
②TPAD引脚设置为浮空输入(IO复位后的状态),电容开始充电。
③同时开启TPAD引脚的输入捕获开始捕获。
④等待充电完成(充电到底Vx,检测到上升沿)。
⑤计算充电时间。

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-20 18:17

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表