野火电子论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 15649|回复: 20

每日一课,记录F429学习情况

[复制链接]
发表于 2017-5-31 22:17:12 | 显示全部楼层 |阅读模式
本帖最后由 王维鋆 于 2017-5-31 22:29 编辑

开帖原因:刚入手F429,有开发F1基础,制定目标,每日一课,督促自己踏踏实实的学习,与大家交流学习,共同进步。
学习板  :挑战者STM32F429
起始时间:2017年06月01日
目    标:独立思考解决完成开发板的大部分实验,不求快但求稳
回复

使用道具 举报

发表于 2017-6-1 08:48:51 | 显示全部楼层
嗯,持之以恒
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-6-1 16:11:43 | 显示全部楼层
本帖最后由 王维鋆 于 2017-6-1 22:15 编辑

时间:2017/06/01    14:15      RCC—使用 HSE  HSI 配置时钟梳理

每日一C梳理http://blog.csdn.net/wangweijundeqq/article/details/72824098
解剖RCC框图——时钟树(参考了火哥的使用手册和网上的一些资料,加上自己的理解)
下面是STM32F429的时钟树:

file:///F:/%E7%AC%94%E8%AE%B0%E6%9C%AC/qq8E1F43BBADC74F719786A7AF9EA5A7A7/25d8debab56f41369a9ffc782edaebc2/_8zwgha0%25%60igi6dd%7Dco8c6b.png
理解STM32的时钟是我们应用定时器等等的基础,首先 ,从系统时钟说起。
1.首先注意图中打蓝色圈的两个地方,HSE和HSI分别是外部时钟和内部时钟,其中HSE是高速的外部时钟信号,可接石英/陶瓷谐振器,或者接外部时钟源,HSE也可以直接做为系统时钟或者 PLL 输入(从蓝圈9处可以看出),频率范围为 4MHz~26MHz。HSE 我们使用的25M 的无源晶振所以移植时一定要格外注意。

