stm32战舰之路

库函数版本与寄存器版本

一、新建工程

1、建立一个工程文件夹TEST,在文件夹新建一个文件夹USER

2、打开MDK5(keil5),project-->newproject,工程路径放到刚才建立的USER文件夹下,命名为test,再根据开发板芯片选择相匹配的类型 ,到这一步,我们已经建立一个框架了。还需要启动代码和一些.c文件。

3、添加启动代码,使用的是原子哥资料,这里根据实际路径添加。程序源码-->stm32启动文件-->大容量hd.s(做法:  双击group1,把启动文件添加进来即可)

4、在工程文件夹下新建OBJ文件夹,修改编译文件路径把原始默认路径都改到OBJ文件夹下。

5、给工程添加文件夹和添加头文件路径。添加分组文件夹是点击编辑器像“品”字那个图标,进行添加即可,头文件路径是点击魔术棒那个图标,进行相应操作即可。

注:库函数版本也是类似,都是先建立一个框架,然后往新建目录添加启动文件和.c文件。

=================================================================

stm32命名规则:

=================================================================

二、学习方法

1、掌握时钟树图

2、存储器和总线架构

3、多实践,多思考。

=============================================================

三、stm32中断分组

STM32中断总共分为5组:0~4组。由SCB->AIRCR寄存器的第8~10bit控制。每个可屏蔽中断占8bit,只有高4bit起作用(7~4bit)

第0组:111  ,  0位抢占优先级,4位响应优先级   

第1组:100  ,  1位抢占优先级,3位响应优先级  

第2组:101  ,  2位抢占优先级,2位响应优先级

第3组:100  ,  3位抢占优先级,1位响应优先级

第4组:011  ,  4位抢占优先级,0位响应优先级

高抢占优先级可以打断低抢占优先级,高响应优先级不可以打断低响应优先级。

中断优先级设置步骤:

1、系统运行开始时时候设置中断优先级分组,确定组号,调用NVIC_PriorityGroupConfig()函数

2、设置所用到中断的中断优先级别。调用NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)函数

其中NVIC_InitTypeDef结构体如下所示,基本在配置时就是按此结构体成员变量来进行的。

typedef struct

{

    uint8_t NVIC_IRQChannel;

   uint8_t NVIC_IRQChannelPreemptionPriority;

   uint8_t NVIC_IRQChannelsubPriority;

   FunctionalState NVIC_IRQChannelCmd;

}NVIC_InitTypeDef;

注意:1、如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;2、高优先级的抢占优先级是可以打断正在进行地抢占优先级中断的,而抢占优先级相同的中断,高优先级的响应优先级不可以打断低响应优先级的中断。

================================================================

四、IO口模式配置:8种模式

1、上拉输入:GPIO_Mode_IPU

2、下拉输入:GPIO_Mode_IPD

3、浮空输入:GPIO_Mode_IN_FLOATING

4、模拟输入:GPIO_Mode_AIN

5、推挽输出:GPIO_Mode_Out_PP

6、开漏输出:GPIO_Mode_Out_OD

7、推挽式复用功能:GPIO_Mode_AF_PP

8、开漏复用功能:GPIO_Mode_AF_OD

每个IO端口都由7个寄存器来控制

2个32位模式配置寄存器:CRL、CRH

2个32位数据寄存器:IDR、ODR

1个32位置位/复位寄存器:BSRR

1个16位复位寄存器BRR

1个32锁存寄存器:LCKR

============================================================

五、跑马灯与蜂鸣器操作(IO作为输出控制外设方法)步骤:

1、定义配置需要的结构体 ----->GPIO_InitTypeDef   GPIO_InitStruct;

2、使能IO口时钟----->RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

3、GPIO模式配置---->GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
                                            GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
                                            GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

4、初始化GPIO----->GPIO_Init(GPIOB, &GPIO_InitStruct);

==========================================================

六、按键(IO口作为输入控制外设)

与输出不同,这里IO口作为输入是配置模式不一样,这里是上下拉输入调用函数(GPIO_ReadIputDataBit()来读取IO电平),还有按键要分支持连续按不支持连续按。设置一个标志位(mode)即可。

