配色: 字号:
STM32F103综合2
2021-06-15 | 阅:  转:  |  分享 
  
第七章串口通信工作原理7.1STM32F103串口7.2串口通信寄存器类型实例7.37.4串口通信库函数类型实例7.5本章小结7.17
.27.37.47.5串口通信工作原理串口通信寄存器类型实例串口通信库函数类型实例STM32F103串口小结图7-1UART异
步串行通信7.1串口通信工作原理串口通信是指数据的各位按串行的方式沿一根总线进行的通信方式,RS-232标准的UART串口通信是
典型的异步双工串行通信,通信方式如图7-1所示。图7-1UART异步串行通信图7-1UART异步串行通信7.1串口通信工作
原理UART串口通信需要两个管脚,即TXD和RXD,TXD为串口数据发送端,RXD为串口数据接收端。STM32F103微控制器的
串口与计算机的串口按图7-1方式相连,串行数据传输没有同步时钟,需要双方按相同的位传输速率异步传输,这个速率称为波特率,常用的波特
率有4800bps、9600bps和115200bps等。UART串口通信的数据包以帧为单位,常用的帧结构为:1位起始位+8位数据
位+1位奇偶校验位(可选)+1位停止位,如图7-2所示。图7-1UART异步串行通信7.1串口通信工作原理图7-2串口通信
数据格式图7-1UART异步串行通信7.1串口通信工作原理奇偶校验方式分为奇校验和偶校验两种,是一种简单的数据误码检验方法,
奇校验是指每帧数据中,包括数据位和奇偶校验位在内的全部9个位中“1”的个数必须为奇数;偶校验为每帧数据中,包括数据位和奇偶校验位在
内的全部9个位中,“1”的个数必须为偶数。例如,发送数据“00110101b”,采用奇校验时,奇偶校验位必须为1,这样才能满足奇校
验条件。如果对方收到数据位和奇偶校验位后,发现“1”的个数为奇数,则认为数据传输正确;否则认为数据传输出现误码。7.17.27.3
7.47.5串口通信工作原理串口通信寄存器类型实例串口通信库函数类型实例STM32F103串口小结7.2STM32F103串口U
SART2串口结构如图7-3所示。图7-3USART2串口结构框图7.2STM32F103串口 由图7-3可知,串口USAR
T2是APB1总线上的外设单元,通过波特率寄存器USART_BRR和串口控制寄存器USART_CRx(x=1,2,3)配置串口的波
特率和工作模式,向发送数据寄存器TDR写入数据,可按设定的波特率实现数据的发送,串口接收到的数据被保存在接收数据寄存器RDR中,A
PB1总线读RDR寄存器可读到串口接收的数据。串口的数据发送和接收状态保存在串口状态寄存器中,一般地,串口发送数据通过写TDR寄存
器实现,而串口接收数据通过串口中断实现。7.2STM32F103串口串口USART2的基地址为0x40004400,其各个寄存
器的情况如下所述:(1)串口数据寄存器USART_DR(偏移地址0x04)32位的串口数据寄存器USART_DR只有第[8:0
]位有效,用于发送串口数据时记为TDR,用于接收串口数据时记为RDR,TDR和RDR是映射到同一个地址的两个物理寄存器,通过读、写
指令来区分使用了哪个寄存器,即读USART_DR时自动识别为RDR,写USART_DR时自动识别为TDR。7.2STM32F1
03串口(2)波特率寄存器USART_BRR(偏移地址0x08,复位值为0x0)32位的波特率寄存器USART_BRR只有第[1
5:0]位有效,其中,第[15:4]位记为DIV_Mantissa[11:0],第[3:0]位记为DIV_Fraction[3:0
]。波特率的计算公式为:波特率=fck/(16×USART_DIV),而USART_DIV=DIV_Mantissa
+DIV_Fraction/16,对于USART2而言,fck=PCLK1=36MHz。如果波特率设为9600bps,则可配
置DIV_Mantissa=234,DIV_Fraction=6;如果波特率设为115200bps,则可配置DIV_Mantiss
a=19,DIV_Fraction=8,实际波特率为115384bps,误差为0.15%(可接收范围内)。7.2STM32F1
03串口(3)串口状态寄存器USART_SR(偏移地址0x0,复位值为0xC0)32位的串口状态寄存器USART_SR只有第[9
:0]位有效,如表7-1所示。7.2STM32F103串口表7-1串口状态寄存器USART_SR位号名称属性含义31:10保
留9CTS可读可写CTS标志位。当nCTS输入跳变时,硬件置位,写入0清零8LBD可读可写LIN中止检测标志位。LIN中止发生后硬
件置位,写入0清零7TXE只读发送数据寄存器空标志位。TDR内容传给移位寄存器时硬件置1,写DR寄存器清06TC可读可写发送完成标
志位。发送完成硬件置1,写入0清零(写DR+读SR也可清0)5RXNE可读可写接收数据没有就绪标志位。接收数据准备好时硬件置1,读
DR或写0均可清零4IDLE只读空闲线路检测标志位。空闲时自动置1,读DR+读SR可清零3ORE只读溢出错误标志位。接收溢出时硬件
置1,读DR+读SR清零2NE只读噪声错误标志位。接收的位在采样时出现噪声则硬件置1,读DR+读SR可清零1FE只读帐错误标志位。
帧错误发生时硬件置1,读DR+读SR可清零该位0PE只读校验位错误标志位。接收的数据校验错误时硬件置1,读DR+读SR可清零该位
表7-1中的“读DR+读SR”或“写DR+读SR”是指连续性的两个操作,即“读DR”或“写DR”后,立即进行读SR的操作。7.2
STM32F103串口(4)串口控制寄存器USART_CR1(偏移地址0x0C,复位值为0x0)32位的串口控制寄存器USA
RT_CR1只有第[13:0]位有效,如表7-2所示。7.2STM32F103串口表7-2串口控制寄存器USART_CR1
位号名称属性含义31:14保留13UE可读可写USART有效位。写入1开启USART,写入0关闭12M可读可写字长位。为0表示8
位数据位;为1表示9位数据位11WAKE可读可写USART唤醒方式位。为0表示空闲位唤醒;为1表示最后有效数据位唤醒10PCE可读
可写校验控制位。为0表示无校验;为1表示有校验9PS可读可写校验选择位。为0表示偶校验;为1表示奇校验8PEIE可读可写PE中断有
效位。为1表示校验位出错触发中断,为0表示不触发7TXEIE可读可写TXE中断有效位。为1表示发送数据进入移位寄存器后触发中断,为
0表示不触发6TCIE可读可写发送完成中断有效位。为1表示发送数据完成后触发中断,为0表示不触发5RXNEIE可读可写RXNE中断
有效位。为1表示接收数据就绪或溢出时触发中断,为0表示不触发4IDLEIE可读可写空闲中断有效位。为1表示空闲将触发中断,为0表示
不触发3TE可读可写发送有效位。为0表示关闭发送单元;为1表示开启发送单元2RE可读可写接收有效位。为0表示关闭接收单元;为1表示
开启接收单元1RWU可读可写接收唤醒位。为0表示接收处于活跃模式下;为1表示处于静默模式下0SBK可读可写发送中止符位。为1表示中
止符将被发送,为0表示不发送中止符7.2STM32F103串口由表7-2可知,STM32F103ZET6微控制的串口的发送和接
收单元是相对独立的,可以单独关闭或启动它们(表7-2中TE和RE位)。此外,串口还有两个控制寄存器USART_CR2和USART_
CR3,主要用于同步串行控制和流控制,这里不做详细介绍,可参考STM32F103参考手册第27章。其中,USART_CR2的第[1
3:12]位称为STOP位,为00b表示1位停止位,为01b表示0.5位停止位,为10b表示2位停止位,为11b表示1.5位停止位
。默认值为00b,即1位停止位。7.2STM32F103串口综上所述,可知串口的操作主要有如下三种:(1)串口初始化串口
初始化包括三个主要的操作,即配置串口通信的波特率、设置串口数据帧的格式以及开启串口接收中断等。对于STM32F103ZET6,还应
通过寄存器USART_CR1打开接收单元和发送单元。(2)发送数据串口发送数据一般通过函数调用实现,发送数据前应选判断前一个发
送的数据是否发送完成,即判断USART_SR寄存器的TC位是否为1,如果为1表示前一个数据发送完成,则可以启动本次数据发送。发送数
据只需要将待发送的数据写入串口数据寄存器USART_DR中,发送单元会按拟定的波特率将数据串行发送出去。7.2STM32F10
3串口(3)接收数据串口接收数据一般通过串口接收中断实现,需要开启串口接收中断,当接收到新的数据就绪时,在串口中断服务函数中读
取串口接收到的数据。下面将在第7.3节和第7.4节中讨论基于串口USART2的串行数据通信实例。7.17.27.37.47.5
串口通信工作原理串口通信寄存器类型实例串口通信库函数类型实例STM32F103串口小结7.4串口通信库函数类型实例 本节介绍
库函数类型的串口通信工程实例,具体建设步骤如下:(1)在工程11的基础上,新建“工程13”,保存在目录“D:\STM32F103
ZET6工程\工程13”下。此时的工程13与工程11完全相同。(2)新建文件uart2.c和uart2.h,保存在目录“D:\S
TM32F103ZET6工程\工程13\BSP”下,其中,uart2.h文件如程序段6-2所示,文件uart2.c如程序段7-6所
示。程序段7-6文件uart2.c1//Filename:uart2.c23#include"incl
udes.h"45Int08Urev;67voidUART2Init(void)8{9GPIO_
InitTypeDefg;10USART_InitTypeDefu;1112 RCC_APB2PeriphClockC
md(RCC_APB2Periph_GPIOA,ENABLE);//PAEN13 RCC_APB1PeriphCloc
kCmd(RCC_APB1Periph_USART2,ENABLE);//USART2EN14第12行打开PA口的时钟源;第13
行打开串口USART2的时钟源。15g.GPIO_Pin=GPIO_Pin_2;16g.GPIO_Mode=GPIO_Mode_A
F_PP;17g.GPIO_Speed=GPIO_Speed_50MHz;18GPIO_Init(GPIOA,&g);//PA2
-U2_TX19g.GPIO_Pin=GPIO_Pin_3;20g.GPIO_Mode=GPIO_Mode_IPU;21GPIO_
Init(GPIOA,&g);//PA3-U2_RX22第15~18初始化PA2口;第19~21行初始化PA3口。23u.US
ART_BaudRate=9600;24u.USART_WordLength=USART_WordLength_8b;25u.US
ART_StopBits=USART_StopBits_1;26u.USART_Parity=USART_Parity_No;27
u.USART_HardwareFlowControl=USART_HardwareFlowControl_None;28u.US
ART_Mode=USART_Mode_Rx|USART_Mode_Tx;29USART_Init(USART2,&u);3
0第23~29行初始化串口USART2。第23行设置串口USART2的波特率为9600bps,这里可直接指定波特率的值,比使用寄
存器方式进行串口程序设计方便很多;第24行设置数据位为8位;第25行设置1位停止位;第26行指定无校验位;第27行指定无流控制;第
28行指示开启串口接收和发送功能。第29行调用库函数USART_Init初始化USART2串口。31USART_ITConfig(
USART2,USART_IT_RXNE,ENABLE);32USART_Cmd(USART2,ENABLE);3334NVIC_
EnableIRQ(USART2_IRQn);35}36第7~35行为串口USART2初始化函数。第31行调用库函数US
ART_ITConfig打开串口USART2的接收中断;第32行调用库函数USART_Cmd开启串口USART2。52}5
3第49~53行为串口USART2接收字符的函数UART2GetChar,该函数直接调用库函数USART_ReceiveData
接收串口数据(第51行)。54voidUSART2_IRQHandler(void)55{56 rev=
UART2GetChar();57 UART2PutChar(rev);58 UART2PutChar(''\n'')
;5960NVIC_ClearPendingIRQ(USART2_IRQn);}(3)修改includes.h文件,如程序段7-3
所示。(4)修改bsp.c文件,如程序段7-4所示。(5)修改tim2.c文件,如程序段7-5所示。普中STM32开发板带您进入
ARM世界6.1系统节拍定时器图6-1系统节拍定时器结构框图Systick定时器是什么?Systick定时器就是系统滴答定
时器,一个24?位的倒计数定时器,计到0?时,将从RELOAD?寄存器中自动重装载定时初值。只要不把它在SysTick?控制及状态
寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作。SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异
常号:15)。Systick中断的优先级也可以设置。在CMSIS库头文件core_cm3.h中还定义了一个初始化系统节拍定时器的
函数,如程序段6-2所示。程序段6-2系统节拍定时器初始化函数(摘自core_cm3.h文件)1__STATIC_
INLINEuint32_tSysTick_Config(uint32_tticks)2{3i
f((ticks-1UL)>SysTick_LOAD_RELOAD_Msk)4{5ret
urn(1UL);6}7SysTick->LOAD=(uint32_t)(ticks-1UL);8NVI
C_SetPriority(SysTick_IRQn,(1UL<<__NVIC_PRIO_BITS)-1UL);9S
ysTick->VAL=0UL;10SysTick->CTRL=SysTick_CTRL_CLKSOURCE_Msk
|11SysTick_CTRL_TICKINT_Msk|12SysTick_CTRL_ENABLE_Msk;13r
eturn(0UL);14}函数SysTick_Config用于初始化系统定时器SysTick,参数ticks表示系统
定时器的计数初值。__STATIC_INLINEuint32_tSysTick_Config(uint32_ttick
s)第1行的uint32_t为自定义的无符号32位整型类型,__STATIC_INLINE即staticinline,用于定义静
态内敛函数。3if((ticks-1UL)>SysTick_LOAD_RELOAD_Msk)4
{5return(1UL);6}第3行的SysTick_LOAD_RELOAD_Msk为宏常量
0x00FFFFFF,这是因为系统定时器是24位的减计数器,最大值为0x00FFFFFF,所以,当第3行为真时,说明参数tic
ks的值超过了系统定时器的最大计数值,故第5行返回1,表示出错。7SysTick->LOAD=(uint32_t)(ti
cks-1UL);第7行将ticks计数值减去1的值作为初值赋给LOAD寄存器(即系统节拍定时器重装值寄存器STRELOAD)
8NVIC_SetPriority(SysTick_IRQn,(1UL<<__NVIC_PRIO_BITS)-1UL
);第8行调用CMSIS库函数NVIC_SetPriority设置系统节拍定时器异常的优先级号为15(参考表5-2和程序段5-2
)。9SysTick->VAL=0UL;第9行向VAL寄存器(即系统节拍定时器当前计数值寄存器STCURR)写入0,使得L
OAD内的值装入VAL寄存器中。10SysTick->CTRL=SysTick_CTRL_CLKSOURCE_Msk|11
SysTick_CTRL_TICKINT_Msk|12SysTick_CTRL_ENABLE_Msk;第10行启动系统节拍定时器
,并且打开系统节拍定时器中断,其中,宏常量SysTick_CTRL_CLKSOURCE_Msk(1uL<<2)SysTick_C
TRL_TICKINT_Msk(1uL<<1)SysTick_CTRL_ENABLE_Msk(1uL<<0)根据程序段6-2可
知,设计一个定时频率为100Hz(即定时周期为10ms)的系统时钟节拍定时器,只需要调用语句“SysTick_Config(720
000uL)”即可。T=1/72MHZ=1/72,000,000定时=T720,000=10/1000
=0.01s=10ms6.2看门狗定时器6.2看门狗定时器STM32F103ZET6微控制中有两个看门狗,即独立看门
狗和窗口看门狗。本书仅介绍复杂一些的窗口看门狗。6.2.1窗口看门狗定时器工作原理窗口看门狗定时器的结构如图6-4所示。窗口
看门狗概述窗口看门狗?之所以称为窗口就是因为其喂狗时间是一个有上下限的范围内(窗口),你可以通过设定相关寄存器,设定其上限时间(下
限固定)。喂狗的时间不能过早也不能过晚。而独立看门狗限制喂狗时间在0-x内,x由相关寄存器决定。喂狗的时间不能过晚。窗口看门狗工作
过程总结STM32F的窗口看门狗中有一个7位的递减计数器T[6:0],它会在出现下述2种情况之一时产生看门狗复位:当喂狗的时候如果
计数器的值大于某一设定数值W[6:0]时,此设定数值在WWDG_CFR寄存器定义。当计数器的数值从0x40减到0x3F(0x40
-1)时【T6位跳变到0】。如果启动了看门狗并且允许中断,当递减计数器等于0x40时产生早期唤醒中断(EWI),它可以用于喂狗
以避免WWDG复位。窗口看门狗超时时间?6.3RTC实时时钟图6-6课本实时时钟工作框图RTC模块有三个时钟源:HSE
片外高精度高速时钟(8MHz)LSE外部低速时钟(32.768kHz)LSI片内低速时钟(40kHz)选择了LSE时钟,则RT
CCLK时钟信号即为32.768kHz,设定RTC预分频器的值为32767,TR_CLK=RTCCLK/(RTC_DIV
+1),即TR_CLK时钟信号为1Hz。RTC模块可触发三种类型的中断,即秒中断、溢出中断和报警中断(或闹钟中断),通过配置R
TC_CRH寄存器实现这三类中断的开启。当RTC计数器的值与RTC预警器的值相等时,产生RTC报警中断同时,该中断还可用于从待机模
式唤醒微控制器。RTC配置一般步骤使能PWR和BKP时钟:RCC_APB1PeriphClockCmd();②使能后备寄存器访
问:PWR_BackupAccessCmd();③配置RTC时钟源,使能RTC时钟:RCC_RTCCLKConfig();
RCC_RTCCLKCmd();如果使用LSE,要打开LSE:RCC_LSEConfig(RCC_LSE_ON);④设置RTC
预分频系数:RTC_SetPrescaler();⑤设置时间:RTC_SetCounter();⑥开启相关中断(如果需要):RT
C_ITConfig();⑦编写中断服务函数:RTC_IRQHandler();⑧部分操作要等待写操作完成和同步。RTC_Wai
tForLastTask();//等待最近一次对RTC寄存器的写操作完成RTC_WaitForSynchro(); //等待RT
C寄存器同步6.4通用定时器图6-8通用定时器TIM2结构框图时钟选择计数器时钟可以由下列时钟源提供:内部时钟(CK_I
NT)外部时钟模式1:外部输入脚(TIx)外部时钟模式2:外部触发输入(ETR)内部触发输入(ITRx):使用一个定时器作为另一个
定时器的预分频器,如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。?内部时钟选择÷NX1/X2
F(CK_PSC)CK_CNTAPB1时钟?时钟计算方法:÷NX1/X2F(CK_PSC)CK_CNTAPB1时钟CK
_INT除非APB1的分频系数是1,否则通用定时器的时钟等于APB1时钟的2倍。默认调用SystemInit函数情况下:SYSCL
K=72MAHB时钟=72MAPB1时钟=36M所以APB1的分频系数=AHB/APB1时钟=2所以,通用定时器时钟CK_INT=
236M=72M计数频率:计数周期:溢出时间:?TIM2_PSC寄存器是一个16位的可读可写寄存器,TIM2计数器的计数频率
=定时器时钟源频率/(TIM2_PSC+1)。如果采用72MHz系统时钟作为TIM2时钟源,设置TIM2_PSC=
7200-1,则TIM2计数器计数频率为10kHz。5voidTIM2Init(void)6{7
RCC->APB1ENR|=(1uL<<0);8 TIM2->ARR=100-1;9 TIM2->PS
C=7200-1;10 TIM2->DIER|=(1uL<<0);11 TIM2->CR1|=(1uL<
<0);1213NVIC_EnableIRQ(TIM2_IRQn);14}第5~14行为TIM2初始化函数。第7行打开
TIM2定时器的时钟源;第8行设置TIM2重装计数值为99;第9行设置TIM2预分频值为7199;第10行打开定时器刷新中断
;第11行启动定时器TIM2。定时器中断实现步骤能定时器时钟。RCC_APB1PeriphClockCmd();②初始化
定时器,配置ARR,PSC。TIM_TimeBaseInit();开启定时器中断,配置NVIC。voidTIM_ITConf
ig();NVIC_Init();④使能定时器。TIM_Cmd();⑥编写中断服务函数。TIMx_IRQHandle
r();淘宝店铺:http://eboard.taobao.com技术论坛:www.openedv.com5voidT
IM2Init(void)6{7TIM_TimeBaseInitTypeDeft;89RCC_APB1P
eriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);1011t.TIM_Period=100-
1;12t.TIM_Prescaler=7200-1;13t.TIM_ClockDivision=TIM_CKD_DIV1
;14t.TIM_CounterMode=TIM_CounterMode_Up;15TIM_TimeBaseInit(TIM2
,&t);16TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);17TIM_Cmd(TIM2,EN
ABLE);19NVIC_EnableIRQ(TIM2_IRQn);20}第5~20行为TIM2定时器初始化函数。第9
行打开TIM2的时钟源;第11~14行分别配置定时器的重装值为99、预分频值为7199、捕获/比较模块的采样频率等于定时
频率加计数工作模式;第15行调用库函数TIM_TimeBaseInit初始化TIM2定时器;第16行打开定时器TIM2刷新中断
;第17行启动定时器。22voidTIM2_IRQHandler(void)23{24 static
Int08Ui=0;25i++;26 if(i==100)27 LED(1,LED_ON);28 i
f(i==200)29 {30i=0;31 LED(1,LED_OFF);32 }33TIM_Clear
Flag(TIM2,TIM_FLAG_Update);34 //TIM_ClearITPendingBit(TIM2,TI
M_IT_Update);35NVIC_ClearPendingIRQ(TIM2_IRQn);36}第5~20行定时器T
IM2的中断服务函数。第33行调用TIM_ClearFlag库函数清除TIM2定时中断标志位。注释掉的第34行也可以实现第33行的
功能。程序要求通过定时器中断配置,每50ms中断一次,然后每1.5s服务函数中控制LED实现LED1状态取反(闪烁)。Tout(溢
出时间)=(ARR+1)(PSC+1)/fclkGO!!淘宝店铺:http://eboard.taobao.com技术论坛:www
.openedv.com第八章存储器管理本章将介绍IS62WV51216(SRAM)、AT24C02(EEPROM)和W25Q1
28(FLASH)等三个存储器芯片的访问方法,其中IS62WV51216借助FSMC(静态存储器控制器)与STM32F103ZET
6之间实现了无缝连接;AT24C02(建议设计者采用与之管脚兼容的AT24C128芯片)借助I2C总线与STM32F103ZET6
进行数据通信;而W25Q128工作在SPI总线协议下,与STM32F103ZET6微控制器片上SPI外设进行通信。EEPROM存
储器7.58.28.38.28.4STM32F103学习板上集成了一片EEPROM芯片AT24C02(工作在3.3V下),其电路
连接如第3章图3-16和图3-3所示,通过I2C1接口模块的IIC_SCL(复用PB6)和IIC_SDA(复用PB7)与STM32
F103ZET6通信。AT24C02除了SCL和SDA管脚外,还有WP管脚,当WP接高电平时,写保护;还有A2、A1和A0三个地址
输入管脚,允许最多8片AT24C02串联使用,在图3-16中,A2、A1和A0均接地,因此,图中AT24C02的地址为000b。
AT24C02内部ROM容量为2048b,即256B,被分成32页,每页8B。因此,AT24C02的地址长度为8位(被称为字地址)
,其中,5位用于页寻址,3位用于页内寻址。AT24C02写入数据方式有两种,即整页写入数据和单个字节写入数据;其读出数据方式有三种
:当前地址读出数据、随机地址读出数据和顺序地址读出数据。为了节省篇幅,这里仅介绍常用的单个字节写入数据和随机地址读出数据的编程方法
,这两种方法可以实现对AT24C02整个ROM空间任一地址的读写操作。单个字节写入数据和随机地址读出数据的时序如图8-9所示。E
EPROM存储器7.58.28.38.28.4图8-9单个字节写入数据和随机地址读出数据的时序(针对图3-16电路)EEPR
OM存储器7.58.28.38.28.4由图8-9可知,当向AT24C02写入单字节数据时,需要首先写入器件地址,由于A2、A1
和A0管脚接地,故器件地址为0xA0+[A2:A0]<<1+R/W=0xA0(写时R/W=0b)。然后写入字地址,该字地址在AT2
4C02内部自动分解为5位+3位的形式作为EEPROM内部阵列地址。接着写入字节型数据,最后延时5ms后才能进行下一次写入字节操作
,延时的时间内AT24C02进行内部的编程操作,无需用户程序干预。从AT24C02任一地址读出数据,需要先写入器件地址0xA0和字
地址,然后,再写入一次器件地址0xA1(读时R/W=1),才能读出该字地址处的字节型数据。图8-9中包括了开始、写、应答、读、无应
答和停止等控制位,这些控制位由I2C总线发出,可以采用中断方式或轮询方式响应这些控制位,本节程序采用轮询方式。例如,向地址0x5
A写入数据0x16,则依次向AT24C02写入0xA0、0x5A和0x16。从地址0x5A读出数据,则应先向AT24C02向入0x
A0、0x5A和0xA1,然后读出数据。7.58.2EEPROM存储器8.38.28.4——8.2.1访问EEPROM寄存器类
型实例(5)修改main.c文件,如程序段8-12所示。程序段8-12文件main.c1//Filename:ma
in.c23#include"includes.h"45Int08Udat1,dat2;6Int
08UDat1[100],Dat2[100];7第5、6行定义了全局变量dat1、dat2和全局数组变量Dat1、Dat2,将
它们定义为全局变量而不是局部变量的目的,在于仿真调试时可以“Watch”窗口中观察它们值。8intmain(void)9
{10 Int08Ui;11 7.58.2EEPROM存储器8.38.28.4——8.2.1访问EEPROM寄存器
类型实例14 dat1=0x16;15 for(i=0;i<100;i++)16 Dat1[i]=i+1
;17第14行赋值dat1为0x16;第15行将数组Dat1赋初值,即Dat1[i]的值为i+1,i=0,1,...,99。18
AT24C02WrByte(0x5A,dat1);19 dat2=AT24C02RdByte(0x5A);20
第18行将dat1写入到AT24C02的地址0x5A处;第19行读出AT24C02地址0x5A处的字节数据,并赋给变量dat2。7
.58.2EEPROM存储器8.38.28.4——8.2.1访问EEPROM寄存器类型实例21 for(i=0;i<1
00;i++)22 AT24C02WrByte(0x60+i,Dat1[i]);23 for(i=0;i<100
;i++)24 Dat2[i]=AT24C02RdByte(0x60+i);25第21~22行将数据Dat1的100
个数据写入到AT24C02的起始地址0x60处。第23~24行将AT24C02起始地址0x60处的100个字节数据读出来,赋给数组
Dat2。26 for(;;)27 {28 }29}第8~29行为主函数,当在线仿真时,可以在第
26行设定断点,从而观察dat2和Dat2的值。FLASH存储器7.58.38.38.28.4STM32F103ZET6微控制
器具有3个同步串行口,其中有2个复用了I2S协议接口。在STM32F103学习板上,SPI2口与FLASH存储器W25Q128相连
接,如第3章图3-15和图3-3所示。本节将以SPI2口为例详细介绍SPI通信协议和工作时序,然后,介绍STM32F103ZET6
微控制器通过SPI2口访问Flash存储器W25Q128的程序设计方法。FLASH存储器7.58.38.38.28.4——8.3
.1STM32F103同步串行口STM32F103ZET6微控制器的SPI2口具有4个功能引脚,按图3-3和图3-15所示电
路与W25Q128相连接,其中STM32F103ZET6工作在主机模式,W25Q128为从机模式,各个功能管脚的定义如表8-12所
示。表8-12STM32F103ZET6芯片SPI2口与W25Q18管脚连接情况序号STM32F103ZET6管脚替换功能W2
5Q128管脚含义1PB13SPI2_SCKCLK数据位串行时钟信号2PB15SPI2_MOSISI主机发送或从机接收数据端3PB
14SPI2_MISOSO主机接收或从机发送数据端4PB12无CS片选信号,低有效FLASH存储器7.58.38.38.28.4
——8.3.2W25Q128访问控制W25Q128为128Mb(即16MB)的串行接口FLASH存储芯片,工作电压为3.3V
,与微控制器STM32F103ZET6的电路连接如图3-15和图3-3所示。当采用标准SPI模式访问W25Q128时,其各个管脚的
含义为:CS表示片选输入信号(低有效),CLK表示串行时钟输入信号,SI为串行数据输入信号,SO为串行数据输出信号,WP表示写保护
输入信号(低有效),VCC和GND分别表示电源和地。STM32F103ZET6通过PB12、PB12(SPI2_SCK)、PB15
(SPI2_MOSI)和PB14(SPI2_MISO)四根线实现对W25Q128的读写访问,指令、地址和数据在CLK上升沿通过SI
线进入W25Q128,而在SCK下降沿从W25Q128的SO线中读出数据或状态字。FLASH存储器7.58.38.38.28.4
——8.3.2W25Q128访问控制W25Q128约有35条操作指令,下面介绍常用的几条,如表8-17所示。表8-17中读
器件ID号指令读出的W25Q128的ID号为0xEF17。表8-17常用的W25Q128指令指令字节1字节2字节3字节4字节5
整片擦除C7H/60H扇区擦除(4KB)20HA23-A16A15-A8A7-A0页编程02HA23-A16A15-A8A7-A0
D7-D0写状态寄存器01HS7-S0S15-S8读状态寄存器105HS7-S0读状态寄存器235HS15-S8写有效06H写禁止
04H读数据03HA23-A16A15-A8A7-A0D7-D0读器件ID号90H00H00H00HFLASH存储器7.58.3
8.38.28.4——8.3.2W25Q128访问控制W25Q128整片擦除的工作流程如图8-16所示。整片擦除将W25
Q128的所有字节擦除为0xFF,由图8-16可知,首先需写芯片有效,然后输出整片擦除指令0xC7或0x60,在擦除过程中,状态寄
存器1的第0位BUSY位保持为1,当擦除完成后,BUSY位转变为0,通过判断BUSY位的状态识别擦除工作是否完成。图8-16W
25Q128整片擦除的工作流程FLASH存储器7.58.38.38.28.4——8.3.2W25Q128访问控制W25Q
128芯片4kB扇区擦除的工作流程如图8-17所示。由图8-17可知,对W25Q128进行扇区擦除时,首先使写W25Q128芯片
有效,然后,向W25Q128写入0x20和24位的扇区首地址(需要注意的是,有效的扇区首地址的低12位必须为0)启动该扇区的擦除操
作,在擦除过程中,BUSY位保持1,擦除完毕后,BUSY位自动清0。图8-17W25Q128扇区擦除的工作流程FLASH存储
器7.58.38.38.28.4——8.3.2W25Q128访问控制W25Q128的页编程工作流程如图8-18所示。页编程
是指向擦除过的页面内写入数据,每次页编程前必须有一次擦除操作。如图8-18所示,页编程首先使W25Q128写入操作有效,然后写入页
编程指令0x02,接着写入24位的页地址(低8位为0),之后连续写入256个字节的数据,最后,等待状态寄存器1的BUSY位为0,说
明页编程完成。读W25Q128操作只需要写入读指令0x03,然后写入24位的地址,即可以从该地址开始读取数据,如果CLK时钟是连
续的,则地址是自动加1的,因此,可以一条读指令实现对整个芯片的读取,当然,也可以只读取一个字节。图8-18页编程工程流程FL
ASH存储器7.58.38.38.28.4——8.3.3访问FLASH存储器寄存器类型工程实例123voidW25Q
128EraseChip(void)//Costabout37s124{125WriteEn();126 SPI
2CSOutput(SPI2_CS_LOW);127 SPI2RdWrByte(0x60);128 SPI2CSOut
put(SPI2_CS_HIGH);129 while((ReadStReg1()&0x01)==0x01);//Bu
sy130}131第123~130为整片擦除W25Q128的函数W25Q128EraseChip。参考表8-17中“整片擦
除”指令,第125行打开“写使能”;第126行选中W25Q128;第127行向W25Q128写入指令0x60(也可写入0xC7);
第128行关闭W25Q128;第129行等待读状态寄存器1的第1位为0,如果该位读出1,表示内部擦除操作正在进行中,完成擦除操作需
要约37秒,之后,读出状态寄存器1的第1位(BUSY位)的值为0。FLASH存储器7.58.38.38.28.4——8.3.3
访问FLASH存储器寄存器类型工程实例132//Total:4096(0x1000)Sectors133//1
Sector=16Pages134voidW25Q128EraseSect(Int16Usect)//sec
t=0,1,...,4095(0xFFF)Cost<<1s135{136 Int08UA23_16,A15_8=
0,A7_0=0;137 A23_16=(sect>>4)&0xFF;138 A15_8|=((sect
&0x0F)<<4)&0xF0;139WriteEn();140 SPI2CSOutput(SPI2_CS_LOW);
141 SPI2RdWrByte(0x20);142 SPI2RdWrByte(A23_16);143 SPI2
RdWrByte(A15_8);144 SPI2RdWrByte(A7_0);145 SPI2CSOutput(SPI
2_CS_HIGH);146 while((ReadStReg1()&0x01)==0x01);//BusyFLAS
H存储器7.58.38.38.28.4——8.3.3访问FLASH存储器寄存器类型工程实例147}148第134~14
7行为W25Q128的扇区擦除函数W25Q128EraseSect,该函数个有一个无符号16位的整型参数sect,表示要擦除第se
ct个扇区。W25Q128共有4096个扇区,编号为0~4095,因此,这里的sect取值为0~4095。参考表8-17中“扇区擦
除(4kB)”指令,这里第136行定义的变量A23_16、A15_8和A7_0为扇区sect的首地址,第137、138行由sect
得到其首地址,即sect<<12为其首地址,然后按8位一组分别赋给变量A23_16、A15_8和A7_0。第139行开打“写使能”
;第140行选中W25Q128;第141行向W25Q128写入指令0x20;第142~144行向W25Q128依次写入24位的扇区
首地址;第145行关闭W25Q128;第146行等待W25Q128内部操作完毕。调用该函数擦除一个扇区所花的时间远小于1秒。149
//0#page--0x000000-0000FF150//1#page--0x000100
-0001FF151//2#page--0x000200-0002FF152//3#page-
-0x000300-0003FF153//..FLASH存储器7.58.38.38.28.4——8.3.3访问F
LASH存储器寄存器类型工程实例154//15#page--0x000F00-000FFF155//..1
56//65535#page--0xFFFF00-FFFFFFpage=0,1,..,65535,le
n<=256157voidW25Q128ProgPage(Int16Upage,Int08UWrDat,Int16
Ulen)158{159 Int16Ui;160 Int08UA23_16,A15_8,A7_0=0;1
61 A23_16=(page>>8)&0xFF;162 A15_8=(page&0xFF);163W
riteEn();164 SPI2CSOutput(SPI2_CS_LOW);165 SPI2RdWrByte(0x0
2);166 SPI2RdWrByte(A23_16);167 SPI2RdWrByte(A15_8);168
SPI2RdWrByte(A7_0);FLASH存储器7.58.38.38.28.4——8.3.3访问FLASH存储器寄存器
类型工程实例169 for(i=0;it++);172 }173 SPI2CSOutput(SPI2_CS_HIGH);174 while((Read
StReg1()&0x01)==0x01);//Busy175}第157~175行为向W25Q128写入一页数据的函
数W25Q128ProgPage(又称页编程),该函数具有三个参数,其中page为页的编号,W25Q128具有65536个页,故p
age的取值为0~65535(0xFFFF);WrDat指向要写入的数据;len为要写入的数据长度,由于每页包含256个字节,所以
,len的最大值为256,该函数不能跨页写入数据。第160~162行将page转化为24位的地址数据;第163行打开“写使能”;
第164行选中W25Q128;参考表8-17中“页编程”指令,第165行向W25Q128写入指令0x02,第166~168行依次写
入页首地址;第169~172行写入长度为len的数据WrDat;第173行关闭W25Q128;第174行等待W25Q128内部编程
完毕。第9章TFTLCD屏LCD显示屏是嵌入式系统中最重要的输出设备之一,STM32F103战舰V3开发板集成了一块4.3
寸480×800=384,000像素分辨率的真彩色TFT型LCD显示屏,设定工作在64k(384,000/6)色彩下。本章将介绍S
TM32F103ZET6驱动LCD屏的显示技术和工程程序设计方法。本章的学习目标:1.了解LCD屏显示原理4.熟练应用寄存器
或库函数方法在LCD屏上输出字符、汉字和图像一般地,LCD显示模块包括四部分:1、LCD显示部分(LCD面板);2、LCD屏驱动
部分;3、LCD屏控制部分(称为LCD控制器);4、LCD屏显示存储器(简称为显存)。对于一些高级的微控制器,如基于Cortex
-M3内核的LPC1788芯片,片内集成了LCD控制器,可以直接与LCD屏相连接。然而,STM32F103ZET6芯片中没有集成L
CD显示控制器,不能直接与LCD屏相连接,而要与LCD显示模块相连接。7核心代码分析讲解//LCD重要参数集typedefs
truct{u16width;//LCD宽度u16height;//LCD高度u16id;//LCDIDu8d
ir;//横屏还是竖屏控制:0,竖屏;1,横屏。u16wramcmd;//开始写gram指令0x2Cu16set
xcmd;//设置x坐标指令0x2Au16setycmd;//设置y坐标指令0x2B}_lcd_dev;//L
CD参数extern_lcd_devlcddev;//管理LCD重要参数先看7个简单,但是很重要的函数:因为FS
MC自动控制了WR/RD/CS等这些信号,所以这7个函数实现起来都非常简单,我们就不多说,注意,上面有几个函数,我们添加
了一些对MDK–O2优化的支持,去掉的话,在-O2优化的时候会出问题。这些函数实现功能见函数前面的备注,通过这几个简单函数
的组合,我们就可以对LCD进行各种操作了//写寄存器函数//regval:寄存器值voidLCD_WR_REG(u16re
gval){ LCD->LCD_REG=regval;//写入要写的寄存器序号}//写LCD数据//data:要写入的值vo
idLCD_WR_DATA(u16data){ LCD->LCD_RAM=data;}//写寄存器//LCD_Reg:寄存器地
址//LCD_RegValue:要写入的数据voidLCD_WriteReg(u16LCD_Reg,u16LCD_RegV
alue){ LCD->LCD_REG=LCD_Reg;//写入要写的寄存器序号 LCD->LCD_RAM=LCD_Re
gValue;//写入数据}//读LCD数据//返回值:读到的值u16LCD_RD_DATA(void){ vu16ra
m;//防止被优化 ram=LCD->LCD_RAM; returnram;}//读寄存器//LCD_Reg:寄存器地址//返
回值:读到的数据u16LCD_ReadReg(u16LCD_Reg){ LCD_WR_REG(LCD_Reg);//写入要读
的寄存器序号 delay_us(5); returnLCD_RD_DATA();//返回读到的值}//开始写GRAMvoid
LCD_WriteRAM_Prepare(void){ LCD->LCD_REG=lcddev.wramcmd;}//LCD写
GRAM//RGB_Code:颜色值voidLCD_WriteRAM(u16RGB_Code){ LCD->LCD_RAM
=RGB_Code;//写十六位GRAM}第八个要介绍的函数是坐标设置函数,该函数代码如下:/设置光标位置//Xpos:横坐标
//Ypos:纵坐标voidLCD_SetCursor(u16Xpos,u16Ypos){ LCD_WR_REG(lcdd
ev.setxcmd); LCD_WR_DATA(Xpos>>8); //高8位 LCD_WR_DATA(Xpos&0XFF);
//低8位 LCD_WR_REG(lcddev.setycmd); LCD_WR_DATA(Ypos>>8);//高8位 L
CD_WR_DATA(Ypos&0XFF);//低8位}//画点//x,y:坐标//POINT_COLOR:此点的颜色void
LCD_DrawPoint(u16x,u16y){ LCD_SetCursor(x,y);//设置光标位置 LCD_Wri
teRAM_Prepare();//开始写入GRAM LCD->LCD_RAM=POINT_COLOR;}7进阶函数讲解22
4voidLCDClear(Int08Ur,Int08Ug,Int08Ub)//ClearLCDScreen
225{226 Int16Ui,j;227SetCursor(0,0);228LCDWrGRAM();229
for(i=0;iDat(RGB(r,g,b));232}233第224~232行为清屏函数LCDClear。第227行将光标设在(0,0
)点(即LCD屏左上角),第228行启动写显存操作,第229~231行将显存内的全部像素点存填充为RGB(r,g,b)色彩。234
voidDrawPoint(Int16Ux,Int16Uy,Int16Ucolor)235{236SetCur
sor(x,y);237LCDWrGRAM();238LCDWrDat(color);239}240第234~239为画点函
数DrawPoint,这是最基本的函数。第236行将光标定位在(x,y)处,第237行启动写显存,第238行向当前(x,y)处写入
色彩color。241voidLCDClearRegion(Int16Ux1,Int16Uy1,Int16Ux2,I
nt16Uy2)242{243 Int16Ui,j;244SetCursor(x1,y1);245LCDWrGRA
M();246 for(i=x1;i<=x2;i++)247 for(j=y1;j<=y2;j++)248DrawP
oint(i,j,groundColor);249}250第241~249行为清除矩形区域的函数LCDClearRegio
n。即将左上角坐标(x1,y1)和右下角坐标(x2,y2)围成的矩形用背景色填充。251voidDrawLine(Int1
6Ux1,Int16Uy1,Int16Ux2,Int16Uy2)252{253 Float32k1,k2;
254Float32fx1,fx2,fy1,fy2,fx,fy;255Int16Ui,xmin,xmax
,ymin,ymax;256Int16Uix,iy;257258xmin=x1;xmax=x2;259ymin=y1;
ymax=y2;260if(x1>x2)261{262xmin=x2;xmax=x1;263}264
if(y1>y2)265{266ymin=y2;ymax=y1;267}268fx1=(Flo
at32)x1;fy1=(Float32)y1;fx2=(Float32)x2;fy2=(Float32)y2;269i
f((x1!=x2)&&(y1!=y2))270{k1=(fy2-fy1)/(fx2-fx1);//直线的斜率
//以xy小的一个为递增单位自加1,另一个按照直线斜率获得数值272for(i=xmin;i<=xmax;i++)273{274fx=(Float32)i;275fy=fy1+k1(fx-fx1);276ix=i;277iy=(Int16U)fy;278DrawPoint(ix,iy,penColor);}//xcontinum280k2=(fx2-fx1)/(fy2-fy1);//重复画281for(i=ymin;i<=ymax;i++)282{283fy=(Float32)i;284fx=fx1+k2(fy-fy1);285iy=i;286ix=(Int16U)fx;287DrawPoint(ix,iy,penColor);288}//ycontinum289}//x或y相等单直线290elseif(x1==x2)291{292for(i=ymin;i<=ymax;i++)293DrawPoint(x1,i,penColor);294}295else296{297for(i=xmin;i<=xmax;i++)298DrawPoint(i,y1,penColor);299}300}301第251~300行为画线函数DrawLine,画线函数的方法很多,该方法参考自文献[10]第198页。302voidDrawRectangle(Int16Ux1,Int16Uy1,Int16Ux2,Int16Uy2){//画四条边304 if((x1!=x2)&&(y1!=y2))305 {306DrawLine(x1,y1,x2,y1);307DrawLine(x1,y1,x1,y2);308DrawLine(x2,y2,x1,y2);309DrawLine(x2,y2,x2,y1);310 }311}312第302~311行为画矩形函数DrawRectangle。313voidDrawCircle(Int16Ux0,Int16Uy0,Int16Ur)314{315 Float32x1,y1,x2,y2,theta;316 Float32fr,fx0,fy0;317 Int16Ui;318fr=(Float32)r;fx0=(Float32)x0;fy0=(Float32)y0;319 x1=fx0+fr;320 y1=fy0;321 if(r>0)322 {323 for(i=0;i<360;i++)324 {325 theta=i3.1416/180.0;//每一°画一条线326 x2=fx0+frarm_cos_f32(theta);327 y2=fy0+frarm_sin_f32(theta);328DrawLine(x1,y1,x2,y2);329 x1=x2;330 y1=y2;331 }332 }333}334第313~333行为画圆函数。这里的arm_cos_f32和arm_sin_f32就是数学中的三解函数cos和sin,这两个函数来自于arm_cortexM3l_math.lib(DSP)库,见图4-13中的CMSIS下的“DSP”。任务:利用voidDrawLine(Int16Ux1,Int16Uy1,Int16Ux2,Int16Uy2)画一个三角形,边长不限,角度不限,想想有哪些方法
献花(0)
+1
(本文系新用户0398M...首藏)