2.注意看图中蓝圈7的部分,这里是主锁相环倍频输出,用于产生系统需要的高速时钟信号,如图绿色箭头所示(STM32还有一个副锁相环,如蓝圈8图中②锁相环 PLL 的主要作用是对时钟进行倍频.
一般而言,我们都是使用HSE经过PLL 倍频之后作为系统时钟F429系统时钟最高为 180M,这个是官方推荐的最高的稳定时钟,如果你想铤而走险,也可以超频,超频最高能到 216M。至于怎么算出来的,下面请看分析.
主 PLL 时钟的时钟源要先经过一个分频系数为 M 的分频器,然后经过倍频系数为 N 的倍频器出来之后的时候还需要经过一个分频系数为 P(第一个输出 PLLP)或者 Q(第二个输出 PLLQ)的分频器分频之后,最后才生成最终的主 PLL 时钟。例如我们的外部晶振选择 8MHz。同时我们设置相应的分频器 M=8,倍频器倍频系数 N=360,分频器分频系数 P=2,那么主 PLL 生成的第一个输出高速时钟 PLLP 为:PLL=8MHz * N/ (M*P)=8MHz* 360/(8*2) = 180MHz,第一个输出时钟 PLLCLK 用于系统时钟, 故F429 里面最稳定的时钟最高是180M.
3.注意图中③系统时钟 SYSCLK,它的来源可以是HSI、 PLLCLK、 HSE,我们一般选择是设置系统时钟: SYSCLK = PLLCLK = 180M。上面也有提到。而我们下面需要用到AHB 总线时钟 HCLK,即图④位置。这里设置为 1 分频,即 HCLK=SYSCLK=180M
4.注意图中⑤⑥的位置,STM32F4 很多外设的时钟来源,即两个总线桥: APB1 和 APB2,其中 APB1是低速总线(最高 45Mhz,设置为 4 分频,即 PCLK1 = HCLK/4 = 45M),APB2 是高速总线(最高 90Mhz,设置为 2 分频,即 PCLK2 =HCLK /2= 90M

这里,我们可以在STM32F4的自带库文件时钟的设置为方向分析
1.启动代码:
文件:startup_stm32f429_439xx.s
file:///F:/%E7%AC%94%E8%AE%B0%E6%9C%AC/qq8E1F43BBADC74F719786A7AF9EA5A7A7/973343e92b754f85939e7562d4326b98/clipboard.png
可以看出,在进入main函数之前,系统就调用了SystemInit函数.
那么,我们找到SystemInit函数,位于system_stm32f4xx.c
file:///F:/%E7%AC%94%E8%AE%B0%E6%9C%AC/qq8E1F43BBADC74F719786A7AF9EA5A7A7/efc945ce81fd42e89c42158a9efab630/clipboard.png
可以看出,对于我们的F429来说,PLL_N默认为360.其他几个宏定义也一目了然。
如果我们使用的是官方库提供的配置,且外部无源晶振25MHz,系统时钟配置为180MHz
我们找到晶振频率在文件stm32f4xx.h中进行设置的
file:///F:/%E7%AC%94%E8%AE%B0%E6%9C%AC/qq8E1F43BBADC74F719786A7AF9EA5A7A7/10aac3e643db4973a4054947457b5a71/2301f9d7a8be466aa70df89a5243906d.png

file:///F:/%E7%AC%94%E8%AE%B0%E6%9C%AC/qq8E1F43BBADC74F719786A7AF9EA5A7A7/4fe7a14e44c340ebb37014ecc496fc02/7udrk%7D_@tr6kf%7E002pn%7D%24ch.png
综上,如果使用外部晶振25MHz,则可以得出默认配置中:
锁相环压腔振荡器时钟PLL_VCO =(HSE_VALUE/PLL_M)* PLL_N=25/ 25* 360 =360MHz
系统时钟SYSCLK = PLL_VCO / PLL_P=360 / 2 = 180MHz .
而对于USB时钟 = PLL_VCO / PLLQ=360 / 7 = 51.7MHz.

在SetSysClock函数中,配置了系统时钟,PLL倍频以及分频系数:
file:///F:/%E7%AC%94%E8%AE%B0%E6%9C%AC/qq8E1F43BBADC74F719786A7AF9EA5A7A7/27eb58ac83ce467683b29fe1ebea93d4/clipboard.png

如果外部时钟启动失败,系统会使用默认的内部系统时钟
默认配置:
HCLK = SYSCLK / 1 = 180MHz ,AHB总线时钟
PCLK2 = HCLK / 2 =90MHz
PCLK1 = HCLK / 4 = 45MHz



STM32F429时钟树

STM32F429时钟树

外部晶振设置库源码

外部晶振设置库源码

内部晶振设置库源码

内部晶振设置库源码

启动代码

启动代码

SystemInit函数

SystemInit函数

SetSysClock函数

SetSysClock函数

RCC—使用 HSE HSI 配置时钟树梳理.rar

196.39 KB, 下载次数: 23

无法显示图片就只要上传word了

回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-6-1 16:13:27 | 显示全部楼层
没法在指定位置传图片纳 尴尬 有需要了解的朋友只有看分享的文件了
回复 支持 反对

使用道具 举报

发表于 2017-6-1 16:15:17 | 显示全部楼层
你可以出一本书了
回复 支持 反对

使用道具 举报

发表于 2017-6-1 19:31:53 | 显示全部楼层
可以分享下学习F1的经验和心得吗?谢谢
回复 支持 反对

使用道具 举报

发表于 2017-6-1 20:10:47 | 显示全部楼层
2014个赞,已经标红置酷。
回复 支持 反对

使用道具 举报

发表于 2017-6-1 20:12:47 | 显示全部楼层
王维鋆 发表于 2017-6-1 16:11
时间:2017/06/01    14:15      RCC—使用 HSE  HSI 配置时钟梳理

而对于USB时钟 = PLL_VCO / PLLQ=360 / 7 = 51.7MHz.

这个部分不知道你理解了没,当真正使用USB的时候,F429是需要降低到168M频率才能使用的,因为USB的时钟必须是48M,这个就是 F429 很奇葩 的地方。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-6-1 20:18:33 | 显示全部楼层
本帖最后由 王维鋆 于 2017-6-1 20:22 编辑
fire 发表于 2017-6-1 20:12
而对于USB时钟 = PLL_VCO / PLLQ=360 / 7 = 51.7MHz.

这个部分不知道你理解了没,当真正使用USB的时候 ...

索噶,原来是这样,火哥.这个我就是还真没明白 直接把你手册里面的这个贴过来的 打算搞usb了在弄清楚 感想火哥置酷,每日一课,事在必行
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-6-1 20:28:09 | 显示全部楼层
veterinarians 发表于 2017-6-1 19:31
可以分享下学习F1的经验和心得吗?谢谢

F1嘛,我也只会点皮毛而已,我f1是用的某原子的,刚开始,没基础按照视频和配套手册一步一步来吧,先模仿在自己修改提高反思总结,入门了就很简单了,自己看各种数据手册和参考手册培养自学能力吧 还有纯c要打好,我就是纯C太差了,现在狂补纯c ,还有百度 谷歌网上牛人开源的资料很多,慢慢来吧 一起学习交流
回复 支持 1 反对 0

使用道具 举报

 楼主| 发表于 2017-6-2 13:11:46 | 显示全部楼层
本帖最后由 王维鋆 于 2017-6-3 11:07 编辑

时间:2017/06/02   10:01                        STM32F429外部中断梳理(二)
每日一c梳理:http://blog.csdn.net/wangweijundeqq/article/details/72835122
一.优先级梳理
F429将优先级分为5个组,是由内核外设 SCB 的应用程序中断及复位控制寄存器 AIRCR 的
PRIGROUP[10:8]位决定的,主优先级即强占优先级,子优先级即响应优先级.
file:///F:/%E7%AC%94%E8%AE%B0%E6%9C%AC/qq8E1F43BBADC74F719786A7AF9EA5A7A7/11cf48dbf88741d6bc07e181d7cf4f88/clipboard.png
关于什么是抢占优先级和子优先级这里就不介绍了 函数已经封装好,我们只需了解优先级的取值范围,调用NVIC函数时不超过范围就ok,我们一般使用时,都配置为中断分组2,因为这时抢占子优先级范围都是0-3。设置方便,使用简单。
file:///F:/%E7%AC%94%E8%AE%B0%E6%9C%AC/qq8E1F43BBADC74F719786A7AF9EA5A7A7/127ef666cd2541c68ca402ba9232463a/clipboard.png

二. 抢占优先级  VS  子优先级
1.高优先级的抢占优先级是可以打断正在进行的低抢占优先级的(数字越小,优先级高)
2.抢占优先级相同的中断,高子优先级不可以打断低子优先级的中断.
3.抢占优先级相同的中断情况下,当两个中断同时发生时,那个子优先级高,谁就先执行
4.如果两个中断的抢占优先级与子优先级都一样,则谁先发生就先执行.

三. NVIC中断优先级设置步骤:
1.系统运行后先设置中断优先级分组,调用函数
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);整个系统中,只执行一次中断分组.
2.针对不同的中断源,设置对应的抢占优先级和子优先级,即初始化NVIC结构体.
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
3.如果需要挂起/解挂,查看中断当前状态等,调用相关函数即可.

四. EXTI外部中断
STM32F4的每个IO都可以作为外部中断的中断输入口,这点也是STM32F4的强大之处
STM32F429的中断控制器支持23个外部中断/事件线。每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置。STM32F429的23个外部中断
EXTI线0~15:对应外部IO口的输入中断。
EXTI线16:连接到PVD输出。
EXTI线17:连接到RTC闹钟事件。
EXTI线18:连接到USB OTG FS唤醒事件。
EXTI线19:连接到以太网唤醒事件。
EXTI线20:连接到USB OTG HS(在FS中配置)唤醒事件。
EXTI线21:连接到RTC入侵和时间戳事件。
EXTI线22:连接到RTC唤醒事件。
STM32F4是怎么把16个中断线和IO口一一对应联系起来的纳?
这里STM32F4就这样设计的:
GPIO的管脚GPIOx.0~GPIOx.15(x=A,B,C,D,E,F,G,H,I)分别对应中断线0~15。这样每个中断线对应了最多9个IO口,以挑战者PDF里面的线0映射关系图为例:
file:///F:/%E7%AC%94%E8%AE%B0%E6%9C%AC/qq8E1F43BBADC74F719786A7AF9EA5A7A7/7b861896c8f1407999b10b3b74a72204/clipboard.png
它对应了GPIOA.0、GPIOB.0、GPIOC.0、GPIOD.0、GPIOE.0、GPIOF.0、GPIOG.0,GPIOH.0,GPIOI.0。而中断线每次只能连接到1个IO口上,这样就需要通过配置来决定对应的中断线配置到哪个GPIO上.
至于EXTI初始化结构体函数成员这块我就不介绍了,很简单.理解了初始化结构体每个成员意义基本上就可以对该外设运用自如. stm32f4xx_exti.c 文件和.h文件多去梳理看看。

五.外部中断配置步骤:
1.初始化IO口为输入,使能IO口时钟(可以设置为上拉\下拉输入,具体看你硬件).
2.开启IO口复用时钟,设置IO口与中断线的映射关系.
这里必须用SYSCFG时钟(因为F4的外部中断复用功能必须使能SYSCFG时钟).
然后就设置IO口与中断线的映射关系:void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex); 配置这个函数即可。
3.初始化线上的中断,即EXTI_InitTypeDef的成员初始化.设置触发条件
(可以设置成上升沿\下降沿触发,但不能设置为高电平\低电平触发)
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct); //这里就不再介绍结构体初始化的过程了.
EXTI_InitTypeDef的成员变量:
typedef struct
{ uint32_t EXTI_Line;    //中断线的标号,EXTI_Line0~EXTI_Line15
  EXTIMode_TypeDef EXTI_Mode; //  中断模式选择  
  EXTITrigger_TypeDef EXTI_Trigger; //触发方式,上升沿\下降沿 任意电平
  FunctionalState EXTI_LineCmd;  //使能中断线  
}EXTI_InitTypeDef;
4.配置NVIC中断分组,且使能中断
对NVIC中断优先级的设置步骤,上面有讲到。这里就不在重复了.
(对于STM32中断而言,只有配置了NVIC的设置,并开启了才能执行,否则是不会执行到中断服务函数里面去的.)
5.编写中断服务函数
位于stm32f4xx_it.c中,这里需要注意中断服务函数的函数名必须跟启动文件里面预先设置的一样,如果有异,系统是找不到中断服务函数的入口的
7个IO口外部中断函数名别弄错了:
EXPORT    EXTI0_IRQHandler                    
EXPORT    EXTI1_IRQHandler              
EXPORT    EXTI2_IRQHandler                  
EXPORT    EXTI3_IRQHandler         
EXPORT    EXTI4_IRQHandler                    
EXPORT    EXTI9_5_IRQHandler               
EXPORT    EXTI15_10_IRQHandler            
中断线5-9共用中断函数EXTI9_5_IRQHandler,中断线10-15共用中断函数EXTI15_10_IRQHandler,中断线0到4就每个对应一个中断函数.
另外中断服务函数会常用的几个函数:
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);//中断线上标志位是否置位
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);//清除中断线中断标志位