==========================================================

七、串口实验(IO复用功能练习)

串口配置模式对应引脚的模式配置

串口配置步骤:

1、GPIO时钟使能,复用IO口时钟使能

2、串口复位

3、GPIO模式配置

4、串口参数配置初始化

5、开启中断并初始化NVIC(需要时才用到)

6、使能串口

7、编写中断服务函数

串口框图:

=======================================================

八、外部中断学习

外部中断配置步骤:

1、配置IO口为输入

2、使能AFIO时钟

3、GPIO与中断线映射设置  ------>函数:void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)

4、中断线上中断初始化----------->函数:void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)

5、中断优先级配置---------------->函数:NVIC_Init(&NVIC_InitStructure);

6、编写中断服务函数。------------>中断服务函数在启动文件中找。

中断服务函数中常用到两个函数:

ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);     //判断中断是否发生 

void EXTI_ClearITPendingBit(uint32_t EXTI_Line);  //清除中断标志位

在这里需要说明一下,固件库还提供了两个函数用来判断外部中断状态以及清除外部状态
标志位的函数 EXTI_GetFlagStatus EXTI_ClearFlag,他们的作用和前面两个函数的作用类似。
只是在
EXTI_GetITStatus 函数中会先判断这种中断是否使能,使能了才去判断中断标志位,而
EXTI_GetFlagStatus 直接用来判断状态标志位。

=======================================================

九、看门狗学习之独立看门狗和窗口看门狗

作用:为了检测和解决由软件错误引起的故障

独立看门狗框图:

开启独立看门狗的步骤

1、取消写保护,往IWDG_KR键寄存器写0x5555 函数 IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);

2、设置预分频系数和重装值:操作IWDG_PR和IWDG_RLR寄存器。

void IWDG_SetPrescaler(uint8_t IWDG_Prescaler);      

void IWDG_SetReload(uint16_t Reload);

独立看门狗溢出时间计算:设置好看门狗的分频系数 prer 和重装载值就可以知道看门狗的喂狗时间(也就是看门狗溢出时间),该时间的计算方式为:   Tout=((4×2^prer) ×rlr) /40
其中 Tout 为看门狗溢出时间(单位为 ms); prer 为看门狗时钟预分频值(IWDG_PR 值),
范围为 0~7; rlr 为看门狗的重装载值(IWDG_RLR 的值);

3、重载计数值喂狗  --->IWDG_ReloadCounter();//向IWDG_KR寄存器写入0xAAAA操作 

4、开启看门狗  -------->IWDG_Enable();//即向IWDG_KR寄存器写入0xCCCC操作

通过上面 4 个步骤,我们就可以启动 STM32 的看门狗了,使能了看门狗,在程序里面就必须间隔一定时间喂狗,否则将导致程序复位

窗口看门狗

概念:窗口看门狗通常被用来监测,由外部干扰或不可预见的逻辑条件造成的应用程序背离正常的运行序列而产生的软件故障。除非递减计数器的值在T6位变成0前被刷新,看门狗电路在达到预置的时间周期时,会产生一个MCU复位。在递减计数器达到窗口寄存器数值之前,如果7位的递减计数器数值(在控制寄存器中)被刷新, 那么也将产生一个MCU复位。这表明递减计数器需要在一个有限的时间窗口中被刷新。

自己理解:对时间要求比较精确的检测机制就启用窗口看门狗,了解窗口看门狗产生复位的条件,那么我们要做的就是提前阻止该条件的成立。条件有两个:1、当递减计数器的值小于0x40, (若看门狗被启动)则产生复位;2、当递减计数器在窗口外被重新装载, (若看门狗被启动)则产生复位。我们提前做的动作就是:启动看门狗并且允许中断,当递减计数器等于0x40时,产生早期唤醒中断(EWI),在中断里进行刷新操作 ,即喂狗动作,它可以被用于重装载计数器,就可以避免WWDG复位。