/以下两个函数和上面的两个函数作用类似,但也有点不同
调用①函数会先判断这种中断是否使能,使能了才去判断中断标志位
而上面的EXTI_GetITStatus是直接用来判断标志位.
调用②也是同理可得.
*/
  EXTI_GetFlagStatus(uint32_t EXTI_Line);
  EXTI_ClearFlag(uint32_t EXTI_Line);

火哥的标准库好久可以出一套HAL库的资料,HAL库配合cubemx配置时钟,引脚等开发起来确实简单方便很多,我现在就是用原子的f4HAL库做参考,移植在挑战者上面,不过太大了 文件传不上来 这就尴尬了.

中断线映射关系

中断线映射关系

中断优先级分组库函数

中断优先级分组库函数

优先级分组表

优先级分组表

STM32F429外部中断梳理(二).rar

113.43 KB, 下载次数: 23

word

秉火固件库版本外部中断.rar

9.54 MB, 下载次数: 16

秉火固件库,原子的太大传不了 想给大家看看异同也不行

回复 支持 反对

使用道具 举报

发表于 2017-6-2 20:08:07 | 显示全部楼层
王维鋆 发表于 2017-6-1 20:28
F1嘛,我也只会点皮毛而已,我f1是用的某原子的,刚开始,没基础按照视频和配套手册一步一步来吧,先 ...

嗯嗯,谢谢
回复 支持 反对

使用道具 举报

发表于 2017-6-3 15:29:37 | 显示全部楼层
学习的真仔细,跟阁下比起来,自觉惭愧。能一起交流学习吗,最近在学习寄存器版本的F429,希望问题能互相交流讨论,方便可以留下一个联系方式
回复 支持 反对

使用道具 举报

发表于 2017-6-3 15:58:58 | 显示全部楼层
dxa572862121 发表于 2017-6-3 15:29
学习的真仔细,跟阁下比起来,自觉惭愧。能一起交流学习吗,最近在学习寄存器版本的F429,希望问题能互相交 ...

直接跟帖交流也是可以的                  
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-6-3 20:50:25 | 显示全部楼层
本帖最后由 王维鋆 于 2017-6-3 20:53 编辑

时间:2017/06/03  19:10                   SysTick----系统定时器梳理(三)

每日一c今天周末太懒了还没整理....
一.什么是SYSTick定时器?
SYSTick系统定时器是一个 24bit 的向下递减的计数器,当重装载数值寄存器的值递减到 0 的时候,系统定时器就产生一次中断,以此循环往复。只要不把SysTick控制及状态寄存器中的使能位清除,就循环往复.
二.SYSTick定时器的作用?
1.系统定时器一般用于操作系统,用于产生时基,维持操作系统的心跳。例如 UCOSII的时钟:ucos 运行需要一个系统时钟节拍(类似 “心跳”),而这个节拍是固定的(由 OS_TICKS_PER_SEC 宏定义设置),而我们STM32一般是由SysTick 来提供时钟节拍的.
2.SysTick定时器能产生中断,实现精准延时功能。如果把SysTick 的时钟选择为内核时钟,这里需要注意的是: SysTick 的时钟源自 HCLK,假设我们外部晶振为 25M,然后倍频到 180MHZ,那么 SysTick 的时钟即为180Mhz.
3.作为闹钟测量时间,具体怎么配置,没有深入研究过.
三.SysTick 配置过程
1.首先设置计数器时钟源,CTRL->CLKSOURCE(控制寄存器),这里我们一般设置为AHCLK=180Mhz。
2.设置重载值(RELOAD寄存器),且包括中断优先级设置
3.清空计数寄存器VAL->CURRENT,置CTRL->ENABLE位开始计时。
这里只需了解过程即可,SysTick 配置库函数已经编写完毕,我们用户需要编写的是SysTick 初始化函数.
四.SysTICK中断实现精确延时过程
在SysTick初始化函数里面调用了SysTick_Config()这个固件库函数.通过设置该固件库函数的形参,就决定了系统定时器经过多少时间就产生一次中断。
SysTick 中断时间的计算:
我们选择内核时钟源时,则Systick时钟为HCLK,则CLKAHB =180MHZ,而VALUELOAD则为重装载寄存器的值.而SysTick 定时器中断一次的时间为 TINT=VALUELOAD/CLKAHB.
则我们可以推出SysTick中断的步骤:
1.初始化SysTick使用的时钟:选择外部时钟源时,则Systick时钟为HCLK /8
                                               选择内核时钟源时,则Systick时钟为HCLK
2.设置好RELOAD重装载寄存器的值
3.计算出重装载寄存器中的值 VALUELOAD 减到 0 的时候产生一次中断的时间TINT=VALUELOAD* TDEC中断
4.SysTick 中断实现定时当我们知道铲射依次中断的时间后,在中断服务函数里面设置一个变量,测出进入中断的次数,从而计算出定时时间.
五.非SysTICK中断实现精确延时方法步骤(参考)
1.计算出产生1us 需要多少个时钟周期 fac_us;
2.计算出RELOAD寄存器的值
也就是产生相应延时所需要的时钟周期数RELOAD=fac_us * nus
3.使能SysTick,开始计数
4.循环检测计数到0的标志位;
5.清空计数器,关闭定时器

1、调用SysTick_CLKSourceConfig() -- 设置SysTick时钟源。
2、调用SysTick_SetReload() -- 设置SysTick重装载值。
3、调用SysTick_ITConfig () -- 使能SysTick中断.
4、调用SysTick_CounterCmd() -- 开启SysTick计数器.
5、当SysTick定时器计到0时,把COUNTFLAG位置位
6、调用SysTick_CounterCmd() -- 失能SysTick计数器,停止计数.
代码在下面贴出
void delay_config(void)
{
        SysTick->CTRL |= SysTick_CTRL_CLKSOURCE_Msk;                //时钟源为系统时钟180MHz
        SysTick->LOAD = 180;                                                                //重载值为180-1,每1us溢出一次
}
void delay_ms(uint32_t nTime)
{
        nTime *= 1000;
        SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;                        //使能SysTick,开始计数
        while(nTime--){
                while((SysTick->CTRL&0X010000) == 0);                        //等待COUNTFLAG标志位置1
        }
        SysTick->CTRL &= (~SysTick_CTRL_ENABLE_Msk);                //失能SysTick,停止计数
}
void delay_us(uint32_t nTime)
{
        SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
        while(nTime--){
                while((SysTick->CTRL&0X010000) == 0);
        }
        SysTick->CTRL &= (~SysTick_CTRL_ENABLE_Msk);
}

SysTick拿来做延时还是很简单,就不上传我自己的代码了.





回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-6-5 21:12:40 | 显示全部楼层
本帖最后由 王维鋆 于 2017-6-6 21:25 编辑

时间:2017/06/05       USART-串口梳理
每日一c梳理:http://blog.csdn.net/wangweijundeqq/article/details/72868608
计算机与外界的信息交换称为通信。通信的基本方式可分为并行通信和串行通信两种
一.并行通信概念及特点
1.所谓并行通信是指数据的各位同时在多根数据线上发送或接收
2.并行通的信特点:控制简单、传输速度快;由于传输线较多,长距离传送时成本高且接收方的各位同时接收存在困难.
二. 串行通讯概念及特点
1.串行通信是指 使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以.
2.在系统间交换信息,特别适用于计算机与计算机、计算机与外设之间的远距离通信.
3.串行通信的特点:传输线少,长距离传送时成本低,但数据的传送控制比并行通信复杂.
三.异步通信与同步通信
异步通信:指通信的发送与接收设备使用各自的时钟控制数据的发送和接收过程。为使双方的收发协调,要求发送和接收设备的时钟尽可能一致。
异步通信是以字符(构成的帧)为单位进行传输,字符与字符之间的时间间隔是任意的,但每个字符中的各位是以固定的时间传送的,即字符之间不一定有“位间隔”的整数倍的关系,但同一字符内的各位之间的距离均为“位间隔”的整数倍。如下图所示。