窗口看门狗原理分析:如下面的图159所示,我们只要在t1~t2之间进行刷新重装载值(即喂狗动作),所谓的在窗口刷新就是指在t1~t2这段时间内进行重装载动作,就可以避免产生系统复位。窗口看门狗超时时间就是指从T[6:0]计数器递减到t1~t2对应的纵坐标值所用的时间,因为T[6:0]计数器最小值只能跟下窗口一样为0x40,最大值为0x7F(7位二进制位都为1时),所以窗口溢出时间也是有范围的。我们只要在溢出时间到来的时候,进行喂狗刷新就可以避免产生复位了。

举个例子验证下自己理解的看门狗原理中溢出时间:

根据溢出时间公式(stm32中文参考手册):Tout = 计数器步数 /时钟频率 = (T[5:0]+ 1)/[(Pclk1/4096)/2^WDGTB], 当计数器值取最小时,分频系数WDTB[1:0]取3,时钟频率取36MHz,代入公式:Tout = 1*4096*2^3/36(微秒:us) ==910us;最大值溢出时间Tout(最大允许刷新的时间,超过这个时间就会复位)= 64*4096*8/36=58254us=58.25ms。算出的结果和图159中文参考手册给出的例子的结果是相符的。
窗口看门狗框图:

1、使能窗口看门狗时钟 在APB1总线上。

      RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);

2、设置预分频系数和上窗口值 

      void WWDG_SetWindowValue(uint8_t WindowValue);

      void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);

3、开启看门狗中断并分组       

      分组:NVIC_Init(),开启中断:WWDG_EnableIT(); 

4、设置计数器初始值和使能看门狗 

    void WWDG_Enable(uint8_t Counter);
    该函数既设置了计数器初始值,同时使能了窗口看门狗。

5、编写中断服务函数

=========================================================

十、定时器中断学习之通用定时器TM3

定时器的知识点非常多,通过stm32参考手册的篇幅就可以知道它是有多重要了,很难一时半会搞通,只能逐步积累。

与前面套路一样,都是先要配置,才能使用定时器来控制我们想要的操作。

配置步骤:

1、使能定时器时钟  ---->TIM3挂载在APB1总线上

     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); 

2、初始化定时器参数,设置自动重装值,分频系数,计数方向等设置

     TIM_TimeBaseInit(TIM_TypeDef*TIMx,TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);

3、设置是怎么样的中断:设置TIM3_DIER允许更新中断

     TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);

     例TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE );

4、中断分组优先级设置

     NVIC_INIT()

5、开启定时器,即使能定时器

     TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)

6、中断服务函数编写

判断中断是否发生:

     ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t)

清除中断标志位

    TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT)

计算中断时间公式: Tout= ((arr+1)*(psc+1))/Tclk;

其中:TclkTIM3 的输入时钟频率(单位为 Mhz)。ToutTIM3 溢出时间(单位为 us)。
STM3F1 的通用 TIMx (TIM2TIM3TIM4 TIM5)定时器功能包括:
1)16 位向上、向下、向上/向下自动装载计数器(TIMx_CNT)。
2)16 位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数为 1
65535 之间的任意数值。
34 个独立通道(TIMx_CH1~4),这些通道可以用来作为:
A.输入捕获
B.输出比较
C. PWM 生成(边缘或中间对齐模式)
D
.单脉冲模式输出
4)可使用外部信号(TIMx_ETR)控制定时器和定时器互连(可以用 1 个定时器控制另外
一个定时器)的同步电路。

5)如下事件发生时产生中断/DMA
A.更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
B
.触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
C
.输入捕获
D.输出比较
E.支持针对定位的增量(正交)编码器和霍尔传感器电路
F.触发输入作为外部时钟或者按周期的电流管理

========================================================

十一、定时器之PWM输出

使用的是定时器3的通道2来输出PWM。

pwm:脉冲宽度调制。

原理:

ARR:最大值,计数器的最大值;

CCRx:捕获/比较值;

CNT:为计数器的值;