file:///F:/%E7%AC%94%E8%AE%B0%E6%9C%AC/qq8E1F43BBADC74F719786A7AF9EA5A7A7/90d1002c005b4b3581fb3b56a91d2eff/%E6%97%A0%E6%A0%87%E9%A2%98.png
同步通信时要建立发送方时钟对接收方时钟的直接控制,使双方达到完全同步。此时,传输数据的位之间的距离均为“位间隔”的整数倍,同时传送的字符间不留间隙,即保持位同步关系,也保持字符同步关系。以上图异步通信传输示意图做参考
四.串行通信的制式
单工:单向的(或者是收或者是发)
半双工串行通信)收/发不可同时进行
全双工串行通信)收/发可同时进行
如图所示:
file:///F:/%E7%AC%94%E8%AE%B0%E6%9C%AC/qq8E1F43BBADC74F719786A7AF9EA5A7A7/17bff11006f041cf8071ffcdd9b3f03d/clipboard.png
传输速率:比特率是每秒钟传输二进制代码的位数,单位是:位/秒(bps)。如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位、1个停止位、8个数据位),这时的比特率为:
10位×240个/秒 = 2400 bps.
五.STM32的USART-串口通信
串口最基本的设置,就是波特率的设置。 STM32F429 的串口使用起来还是蛮简单的,只要
你开启了串口时钟,并设置相应 IO 口的模式,然后配置一下波特率,数据位长度,奇偶校验
位等信息,就ok了.
先来个火哥的串口历程图
file:///F:/%E7%AC%94%E8%AE%B0%E6%9C%AC/qq8E1F43BBADC74F719786A7AF9EA5A7A7/07f2ff5343c041a4b9caf2b82b1d91b5/clipboard.png
从上图中,轻易的看出串口的步骤,总结下:
1) 使能 RX 和 TX 引脚 GPIO 时钟和 USART 时钟,基础操作。
2) 初始化 GPIO,并将 GPIO 复用到 USART 上,基础操作。
3) 配置 USART 参数;
4) 配置中断控制器并使能 USART 接收中断,基础操作。
5) 使能 USART;
其实通过这些的学习,我们可以总结出一个配置某项功能的基本步骤使能时钟——>配置端口----->查找相关寄存器,并按照需要配置.在结合参考手册和库开发手册,自己学习,比看视频效率高的多,也培养了自学能力.

最近忙着期末考试还有论文等等,所以 不定时更新 看情况了.

clipboard.png
clipboard1.png
无标题.png
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-6-7 22:26:45 | 显示全部楼层
本帖最后由 王维鋆 于 2017-6-10 16:36 编辑

时间:2017/06/07   22:01                          IIC总线的EEPROM  
最近有点忙,写的也有点不怎么好  至于代码工程没贴出,相对比较简单 ,有需要的朋友参考火哥的历程就行,后面的我觉得又需要的在贴出我的工程,也可以更贴一起交流学习另外,有需要参考的小伙伴觉得这个看着不舒服,图片不一致,可以下载分享的word查看,里面图片与内容是一致的,另,上传一个51的EEPROM做参考
————————————————————————————————————
常用的串行总线协议:
常用的微机与外设之间进行数据传输的串行总线主要有I2C总线、SPI总线和SCI总线
其中I2C总线以同步串行2线方式进行通信(一条时钟线,一条数据线)。
SPI总线则以同步串行3线方式进行通信(一条时钟线,一条数据输入线,一条数据输出线)。
SCI总线是以异步方式进行通信(一条数据输入线,一条数据输出线)。
1-wire,即单线总线,又叫单总线。例如DS18B20温度传感器就是用的这种总线结构.
我们这里重点详解下I2C串行总线,我们这里以数据手册的IIC时序图为例讲起,看不懂时序图的小伙伴必须补上来了.
一.I2C串行总线的组成及工作原理.
1.I2C总线是PHLIPS公司推出的一种串行总线,它只有两根双向信号线。一根是数据线SDA(serial data I/O),另一根是时钟线SCL(serial clock)
2.如下图所示,IIC总线上可以挂多个器件,而每个器件都有唯一的地址,这样可以标识通信目标。数据的通信的方式采用主从方式,主机负责主动联系从机,而从机则被动回应数据。
F:/%E7%AC%94%E8%AE%B0%E6%9C%AC/qq8E1F43BBADC74F719786A7AF9EA5A7A7/60a549dd77074a3f9d5f46008a5dfde4/clipboard.png

二.I2C总线传输协议
1.数据位的有效性规定:
SCL为高电平期间,数据线上的数据必须保持稳定,只有SCL信号为低电平期间,SDA状态才允许变化。如图所示
F:/%E7%AC%94%E8%AE%B0%E6%9C%AC/qq8E1F43BBADC74F719786A7AF9EA5A7A7/3e88003fd3b340ffa29f8c1e7a3b6cfa/clipboard.png
2.I2C的起始和终止信号
SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。如图所示
F:/%E7%AC%94%E8%AE%B0%E6%9C%AC/qq8E1F43BBADC74F719786A7AF9EA5A7A7/06a8699412974539980296aa66dcc8de/clipboard.png
3.I2C字节的传送与应答
每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位).如图所示
F:/%E7%AC%94%E8%AE%B0%E6%9C%AC/qq8E1F43BBADC74F719786A7AF9EA5A7A7/7772d1e4396d49d683a8f91edbfbf003/clipboard.png
4.应答位的作用
主机在发送数据时,每次发送一字节数据,都需要读取从机应答位,当从机空闲可以接收该字节数据时,从机会发出应答(一帧数据的第9位为“0”),当从机正忙于其他工作的处理来不及接收主机发送的数据时,从机会发出非应答(一帧数据的第9位为“1”)主机则应发出终止信号以结束数据的继续传送,主机通过从机发出的应答位来判断从机是否成功接收数据.

当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”来实现的。然后,从机释放SDA线,以允许主机产生终止信号。非应答信号是主机给从机的,当读取完一字节数据以后,主机不再去读取数据就给从机一个非应答信号,接着一个停止信号,直接给停止信号也是可以结束此次读操作,但是会对后面的操作带来影响,始终要记住,该给非应答信号的时候必须给.贴个IIC总线在传送数据过程中信号时序图.好好研究好时序图,一切都可以轻松解决.
F:/%E7%AC%94%E8%AE%B0%E6%9C%AC/qq8E1F43BBADC74F719786A7AF9EA5A7A7/9a866d2abe964aef86aa0c2e4e49939d/clipboard.png

三.IIC驱动代码理解(以51为例,模拟IIC)
这里,我用51单片机为例写下IIC 驱动代码,实现包括 IIC 的初始化( IO 口)、 IIC 开始、 IIC 结束、 ACK、 IIC读写等功能.IIC驱动代码是根据时序图来编写的,结合着时序图看,感兴趣的小伙伴可以试着自己写一下底层驱动代码.由于是根据时序图来编写的,就没什么要说明的
I2C起始信号程序:
void I2C_Start()
{
        SCL = 1;
        _nop_(); //51的一个机器周期 1.08506us
        SDA = 1;
        delay_5us();
        SDA = 0;
        delay_5us();
}
I2C终止信号程序:
void I2C_Stop()
{
        SDA = 0;
        _nop_();
        SCL = 1;
        delay_5us();
        SDA = 1;
        delay_5us();
}
I2C主机检测从机应答:
bit Test_ACK()
{
        SCL =  1;        //在时钟总线为高电平期间可以读取应答信号
        delay_5us();
        if (SDA)
        {
                SCL = 0;
                I2C_Stop();
                return(0);
        }
        else
        {
                SCL = 0;
                return(1);
        }
}
I2C主机发送应答:
void Master_ACK(bit i)
{
        SCL = 0;
        _nop_();
        if (i)
        {
                SDA = 0;
        }
        else
        {
                SDA = 1;
        }
        _nop_();
        SCL = 1;//数据保持稳定
        _nop_();
        SCL = 0;
        _nop_();
        SDA = 1;
        _nop_();
}
根据这些IIC驱动代码编写自己的读写函数就可以和外部IIC器件通信了.
四.EEPROM——AT24CXX梳理
1.I2C写数据流程
F:/%E7%AC%94%E8%AE%B0%E6%9C%AC/qq8E1F43BBADC74F719786A7AF9EA5A7A7/8ec338aba9964ce084739650d5a08c93/clipboard.png
在起始信号后必须传送一个从机的地址(7位)我们开发板上的AT24C02器件地址为0xa0,第8位是数据的传送方向位(R/T),用“0”表示主机发送数据(T),“1”表示主机接收数据(R)

2.I2C读数据流程
F:/%E7%AC%94%E8%AE%B0%E6%9C%AC/qq8E1F43BBADC74F719786A7AF9EA5A7A7/c55f82ce40c543b8a9687d45147c5064/clipboard.png
在读数据时也要先发送器件地址,读写方向为写,因为我们下一帧需要发送从AT24C02内那个单元开始读,之后需在发一次器件地址这个时候读写方向就为读了,接着我们就可以从总线上读取数据

对IIC芯片外设的初始化,这里就不讲解了,很简单的配置过程。
初始化好I2C后,就可以使用I2C通讯了,我这里还是以51为例,配合火哥的模拟IIC通讯历程看,你会发现,发送数据流程和接收数据流程原理是一样的。
3./*I2C发送数据 I2C写数据流程*/ 下面放图 火哥的模拟IIC的写数据历程
bit I2C_TransmitData(uchar ADDR, DAT)
{
        I2C_Start();                                        //I2C总线起始
        I2C_send_byte(AT24C02_ADDR+0);        //发送AT24C02地址加读写方向位0(写)
        if (!Test_ACK())                                //检测是否发送成功(应答)
        {
                return(0);
        }
        I2C_send_byte(ADDR);                    //发送控制字节ADDR地址输出使能
        if (!Test_ACK())                           //检测是否发送成功(应答)
        {
                return(0);
        }
        I2C_send_byte(DAT);                          //发送数字量交由AT24C02转为模拟量AOUT脚输出
        if (!Test_ACK())                          //检测是否发送成功(应答)
        {
                return(0);
        }
        I2C_Stop();                                          //I2C停止信号
        return(1);        
}
F:/%E7%AC%94%E8%AE%B0%E6%9C%AC/qq8E1F43BBADC74F719786A7AF9EA5A7A7/b54913f784d3460388713ef85eaa6501/clipboard.png
火哥的历程已经详细的说明了写数据的流程,这里,我就不多此一举了.
4.  /*I2C接收数据 I2C读数据流程*/
uchar I2C_ReceiveData(uchar ADDR)
{
        uchar DAT;
        I2C_Start();
        I2C_send_byte(AT24C02_ADDR+0);
        if (!Test_ACK())
        {
                return(0);
        }
        I2C_send_byte(ADDR);        //发送控制字节地址
        Master_ACK(0);                   //发送非应答
        I2C_Start();                   //重发起始信号
        I2C_send_byte(AT24C02_ADDR+1);        //改变读写方向(读)
        if (!Test_ACK())
        {
                return(0);
        }
        DAT = I2C_read_byte(); //把读取的值赋给形参
        Master_ACK(0);                   //主机发送非应答
        I2C_Stop();                           //I2C停止信号
        return(DAT);               //成功返回1
}
F:/%E7%AC%94%E8%AE%B0%E6%9C%AC/qq8E1F43BBADC74F719786A7AF9EA5A7A7/7ca61013c3a340b5a95c6b5534b5d3cf/clipboard.png
通过上面的分析,IIC写数据,读数据的原理我就讲的这么多了,这是我自己的理解,仅供参考.



I2C发送与应答时序图.png
I2C起始和终止信号时序图.png
I2C四个信号时序图png.png
I2C总线原理流程图.png
读数据流程图.png
火哥的模拟IIC的读数据历程.png
火哥的模拟IIC的写数据历程.png
数据位的有效性规定时序图.png
写数据流程图.png

IIC总线的EEPROM .rar

237.02 KB, 下载次数: 7

word

IIC总线基本驱动及51程序(参考).rar

2.55 KB, 下载次数: 5

51参考程序

回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-6-12 22:47:47 | 显示全部楼层
马上期末了,还有忙着过二级C,和暑假8月今年的电赛  有点忙,,有空就更,或者把现在每天自己整理的二级C笔记分享下也行
回复 支持 反对