选择pwm模式共有两种:pwm1和pwm2,二者是有区别的,通过stm32参考手册可知是由几部分决定的,下面贴出通道1作为例子

1、当选择pwm模式1时,即OC1M[2:0]=110,采用向上计数,CNT < CCRx,输出电压有效,具体是高电平还是低电平有效呢,取决于CCER寄存器中的CCxP位,若该位为0,则高电平有效,即OC1输出高电平(1),若该位为1,则低电平有效,即OC1输出低电平(0),向下计数,也是CNT < CCRx有效;

2、当选择pwm模式2时,即OC1M[2:0]=111,采用向上计数,CNT > CCRx,输出电压有效,具体是高电平还是低电平有效呢,取决于CCER寄存器中的CCxP位,若该位为0,则高电平有效,即OC1输出高电平(1),若该位为1,则低电平有效,即OC1输出低电平(0)向下计数,也是CNT > CCRx有效;

配置步骤:

1、使能定时器3时钟,开启复用功能时钟AFIO。

      RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

      RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); 

2、将定时器3通道2(根据实际情况设置)映射到想要操作的IO口上,实际是想用定时器输出PWM来控制led,实现呼吸灯效           果,因此映射到LED0上。并设置LEDIO口为推挽复用模式

     GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);

3、初始化TIM3_CH2,设置自动重装值和预分频系数arr,psc

     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

4、设置 TIM3_CH2 的 PWM 模式,使能 TIM3 的 CH2 输出。

     TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

5、使能定时器,开启定时器

     TIM_Cmd(TIM3, ENABLE); 
6、修改 TIM3_CCR2 来控制占空比。

     TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);

=========================================================

十二、LCD学习

LCD这块需要掌握的内容比较多,也算是一个难点吧。

就我个人而言,掌握一个外设的操作,无外乎就是能够熟练使用它就可以了。

先贴一张真彩色表:

从大方面来说,学习LCD分为两个方面:熟悉TFTLCD的相关设置、FSMC(灵活的静态存储控制器)的原理,及配置。

1、关于TFTLCD来说,只要掌握液晶屏控制器的使用即可。以原子哥2.8寸LCD模块来说,使用的控制器是ILI9341。对于它,只要明白与相关操作指令即可。

0xD3:    读出LCD控制器的ID,该指令后面跟着4个参数,其中后面两个参数是控制器的ID。一个参数2字节,低八位有效。

0x36:    存储访问控制指令,通俗的说,就是填充LCD液晶屏时的方向顺序。只带一个参数,2字节,低八位有效。通过设置bit7(MY),bit6(MX),bit5(MV)来控制填充方向。

0x2A:    列地址指令,通俗的说,就是设置x坐标,带有4个参数,实际是两个坐标值(0<=SC<=EC <=240)根据实际情况

0X2B:    页地址设置指令,通俗来说,就是设置y坐标,带有4个参数,实际是两个坐标值(0<=SP<=EP<=320)根据实际情况。

0x2C:    写GRAM指令,发送该指令之后,就可以往LCD的GRAM里面写入颜色数据了。

0x2E:    读GRAM数据,发送该指令之后,第一次输出的是无效数据dummy,第二次开始的才是有效的GRAM数据。

通过上面几个指令,我们便可控制ILI9341控制器来显示我们所要显示的内容。

TFTLCD使用流程:

       初始化序列,就是向 LCD 控制器写入一系列的设置值(比如伽马校准),这些初始化序列一般 LCD 供应商会提供给客户,我们直接使用这些序列即可,不需要深入研究。在初始化之后, LCD 才可以正常使用。
 

2、FSMC(大容量,且引脚数在 100 脚以上的 STM32F103 芯片都带有 FSMC 接口)。

       用它来驱动LCD工作。任何LCD的使用,一般都经过几个步骤:硬复位,初始化序列,设置坐标,{1、写GRAM指令,写入颜色数据,LCD显示。或者2、读GRAM指令,读出颜色数据,单片机处理。}

FSMC定义:灵活的静态存储控制器

FSMC作用:1、将AHB传输信号转换到适当的外部设备协议。2、满足访问外部设备的时序要求。