使用道具 举报

发表于 2017-6-13 23:13:42 | 显示全部楼层
fire 发表于 2017-6-1 20:12
而对于USB时钟 = PLL_VCO / PLLQ=360 / 7 = 51.7MHz.

这个部分不知道你理解了没,当真正使用USB的时候 ...

当真正使用USB的时候,F429是需要降低到168M频率才能使用的,因为USB的时钟必须是48M,这个就是 F429 很奇葩 的地方。

我试了一下,好像不必要降到168M呀。
我用平时例程里面180M的那个system_stm32f4xx.c文件覆盖掉
USB_OTG—开发板作USB主机读取U盘  里面168M的那个system_stm32f4xx.c,
重新编译下载,测试照样可以可以读写U盘呀。串口也有输出。

但是奇怪的是用例程中的按键K2操作可以读取U盘。
把它改成每隔一段时间自动往U盘里面写数据就不行。
只是读写U盘的触发条件不一样而已,为什么就不行呢,请fire能回答一下吗?
回复 支持 反对

使用道具 举报

发表于 2017-6-14 10:48:23 | 显示全部楼层
王维鋆 发表于 2017-6-12 22:47
马上期末了,还有忙着过二级C,和暑假8月今年的电赛  有点忙,,有空就更,或者把现在每天自己整理的二级C ...

我也是8月电赛
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-6-14 16:52:25 | 显示全部楼层
本帖最后由 王维鋆 于 2017-6-14 20:57 编辑

时间:2017/06/14       SPI---读写串行FLASH   HAL库版本


百度云链接:http://pan.baidu.com/s/1hr4U1MW 密码:dy46
一、SPI基础知识
1.SPI(Serial Peripheral interface),即串行外围设备接口。主要应用于EEPROM、FLASH、实时时钟、AD转换器以及数字信号处理器和数字信号解码器之间。是一种高速的、全双工、同步的通信总线,在芯片管脚上只占用四根线。               
2.SPI接口一般使用4条线进行通信:
        MISO--主设备数据输入,从设备数据输出
        MOSI--主设备数据输出,从设备数据输入        
        SCLK--时钟信号,由主设备产生
        NSS/CS --从设备片选信号,因为SPI协议不像IC2设备有设备地址可以寻址,SPI采    11111用NSS信号线寻找,本信号线独占主机的一个引脚,即有多少个从设备,就有多少                                                   条片选信号线。
3.SPI主要特点:可以同时发出和接收串行数据,可以当做主机或从机工作
4.时钟极性(CPOL),=1串行同步时钟的空闲状态为高电平;=0串行同步时钟的空闲状态为低电平
5.时钟相位(CPHA),=1串行同步时钟的第一个跳变沿(偶数边沿)采样数据;=0串行同步时钟的第二个跳变沿(奇数边沿)采样数据。
6.STM32F429的SCK时钟频率可为fpclk1 为 90MHz, fpclk2 为 45MHz.
二.串行FLASH
W25Q128容量为16M字节,共有256个块(block),而一个块(Block)包含 16 个扇区,一个扇区4k(4096字节),即每个Block 64KB. FLASH_SIZE=16*1024*1024即16MByte寻址空间.FLASH 芯片的最小擦除单位为扇区(Sector),也就是说每次必须擦除4K字节,所有,我们必须给W25QXX一个至少4K的缓存区.
SPI初始化步骤:
1:配置相关引脚复用为SPI,使能SPI时钟;
2:设置SPI工作模式,包括主机或者从机、数据格式(高位在前还是低位在前)、设置串行时钟的极性和相位(采样方式)、SPI时钟频率(SPI的传输速度);
3:使能SPI;
SPI底层函数,重点就是写SPI FLASH函数,这里简要说明哈自己对写函数思路 我使用的是原子的HAL库,所有,这里 我参考的写数据源码就不是火哥的。
在上面已经说过,每个扇区是4k,也就是4096个地址,所以,我们在写地址之前,如果该地址的值不是0XFF,必须先擦除对应扇区,然后在写入..
1.根据要写的起始地址,确定要写的起始区域的扇区号(Sector)以及在起始扇区中的偏移量.
2.根据要写入数据的起始地址和字节数,是否已经超过了扇区容量.确定操作扇区的地址范围.
3.对没一个扇区,先遍历即将写入数据的地址区域保存的数据是否为0xff,如果都是,则不用擦除,如果检测到有不是0xff的地址区域,则先读出里面的数据,保存在一个已经事前写好的缓存区里面,再擦除扇区内容。然后把需要写入的数据,先写入缓存区,最后一起把缓存的数据写入扇区.
//写SPI FLASH  
//在指定地址开始写入指定长度的数据
//该函数带擦除操作!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)                                                
//NumByteToWrite:要写入的字节数(最大65535)   
u8 W25QXX_BUFFER[4096];                 
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
{
        u32 secpos;
        u16 secoff;
        u16 secremain;           
         u16 i;   
        u8 * W25QXX_BUF;         
           W25QXX_BUF=W25QXX_BUFFER;            
         secpos=WriteAddr/4096;//扇区地址  
        secoff=WriteAddr%4096;//在扇区内的偏移
        secremain=4096-secoff;//扇区剩余空间大小   
         //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
         if(NumByteToWrite<=secremain)
        {
                secremain=NumByteToWrite;//所要写入区域有不为0xff字节,停止检查,直接对扇区进行擦除,即写入0xff,此时i < secremain
        }
        //以下对写入数据进行处理,如果数据在一个扇区写完则结束,如果超过则将其进行分割,每份不超过一个扇区容纳量,依次写入,直到写完为止
        while(1)
        {        
                W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出所在扇区所有数据放在缓冲池
                for(i=0;i<secremain;i++)//校验数据 是否需要擦除
                {
                        if(W25QXX_BUF[secoff+i]!=0XFF)//所要写入区域有不为0xff字节,停止检查,直接对扇区进行擦除,即写入0xff           
                                break;
                }
                if(i<secremain)//写入区域有数据非OXFF数据字节,需要进行擦除处理
                {
                        W25QXX_Erase_Sector(secpos);//将需要写入扇区且要写入区域存在非0XFF字节的扇区全部擦除
                        for(i=0;i<secremain;i++) //将要写入数据放入到缓冲池对应要写入扇区相应位置,缓冲池与扇区一一对应
                        {
                                W25QXX_BUF[i+secoff]=pBuffer
;         
                        }
                        W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);////将缓冲池中的数据一一对应写入扇区,写入区域前的数据进行了恢复

                }else
                W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写入区域数据均为0xff,无需对sector进行擦除,直接写即可                                    
                if(NumByteToWrite==secremain)//如果写入数据正好在同一sector写完则处理完毕
                        break;//写入结束了
                else//写入未结束
                {
                        secpos++;//扇区地址增1 进入下一sector继续存放剩余数据
                        secoff=0;//偏移位置为0           从下一扇区起始位置开始写入

                           pBuffer+=secremain;  ////更新数据,要写入的数据地址偏移已写入数据量作为继续写入新地址
                        WriteAddr+=secremain;//更新数据,存储芯片写入地址更新     
                           NumByteToWrite-=secremain;                                 //更新数据,要写入数据减去已写入字节数量作为继续写入新数量
                        if(NumByteToWrite>4096)  //剩余数据超过一个sector,按照一个sector进行写,重走以上过程
                                secremain=4096;        //下一个扇区还是写不完
                        else                 //下一个扇区可以写完了
                                secremain=NumByteToWrite;                        
                }         
        };         
}

然后我们看读取SPI  FLASH函数,W25Q128容量为16M字节,共有256个块(block),而一个块(Block)包含 16 个扇区,所以扇区的个数为 256*16=4096,那么上面函数的参数Dst_Addr的范围就是0-4096,假如要擦除第1000个的扇区,那么这个扇区的字节起始就是1000*4096=4096000,因此把4096000先发送最高8位,次高8位,再到最低8位,然后W25Q64就从4096000开始往下擦除4K大小的数据空间,计算地址的时候是使用字节来计算的。(这里我是参考了网上的资料总结的)
//读取SPI FLASH  
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大65535)
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   
{
         u16 i;                                                                                       
        W25QXX_CS=0;                            //使能器件   
    SPI5_ReadWriteByte(W25X_ReadData);      //发送读取命令  
    SPI5_ReadWriteByte((u8)((ReadAddr)>>16));   //发送24bit地址   
    SPI5_ReadWriteByte((u8)((ReadAddr)>>8));   
    SPI5_ReadWriteByte((u8)ReadAddr);   
    for(i=0;i<NumByteToRead;i++)
        {
        pBuffer
=SPI5_ReadWriteByte(0XFF);    //循环读数  
    }
        W25QXX_CS=1;                                                   
}  

有需要参考的小伙伴,我分享了百度云0 F429挑战者IIC——
EEPROMHAL库版本   SPI通信实验 HAL库版本加入了DMA功能,对写读SPI函数有详细的注释。——————————————————————————————————————————————————————————————————————————————————
二级C基础知识梳理

时间:2017/06/12  22:37

数据结构与算法
一.算法的基本概念
算法的基本特性:可行性,确定性,有穷性,拥有足够的情报.
一个算法由两种基本要素组成:1.数据对象的运算和操作
                                                   2.算法的控制结构
基本的运算和操作有以下4类:算术运算 逻辑运算   关系运算  数据传输

算法的时间复杂度:是指执行算法所需要的计算工作量
算法的空间复杂度:是指执行算法所需要的内存空间
二.数据结构的基本概念
数据结构主要研究3个方面:1.数据的逻辑结构
                                              2.数据的存储结构
                                              3.对各种数据结构进行的运算
数据的逻辑结构是指对数据元素之间逻辑关系的描述,数据的逻辑结构有两个元素:一是元素的集合,二是数据元素之间的前后间关系.
数据的逻辑结构在计算机存储空间中的存放形式称为数据的存储结构.
为了表示存放在计算机存储空间中的各数据元素之间的逻辑关系,即前后件关系。.

数据结构分为两大类型:线性结构与非线性结构 (数据结构非空)
线性结构满足条件:有且只有一个根结点
                                每一个结点最多有一个前件,也最多有一个后件.
如果一个数据结构不是线性结构,则称为非线性结构
一个问题:空的数据结构是线性结构还是非线性结构?
如果对该线性结构的算法是按线性结构的规则处理的,则属于线性结构,否则属于非线性结构.




三.栈及其基本运算
栈是限定在一段进行插入与删除的线性表
栈顶:通常称插入,删除这一端为栈顶,另一端为栈底,空栈即表中没有任何元素
栈是按照“先进后出”或“后进先出”的原则组织数据的

栈的基本运算有3种,即入栈,退栈,读栈顶元素.
入栈运算:入栈运算是指在栈顶位置插入一个新元素
退栈运算:退栈运算是指取出栈顶元素赋给一个新的变量
读栈顶元素:读栈顶元素是指将栈顶元素赋给一个指定的变量

线性链表的基本概念
链式存储,由两部分组成:一部分用于存放数据元素值,称为数据域;另一部分用于存放指针,被称为指针域.
链式存储方式即可用于表示线性结构,也可用于表示非线性结构.
1.线性链表:线性表的链式存储结构称为线性链表
2.带链的栈:栈也是线性表,也可以采用链式存储结构.
在链式结构中,存储空间位置关系与逻辑关系是什么?
在链式存储结构中,存储数据结构的存储空间可以不连续,各数据节点的存储顺序与数据元素之间的逻辑关系可以不一致.





回复 支持 反对

使用道具 举报

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

本版积分规则

联系站长|手机版|野火电子官网|野火淘宝店铺|野火电子论坛 ( 粤ICP备14069197号 ) 大学生ARM嵌入式2群

GMT+8, 2024-5-14 20:16 , Processed in 0.057461 second(s), 27 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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