FSMC接口框图:

FMSC接口支持SRAM,NOR闪存,NAND闪存,PSRAM等存储器。它将外部设备分为3类:NOR/PSRAM设备、NAND设备,PC卡设备。

FSMC里面有四个存储块(bank1,bank2,bank3,bank4),每个存储块里面有4个区。他们共用地址数据总线等信号。通过选择片选信号CS以区分不同的设备。

本节是把TFTLCD当做SRAM存储器来控制。

对FSMC里的三个寄存器进行配置,就可以驱动TFTLCD了。

FSMC_BCRx(x=1~4):闪存片选控制寄存器。

FSMC_BTRx(x=1~4):闪存片选读时序寄存器。

FSMC_BWTRx(x=1~4):闪存片选写时序。

注意:MDK里面的寄存器定义里面没有定义FSMC_BCR,FSMC_BTR,FSMC_BWTR等单独的寄存器,而是进行一些组合。FSMC_BCR和FSMC_BTR组合成BTCR[8]寄存器,FSMC_BWTRX组合成BWTR[7]寄存器。

对FMSC的操作:

1.FSMC 初始化函数 ,根据前面的讲解,初始化 FSMC 主要是初始化三个寄存器 FSMC_BCRx, FSMC_BTRx,

FSMC_BWTRx。

FSMC_NORSRAMInit();
FSMC_NANDInit();
FSMC_PCCARDInit();

这三个函数分别用来初始化 4 种类型存储器。

把LCD当做SRAM存储器,因此初始化函数为

void FSMC_NORSRAMInit(FSMC_NORSRAMInitTypeDef* FSMC_NORSRAMInitStruct);
typedef struct

{
uint32_t FSMC_Bank;
uint32_t FSMC_DataAddressMux;
uint32_t FSMC_MemoryType;
uint32_t FSMC_MemoryDataWidth;
uint32_t FSMC_BurstAccessMode;
uint32_t FSMC_AsynchronousWait;
uint32_t FSMC_WaitSignalPolarity;
uint32_t FSMC_WrapMode;
uint32_t FSMC_WaitSignalActive;
uint32_t FSMC_WriteOperation;
uint32_t FSMC_WaitSignal;
uint32_t FSMC_ExtendedMode;
uint32_t FSMC_WriteBurst;
FSMC_NORSRAMTimingInitTypeDef* FSMC_ReadWriteTimingStruct;
FSMC_NORSRAMTimingInitTypeDef* FSMC_WriteTimingStruct;
}FSMC_NORSRAMInitTypeDef;
从这个结构体我们可以看出,前面有 13 个基本类型(unit32_t)的成员变量, 这 13 个参数是用
来配置片选控制寄存器 FSMC_BCRx。 最后面还有两个SMC_NORSRAMTimingInitTypeDef 指针类型的成员变量。前面我们讲到, FSMC 有读时序和写时序之分,所以这里就是用来设置读时序和写时序的参数了, 也就是说这两个参数是用来配置寄存器写时序之分,所以这里就是用来设置读时序和写时序的参数了, 也就是说,这两个参数是用来配置寄存器 FSMC_BTRx 和 FSMC_BWTRx。
FSMC_NORSRAMTimingInitTypeDef 类型的定义:
typedef struct
{
uint32_t FSMC_AddressSetupTime;
uint32_t FSMC_AddressHoldTime;
uint32_t FSMC_DataSetupTime;
uint32_t FSMC_BusTurnAroundDuration;
uint32_t FSMC_CLKDivision;
uint32_t FSMC_DataLatency;
uint32_t FSMC_AccessMode;
}FSMC_NORSRAMTimingInitTypeDef;
这个结构体有 7 个参数用来设置 FSMC 读写时序。 

2、FSMC使能函数

FSMC 对不同的存储器类型同样提供了不同的使能函数:
void FSMC_NORSRAMCmd(uint32_t FSMC_Bank, FunctionalState NewState);
void FSMC_NANDCmd(uint32_t FSMC_Bank, FunctionalState NewState);
void FSMC_PCCARDCmd(FunctionalState NewState);

这里根据实际情况来定,你的设备是当做这三类存储器哪一种来做,就选择相对应的使能函数。

总结:LCD初始化步骤

1、GPIO/FSMC/AFIO时钟使能

2、GPIO初始化:GPIO_Init()函数

3、FSMC初始化:FSMC_NORSRAMInit()函数

4、FSMC使能:FSMC_NORSRAMCmd()函数

6、LCD驱动初始化代码。

===============================================================

13、IIC学习

IIC总线在传输数据过程中总共有3种类型信号:分别是开始信号、结束信号、应答信号。

空闲状态:scl = 1; sda = 1; 接在IIC线总线上的设备都被拉高。

开始信号:SCL处于高电平,SDA从高电平转变为低电平,数据开始发送

void IIC_Start(void)
{
SDA_OUT();     //sda线输出
IIC_SDA=1;     
IIC_SCL=1;
delay_us(4);
  IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
delay_us(4);
IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 
}   

结束信号:SCL处于高电平,SDA从低电平转变为高电平,数据发送结束

void IIC_Stop(void)
{
SDA_OUT();//sda线输出
IIC_SCL=0;
IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
  delay_us(4);
IIC_SCL=1; 
IIC_SDA=1;//发送I2C总线结束信号
delay_us(4);    
}

应答信号:接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲,

表示已收到数据

连接到相同总线上的IC数量只受总线最大电容的限制,串行的8位双向数据传输位速率在标准模式下可达100Kbit/s,快速模式下可达400Kbit/s,高速模式下可达3.4Mbit/s

IIC设备工作过程:

总线上的所用通信都是由主控制器发起的,当主控发起开始信号时,IIC总线上的设备都知道主设备要传输数据了。

1、主设备向从设备发送数据。主设备发送起始位,这会通知总线上的所有设备传输开始了,接下来主机发送设备地址,与这一地址匹配的 slave 将继续这一传输过程,而其它 slave 将会忽略接下来的传输并等待下一次传输的开始。主设备寻址到从设备后,发送它所要读取或写入的从设备的内部寄存器地址; 之后,发送数据。数据发送完毕后,发送停止位。

写入过程:

主设备先发送开始信号------>IIC_start();--->发送从设备的地址(地址有7位,前面4位已固定:1010,后面三位由IIC芯片决定,第八位是读:1/写:0位)----->等待应答ack---->发送想要往从设备哪个地址写入内容------>等待应答ack---->发送数据----->等待应答------->发送结束信号。

2、主设备从从设备中读取数据

从从设备读出数据比写入数据麻烦一点,其实就是时序多了几个步骤而已。你想要在从设备读出数据,那么你肯定要知道从从设备哪个地址读出来,那么你就需要先往从设备写入想要读取数据的地址,然后再重新发送开始信号。

读数据过程:

主设备发送开始信号------->从设备的地址(前面7为从设备地址,第八位是写:0)------->等待从设备应答------->发送想要读取数据的地址--------->等待从设备应答-------->重新发送开始信号------->发送从设备地址(前7位为地址,第八位为读:1)------->读取数据--------->不发送应答信号----------->主设备发送结束信号。

14、SPI学习

SPI定义:英文名:Serial peripheral interface.中文名:串行外围设备接口

SPI是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议。

SPI是一个环形总线结构,由ss(cs)、sck、sdi、sdo构成,其时序其实很简单,主要是在sck的控制下,两个双向移位寄存器进行数据交换。

要传输数据,就要了解时序图,来自于stm32中文参考手册。

诸多细节通过中文参考手册就清楚了。

这里查看spi_cr1寄存器,就知道各个位是控制什么的了

spi内部有控制寄存器(spi_cr1,spi_cr2)控制器1是一些控制位,控制器2是一些标志位。spi状态寄存器。spi数据寄存器等。

各寄存器地址

猜你喜欢

转载自blog.csdn.net/Mr_jianfeng/article/details/79822972