打印
[应用相关]

STM32高级外设篇笔记

[复制链接]
324|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2024-10-27 12:49 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
32的最小系统通常包括:STM32芯片、电源、时钟、下载调试和复位5部分组成。

STM32F103ZET6 型号是什么意思?
ST:公司
M:基于Coretex-M 3
32:32位
F103:基础型
Z:引脚数 144
E:闪存大小 512K
T:QFP 封装 四面扁平封装
6:工作温度

1 GPIO
1.1 GPIO的8种工作模式
NMOS和PMOS组成一个CMOS,CMOS具有反向功能,输出控制也具有反向功能。

(1)输入浮空(Input floating)

(2)输入上拉(Input pull-up)

(3)输入下拉(Input-pull-down)

(4)模拟输入(Analog)

(5)通用开漏输出(Output open-drain)

(6)通用推挽式输出(Output push-pull)

(7)推挽式复用功能(Alternate function push-pull)

(8)开漏复用功能(Alternate function open-drain)

1.1.1输入输出
输出时
(1)输出缓冲器被激活。

(2)推挽模式:输出寄存器上的 1 将激活P-MOS,输出高电平。0 将激活N-MOS,输出低电平。

(3)开漏模式:PMOS***关闭。 输出寄存器上的 0 激活N-MOS,而输出寄存器上的1 将端口置于高阻状态,所以外部必须要接上拉电阻。

(4)施密特触发输入被激活。

(5)弱上拉和下拉电阻被禁止。

(6)出现在I/O脚上的数据在每个APB2时钟被采样到输入数据寄存器。

(7)在开漏模式时,对输入数据寄存器的读访问可得到I/O状态。

(8)在推挽模式时,对输出数据寄存器的读访问得到最后一次写的值。

推挽输出和开漏输出有什么区别? 复用输出和通用输出有什么区别?
推挽输出:
P-MOS管与N-MOS管交替工作
适用场景:驱动能力强,高速
开漏输出:
P-MOS管永久关闭,正常输出0,如果要输出1,需要在外部接一个上拉电阻
适用场景:不同电压系统、总线结构
通用输出:输入写入ODR
复用输出:ODR关闭,数据来自于片上外设

在优化代码的宏定义中, RCC->APB2ENR是什么意思? GPIO_ODR_ODR0是什么意思?
RCC->APB2ENR代表的是 时钟APB2总线的寄存器地址
GPIO_ODR_ODR0代表的是 在GPIO模块中输出数据寄存器中的位置信息

通用推挽输出:



开漏输出



开漏输出总结:

1.上部晶体管***关闭。
2.可以输出0。
3.如果要输出1必须接上拉电阻

输入时



模拟输入



当配置为模拟输入时:

(1)输出部分被禁止。

(2)禁止施密特触发输入,实现了每个模拟I/O引脚上的零消耗。施密特触发输出值被强置为0。

(3)弱上拉和下拉电阻被禁止。

(4)读取输入数据寄存器时数值***为0。

1.2 系统架构
1.2.1 4个主动单元



1.2.2 3个主动单元



1.2.3 其他单元



1.3 时钟树



在STM32中有3种不同的时钟源用来驱动系统时钟(SYSCLK):

(1)HSI振荡器时钟(High Speed Internal oscillator,高速内部时钟)

(2)HSE振荡器时钟(High Speed External(Oscillator / Clock),高速外部时钟)

(3)PLL时钟(Phase Locked Loop 锁相环/倍频器)

还有2种2级时钟:

(4)LSI时钟(Low Speed Internal,低速内部时钟)

(5)LSE时钟(Low Speed External oscillator,低速外部时钟)。

为什么提供这么多的时钟?节能!高速设备接高速时钟,低速设备接低速时钟,可以最大程度的达到节能效果。

2 STM32的中断体系架构



1.我们使用的stm32F103ze一共有多少种中断? 分为哪几类?
70
10个内部中断(异常)
60个外部中断
片上外设:40
片外外设:20

NVIC是什么? 主要控制什么?
嵌套向量中断控制器
使能,优先级

在什么情况下的中断会使用到EXTI? EXTI主要设置哪些东西?
片外外设通过引脚产生的中断会用EXTI
主要设置:
触发方式:上升沿/下降沿
屏蔽寄存器
中断处理函数:PR,用于清除中断标志位

2.1 中断优先级
NVIC为了方便管理中断,可以通过软件给每个中断设置优先级。NVIC用4个位来控制优先级,值小的优先级高。把优先级分为两种:抢占优先级和响应优先级。

规则:

Ø 优先级值越小,优先级越高。

Ø 如果不设置优先级,则默认优先级为0。

Ø 先比较抢占优先级。抢占优先级高的可以打断抢占优先级低的。

Ø 若抢占优先级一样,再比较响应优先级。但是响应优先级不会导致中断嵌套。

Ø 若抢占优先级一样的同时挂起,则优先处理响应抢占优先级高的。

Ø 若挂起的优先级(抢占和响应)都一样,则查找中断向量表,值小的先响应。

NVIC对优先级分了5组,在程序中先对中断进行分组,而且分组只能分一次,若多次分,只有最后一次生效。



注:由图可知分组是从序号3开始的。
2.2 外部中断



2.3 外部中断配置
2.3.1 基础配置
        /*1.开启时钟 GPIO AFIO*/
    RCC->APB2ENR |= RCC_APB2ENR_IOPFEN;
    RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
    /*2.配置PF10 工作模式 下拉输入 MODE: 00 CNF: 10 ODR:0*/
    GPIOF->CRH &= ~GPIO_CRH_MODE10;
    GPIOF->CRH &= ~GPIO_CRH_CNF10_0;
    GPIOF->CRH |= GPIO_CRH_CNF10_1;
    GPIOF->ODR &= ~GPIO_ODR_ODR10;
    /*3.配置AFIO 选择PF10*/
    AFIO->EXTICR[2] |= AFIO_EXTICR3_EXTI10_PF;


2.3.2 EXTI
中断屏蔽寄存器(EXTI_IMR) :

MRx: 线x上的中断屏蔽
上升沿触发选择寄存器(EXTI_RTSR) :

TRx: 线x上的上升沿触发事件配置位 (做输入时,要配合选择下拉输入)
下降沿触发选择寄存器(EXTI_FTSR) :

TRx: 线x上的下降沿触发事件配置位 (做输入时,要配合选择上拉输入)
挂起寄存器(EXTI_PR) : ---- 通常在中断处理函数中写 “1” 清除

PRx: 挂起位 (在该位中写入’1’可以清除它)
2.3.3 NVIC
        NVIC_SetPriorityGrouping(3); //1.设置分组
    NVIC_SetPriority(EXTI15_10_IRQn,1);//2.设置优先级
    NVIC_EnableIRQ(EXTI15_10_IRQn);//3.使能


3 USART
3.1 串口通信协议



3.2 USART外设
STM32提供了USART(Universal Synchronous Asynchronous Receiver and Transmitter)通用同步异步收发器。是一个串行通信设备,可以灵活地与外部设备进行全双工数据交换。

还有UART相比USART去掉了同步通讯功能。

一共提供5个串口供开发者选择。



3.2.1USART功能框图



3.3 相关寄存器
状态寄存器(USART_SR) :

TXE:发送数据寄存器空
RXNE:读数据寄存器非空 ---- 清零操作,读USART_DR
IDLE:监测到总线空闲 ---- 清零操作,先读USART_SR,然后读USART_DR
数据寄存器(USART_DR) :

DR[8:0]:数据值
波特比率寄存器(USART_BRR) :

位15:4 整数部分
位3:0 小数部分
控制寄存器 1(USART_CR1) :

UE:USART使能
TE:发送使能
RE:接收使能
IDLEIE:IDLE中断使能
RXNEIE:接收缓冲区非空中断使能
M:字长
PCE:检验控制使能
控制寄存器 2(USART_CR2) :

STOP:停止位
3.4 USART配置
3.4.1 基础配置
        /* 1.开启时钟 GPIOA USART */
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN;

    /* 2.配置GPIO工作模式 */
    // 2.1 PA9  Tx  复用推挽输出  MODE:11  CNF:10
    GPIOA->CRH |= GPIO_CRH_MODE9;
    GPIOA->CRH &= ~GPIO_CRH_CNF9_0;
    GPIOA->CRH |= GPIO_CRH_CNF9_1;

    // 2.2 PA10 Rx  浮空输入      MODE:00  CNF:01
    GPIOA->CRH &= ~GPIO_CRH_MODE10;
    GPIOA->CRH |= GPIO_CRH_CNF10_0;
    GPIOA->CRH &= ~GPIO_CRH_CNF10_1;


3.4.2 USART模块配置
        // 3.1 配置波特率  115200
    USART1->BRR = 0x271;
    // 3.2 接收、发送使能
    USART1->CR1 |= USART_CR1_RE;
    USART1->CR1 |= USART_CR1_TE;
    // 3.3 字长  默认0:表示数据长度8
    USART1->CR1 &= ~USART_CR1_M;
    // 3.4 不需要校验 默认0:表示不开启检验
    USART1->CR1 &= ~USART_CR1_PCE;
    // 3.5 配置停止位 默认00:表示停止位为1位
    USART1->CR2 &= ~USART_CR2_STOP;
    // 3.6 开启中断,接收单个字节完成,空闲线路
    USART1->CR1 &= ~USART_CR1_RXNEIE;
    USART1->CR1 &= ~USART_CR1_IDLEIE;
    // 3.7 USART使能
    USART1->CR1 |= USART_CR1_UE;



3.4.3 NVIC配置
        NVIC_SetPriorityGrouping(3);//1.设置分组
    NVIC_SetPriority(USART1_IRQn,1);//2.设置优先级
    NVIC_EnableIRQ(USART1_IRQn);//3.使能


3.4.4 状态判断

while ((USART1->SR & USART_SR_TXE) == 0)
        ; //等待循环至发送完成

while ((USART1->SR & USART_SR_RXNE) == 0)
        ;//等待循环至接收完成

if (USART1->SR & USART_SR_IDLE)//判断条件为true则到了空闲帧




3.4.5 中断处理函数(接收)
uint8_t buffer[100] = {0};
uint8_t len = 0;
uint8_t isOver = 0;
void USART1_IRQHandler(void)
{
    if (USART1->SR & USART_SR_RXNE)
    {
        //将RXNE置零
        buffer[len] = USART1->DR;
        //len可放在主函数中置零
        len++;
    }
    else if (USART1->SR & USART_SR_IDLE)
    {
        //将IDLE置零
        USART1->SR;
        USART1->DR;
                //isOver标志位可放在主函数中置零
        isOver = 1;
    }
}





3.4.6 HAL库USART轮询----接收定长和变长数据
main.c

/* USER CODE BEGIN 0 */
uint8_t buffer[100] = {0};
uint16_t size = 0;
/* USER CODE END 0 */

/* USER CODE BEGIN WHILE */
  while (1)
  {
    /*1.使用轮询的方式接受定长数据*/
    // if ((HAL_UART_Receive(&huart1,buffer,10,HAL_MAX_DELAY)) == HAL_OK)
    // {
    //   HAL_UART_Transmit(&huart1,buffer,10,HAL_MAX_DELAY);
    // }

    /*2.使用轮询方式接受变长数据*/
    if(HAL_UARTEx_ReceiveToIdle(&huart1,buffer,100,&size,HAL_MAX_DELAY) == HAL_OK)
    {
      HAL_UART_Transmit(&huart1,buffer,size,HAL_MAX_DELAY);
    }
    /* USER CODE END WHILE */
  }




3.4.7 HAL库USART中断----接收定长和变长数据
1:stm32f1xx_it.c

/* USER CODE BEGIN 1 */
extern uint8_t isOver;
extern uint16_t size;
// void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
// {
//   isOver = 1;
// }

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
  if(huart1.Instance == USART1)
  {
    isOver = 1;
    size = Size; // 真正接收到的数据
  }
}
/* USER CODE END 1 */



2: main.c

/* USER CODE BEGIN 0 */
uint8_t isOver = 0;
uint8_t buffer[100] = {0};
uint16_t size = 0;
/* USER CODE END 0 */

/* USER CODE BEGIN WHILE */
  while (1)
  {
    /*1.使用中断方式接收定长数据*/
    //为什么放在循环里边,因为这个函数调用才打开,中断执行一次就自动关闭中断了
    // HAL_UART_Receive_IT(&huart1,buffer,10);
    // if (isOver)
    // {
    //   HAL_UART_Transmit(&huart1,buffer,10,HAL_MAX_DELAY);
    //   isOver = 0;
    // }

    /*2. 使用中断方式接受变长数据*/
    HAL_UARTEx_ReceiveToIdle_IT(&huart1,buffer,100);
    if (isOver)
    {
      HAL_UART_Transmit(&huart1,buffer,size,HAL_MAX_DELAY);
      isOver = 0;
    }
    /* USER CODE END WHILE */
  }





4 IIC
IIC 通信要求连接总线的引脚设置为开漏输出模式
4.1 操作时序图整理
1)起始和停止信号



2)数据有效性



3)响应和非响应



4)写入一个字节时序



5)读出一个字节时序



6)单次写入多个字节时序



一次性写入多个字节,也叫页写入(Page Write)。AT24C02每页只有16个字节,每次只能写入单独的一个页中,所以一次性最多只能写入16个字节。当一次性写入超过16个字节的时候,则超过的部分会重新从这页的首地址重新写入。

7)单次读出多个字节时序



读出多个字节的时候没有限制,可以读出任意多个。

4.2.1 IIC初始化配置
void Driver_I2C2_Init(void)
{
    /*1.开启时钟*/
    RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
    RCC->APB1ENR |= RCC_APB1ENR_I2C2EN;

    /*2.工作模式  PB10 PB11 复用开漏输出  MODE:11 CNF:11*/
    GPIOB->CRH |= (GPIO_CRH_MODE10 | GPIO_CRH_MODE11);
    GPIOB->CRH |= (GPIO_CRH_CNF10 | GPIO_CRH_CNF11);

    /*3.配置I2C2*/
       // 3.1 配置使用I2C模式  默认配置0 I2C   1  SMBus
    I2C2->CR1 &= ~I2C_CR1_SMBUS;

    // 3.2 配置时钟频率 36MHz
    I2C2->CR2 |= 36;// 刚好最后6位

    // 3.3 配置标准模式  0:100k   1:400k
    I2C2->CCR &= ~I2C_CCR_FS;

    // 3.4 配置CCR  T(h) = CCR * T(apb1)
    // 速率:100k  周期:10us
    // 高电平与低电平时间相同,5us
    // ccr = 5 * 36 = 180;
    I2C2->CCR |= 180;

    // 3.5 配置上升沿最大时间 1us+1个时钟周期
    I2C2->TRISE |= 37;

    // 3.6 I2C模块使能
    I2C2->CR1 |= I2C_CR1_PE;

}



4.2.2 IIC HAL库函数
HAL_I2C_Mem_Read(&hi2c2,
                      R_ADDR,
                      innerAddr,
                      I2C_MEMADD_SIZE_8BIT,
                      bytes,
                      len,
                      HAL_MAX_DELAY);

HAL_I2C_Mem_Write(&hi2c2,
                      W_ADDR,
                      innerAddr,
                      I2C_MEMADD_SIZE_8BIT,
                      bytes,
                      len,
                      HAL_MAX_DELAY);


4.3 寄存器
控制寄存器 1(I2C_CR1) :

PE:I2C模块使能

SMBUS:SMBus模式 ,为 0 时是I2C模式,默认为 0;

START:起始条件产生

STOP:停止条件产生 ---- (该项为配置项,所以需要提前配置)

ACK:应答使能 ---- (该项为配置项,所以需要提前配置)

控制寄存器 2(I2C_CR2) :

FREQ[5:0]:I2C模块时钟频率 ---- 100100:36MHz
状态寄存器 1(I2C_SR1) :

SB:起始位(主模式) ---- 读SR1清除标志位。
TxE:发送数据寄存器为空(发送时) ---- 写DR清除标志位。
RxNE:接收数据寄存器非空(接收时) ---- 读/写DR清除标志位
ADDR:地址已被发送(主模式)/地址匹配(从模式) ---- 先读SR1,再读SR2清除标志位。
BTF:字节发送结束 ---- 读SR1,读/写DR清除标志位。
时钟控制寄存器(I2C_CCR) :

F/S:I2C主模式选项 ---- 0:标准模式100k; 1:快速模式400k。
CCR[11:0]:快速/标准模式下的时钟控制分频系数(主模式) ---- T(h) = CCR * T(apb1)
TRISE寄存器(I2C_TRISE) :

TRISE[5:0] :在快速 / 标准模式下的最大上升时间 ( 主模式 ) ---- 配置上升沿最大时间 1us+1个时钟周期
数据寄存器(I2C_DR) :

DR[7:0]:8位数据寄存器
4.4 硬件时序问题
        // a.√

    // b.×
    // printf("Inf_M24C02_ReadBytes---Addr Ack = %d\n", ack);

    // c.×
    // printf("Inf_M24C02_ReadBytes---Addr Ack = %d\n", ack);
    // Driver_I2C2_Ack();

    // d.√
    // Driver_I2C2_Ack();
    // printf("Inf_M24C02_ReadBytes---Addr Ack = %d\n", ack);


接收多数据时,由于硬件的速度很快,从节点到达DR后立刻将RXNE置1,读取ACK。所以配置ACK的优先级必须设置高,否则还没来得及配置ACK硬件已经接收了NACK。

4.5 每日一考
1.判断启动信号和地址发送成功的标志位是什么?
判断数据可以接收和发送的标志位是什么?并说明如何清除这些标志位.
启动信号:
start -> 1
发送成功 SB 读SR1清除标志位
地址发送成功
ADDR 先读SR1,再读SR2清除标志位
数据可以接收
RXNE 读/写DR清除标志位
数据可以发送
TXE 写DR清除标志位
发送成功 BTF 读SR1,读/写DR清除标志位

2.硬件实现I2C,在接收数据的函数中, 返回ACK和发送Stop信号需要注意什么?
ACK/NACK STOP都属于自动回复的配置项,在接收完数据之后自动回复
需要注意:提前做好配置

5 定时器
5.1 系统滴答定时器
5.1.1 系统定时器寄存器
CTRL:



CLKSOURCE: Clock source selection
Selects the clock source.
0: AHB/8
1: Processor clock (AHB)

LOAD:



VAL:



5.2 基本定时器
5.2.1 基本定时器框图



5.2.2 基本定时器时钟源



5.2.3 基本定时器时基单元
PSC 预分频寄存器:

预分频器将过来的时钟信号进行预分频
按1到65536之间的任意值分频。
然后把分频后的信号作为计数器的时钟
自动重装载寄存器:

基本定时器只能向上计数,从0开始自增自增到自动重装载寄存器的值时,下一个时钟上升沿到来后,计数器产生溢出,从0重新计数,并产生更新事件(UEV)。
如果开启中断,也会产生更新中断
计数器寄存器:

其实包含两个寄存器:预加载寄存器和影子寄存器
写数据到自动重装载寄存器时先写到预加载寄存器然后再更新到影子寄存器
计数器是否溢出,是查看的影子寄存器的值
寄存器CR1的ARPE位决定更新时机(是否预加载)。没有预加载时,写入的值会立即更新到影子寄存器
有预加载时,写入的值会等到产生更新事件(计数器溢出)才更新到影子寄存器
5.2.4 计算定时时间



5.2.5 影子寄存器的典型代码案例
        /* 2.预分频系数 7199 */
    TIM6->PSC = 7200 - 1;
    /* 3.重装载值 9999 */
    TIM6->ARR = 10000 - 1;
    /* 4.中断使能 */
    TIM6->DIER |= TIM_DIER_UIE;

    /*4.1 手动产生一个更新事件,将PSC的数值传送到实际的预分频寄存器(即影子寄存器)中*/
    TIM6->CR1 |= TIM_CR1_URS;
    TIM6->EGR |= TIM_EGR_UG;


PSC[15:0]:预分频器数值 (Prescaler value)
计数器的时钟频率CK_CNT等于fCK_PSC/(PSC[15:0]+1)。
在每一次更新事件时,PSC的数值被传送到实际的预分频寄存器(影子寄存器)中。

由于将PSC的数值被传送到实际的预分频寄存器中需要一次更新事件,所以第一次进中断前PSC的值为默认值1分频,此时频率为72MHZ,重装值9999后也是小于1ms级别,人眼难以观察到。为了让PSC在初始配置时就将分频系数传递到影子寄存器中,我们可以手动产生一个更新事件。用到如下两个寄存器中的位;

CR1->URS:更新请求源 (Update request source)
该位由软件设置和清除,以选择UEV事件的请求源。

0:如果使能了中断或DMA,以下任一事件可以产生一个更新中断或DMA请求:

计数器上溢或下溢
设置UG位
通过从模式控制器产生的更新
1:如果使能了中断或DMA,只有计数器上溢或下溢可以产生更新中断或DMA请求。

EGR->UG:产生更新事件 (Update generation)
该位由软件设置,由硬件自动清除。

0:无作用
1:重新初始化定时器的计数器并产生对寄存器的更新。注意:预分频器也被清除(但预分频系
数不变)。
5.2.6 相关寄存器
CR1:

CEN:计数器使能

ARPE:自动重装载预装载使能 (Auto-reload preload enable)

0:TIMx_ARR寄存器没有缓冲
1:TIMx_ARR寄存器具有缓冲
URS:更新请求源 (Update request source)
该位由软件设置和清除,以选择UEV事件的请求源。

0:如果使能了中断或DMA,以下任一事件可以产生一个更新中断或DMA请求:

计数器上溢或下溢
设置UG位
通过从模式控制器产生的更新
1:如果使能了中断或DMA,只有计数器上溢或下溢可以产生更新中断或DMA请求。

DIER:

UIE:更新中断使能 (Update interrupt enable)
SR:

UIF:更新中断标志 (Update interrupt flag)
EGR:

UG:产生更新事件 (Update generation)
该位由软件设置,由硬件自动清除。
0:无作用
1:重新初始化定时器的计数器并产生对寄存器的更新。注意:预分频器也被清除(但预分频系
数不变)。
CNT:

CNT[15:0]:计数器数值 (Counter value)
PSC:

PSC[15:0]:预分频器数值 (Prescaler value)
计数器的时钟频率CK_CNT等于fCK_PSC/(PSC[15:0]+1)。
在每一次更新事件时,PSC的数值被传送到实际的预分频寄存器中
ARR:

ARR[15:0]:自动重装载数值 (Prescaler value)
5.2.7 HAL库中的中断函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  if (htim->Instance == TIM6)
  {
    HAL_GPIO_TogglePin(LED2_GPIO_Port,LED2_Pin);

  }

}


5.3 通用定时器
5.3.1 通用定时器功能框架



5.3.2 3种可选时钟源
内部时钟模式,一般72MHz。与基本定时器一致默认时钟源就是内部时钟
外部时钟源模式1,
使用定时器 自身通道的输入信号作为时钟源
只有通道1和通道2的信号可以作为时钟信号源
外部时钟源模式2,使用定时器的特殊引脚ETR引脚的信号作为时钟源
5.3.3 输出比较
捕获/比较模式寄存器 1(TIMx_CCMR1) :



CC1S[1:0]:捕获/比较1 选择 (Capture/Compare 1 selection)
这2位定义通道的方向(输入/输出),及输入脚的选择:

00:CC1通道被配置为输出;

01:CC1通道被配置为输入,IC1映射在TI1上;

10:CC1通道被配置为输入,IC1映射在TI2上;

11:CC1通道被配置为输入,IC1映射在TRC上。此模式仅工作在内部触发器输入被选中时(由
TIMx_SMCR寄存器的TS位选择)。

OC1M[2:0]:输出比较1模式 (Output Compare 1 mode)

000:冻结。输出比较寄存器TIMx_CCR1与计数器TIMx_CNT间的比较对OC1REF不起作用;
001 :匹配时设置通道 1 为有效电平。当计数器 TIMx_CNT 的值与捕获 / 比较寄存器 1
(TIMx_CCR1)相同时,强制OC1REF为高。
010 :匹配时设置通道 1 为无效电平。当计数器 TIMx_CNT 的值与捕获 / 比较寄存器 1
(TIMx_CCR1)相同时,强制OC1REF为低。
011:翻转。当TIMx_CCR1=TIMx_CNT时,翻转OC1REF的电平。
100:强制为无效电平。强制OC1REF为低。
101:强制为有效电平。强制OC1REF为高。
110:PWM模式1- 在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为有效电平,否则为
无效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为无效电平(OC1REF=0),否
则为有效电平(OC1REF=1)。
111:PWM模式2- 在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为无效电平,否则为
有效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为有效电平,否则为无效电
平。
捕获/比较寄存器 1(TIMx_CCR1) :

CCR1[15:0]: 捕获/比较通道1的值 (Capture/Compare 1 value)
若CC1通道配置为输出:
CCR1包含了装入当前捕获/比较1寄存器的值(预装载值)。
若CC1通道配置为输入:
CCR1包含了由上一次输入捕获1事件(IC1)传输的计数器值。
捕获/比较使能寄存器(TIMx_CCER) :

CC1P:输入/捕获1输出极性 (Capture/Compare 1 output polarity)
CC1通道配置为输出:
0:OC1高电平有效;
1:OC1低电平有效。
CC1通道配置为输入:
该位选择是IC1还是IC1的反相信号作为触发或捕获信号。
0:不反相:捕获发生在IC1的上升沿;当用作外部触发器时,IC1不反相。
1:反相:捕获发生在IC1的下降沿;当用作外部触发器时,IC1反相。
CC1E:输入/捕获1输出使能 (Capture/Compare 1 output enable)
CC1通道配置为输出:
0: 关闭- OC1禁止输出
1: 开启- OC1信号输出到对应的输出引脚
CC1通道配置为输入:
该位决定了计数器的值是否能捕获入TIMx_CCR1寄存器。
0:捕获禁止。
0:捕获使能。
控制寄存器 1(TIMx_CR1) :

DIR:方向 (Direction)
0:计数器向上计数;
1:计数器向下计数
CEN:使能计数器 (Counter enable)
从模式控制寄存器(TIMx_SMCR) :

SMS[2:0]:从模式选择 (Slave mode selection)
当选择了外部信号,触发信号(TRGI)的有效边沿与选中的外部输入极性相关(见输入控制寄存器
和控制寄存器的说明)
000:关闭从模式 – 如果CEN=1,则预分频器直接由内部时钟驱动。
001:编码器模式1 – 根据TI1FP1的电平,计数器在TI2FP2的边沿向上/下计数。
010:编码器模式2 – 根据TI2FP2的电平,计数器在TI1FP1的边沿向上/下计数。
011:编码器模式3 – 根据另一个信号的输入电平,计数器在TI1FP1和TI2FP2的边沿向上/下计
数。
100:复位模式 – 选中的触发输入(TRGI)的上升沿重新初始化计数器,并且产生一个更新寄存
器的信号。
101:门控模式 – 当触发输入(TRGI)为高时,计数器的时钟开启。一旦触发输入变为低,则计
数器停止(但不复位)。计数器的启动和停止都是受控的。
110:触发模式 – 计数器在触发输入TRGI的上升沿启动(但不复位),只有计数器的启动是受控
的。
111:外部时钟模式1 – 选中的触发输入(TRGI)的上升沿驱动计数器。
注:如果TI1F_EN被选为触发输入(TS=100)时,不要使用门控模式。这是因为,TI1F_ED在每
次TI1F变化时输出一个脉冲,然而门控模式是要检查触发输入的电平。
TS[2:0]:触发选择 (Trigger selection)
这3位选择用于同步计数器的触发输入。
000:内部触发0(ITR0),TIM1
001:内部触发1(ITR1),TIM2
010:内部触发2(ITR2),TIM3
011:内部触发3(ITR3),TIM4
100:TI1的边沿检测器(TI1F_ED)
**101:滤波后的定时器输入1(TI1FP1) **
110:滤波后的定时器输入2(TI2FP2)
111:外部触发输入(ETRF)
预分频器(TIMx_PSC) :

PSC[15:0]:预分频器的值 (Prescaler value)
自动重装载寄存器(TIMx_ARR) :

ARR[15:0]: 自动重装载的值 (Prescaler value)
5.3.3.1 案例2—测周期频率HAL库相关函数
//设置CCR的值
__HAL_TIM_SetCompare(&htim5,TIM_CHANNEL_2,dutycycle);
//获取CCR的值
__HAL_TIM_GetCompare(&htim4,TIM_CHANNEL_1)

    //输入捕获的中断服务函数
    void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
    //设置CNT的值
    __HAL_TIM_SetCounter(&htim4,0);

/*1.开启TIM4和TIM5*/
  HAL_TIM_IC_Start_IT(&htim4, TIM_CHANNEL_1);
  HAL_TIM_PWM_Start(&htim5, TIM_CHANNEL_2);




5.3.3.2 CNT置零(硬)和CNT赋值给CCR(软)问题



double Driver_TIM4_GetCycle(void)
{
    return TIM4->CCR1 / 1000.0;
}

void TIM4_IRQHandler(void)
{
    /*1.清除标志位*/
    TIM4->SR &= ~TIM_SR_CC1IF;

        TIM4->CNT = 0;   
}


如图:在上升沿来临时,CNT的值由硬件先给CCR,再进入中断将CNT置零。

5.3.4 输入捕获
捕获/比较模式寄存器 1(TIMx_CCMR1) :

CC1S[1:0]:捕获/比较1选择 (Capture/Compare 1 Selection)
这2位定义通道的方向(输入/输出),及输入脚的选择:
00:CC1通道被配置为输出;
01:CC1通道被配置为输入,IC1映射在TI1上;
10:CC1通道被配置为输入,IC1映射在TI2上;
11:CC1通道被配置为输入,IC1映射在TRC上。
IC1PSC[1:0]:输入/捕获1预分频器 (Input capture 1 prescaler)
这2位定义了CC1输入(IC1)的预分频系数。
一旦CC1E=0(TIMx_CCER寄存器中),则预分频器复位。
00:无预分频器,捕获输入口上检测到的每一个边沿都触发一次捕获;

IC1F[3:0]:输入捕获1滤波器 (Input capture 1 filter)
这几位定义了TI1输入的采样频率及数字滤波器长度。数字滤波器由一个事件计数器组成,它记
录到N个事件后会产生一个输出的跳变:
**0000:无滤波器,以fDTS采样 1000:采样频率fSAMPLING=fDTS/8,N=6 **

捕获/比较使能寄存器(TIMx_CCER) :

CC1E:输入/捕获1输出使能 (Capture/Compare 1 output enable)



CC1通道配置为输入:
该位决定了计数器的值是否能捕获入TIMx_CCR1寄存器。

0:捕获禁止;
1:捕获使能。
CC1P:输入/捕获1输出极性 (Capture/Compare 1 output polarity)

CC1通道配置为输入:
该位选择是IC1还是IC1的反相信号作为触发或捕获信号。

0:不反相:捕获发生在IC1的上升沿;当用作外部触发器时,IC1不反相。

1:反相:捕获发生在IC1的下降沿;当用作外部触发器时,IC1反相。

控制寄存器 1(TIMx_CR1) :

DIR:方向 (Direction)
0:计数器向上计数;
1:计数器向下计数。
CEN:使能计数器 (Counter enable)
0:禁止计数器;
1:使能计数器。
控制寄存器 2(TIMx_CR2) :

TI1S:TI1选择 (TI1 selection)
0:TIMx_CH1引脚连到TI1输入;
1:TIMx_CH1、TIMx_CH2和TIMx_CH3引脚经异或后连到TI1输入。
DMA/中断使能寄存器(TIMx_DIER) :

CC1IE:允许捕获/比较1中断 (Capture/Compare 1 interrupt enable)
0:禁止捕获/比较1中断;
1:允许捕获/比较1中断。
**捕获/比较寄存器 1(TIMx_CCR1) **

CCR1[15:0]: 捕获/比较通道1的值 (Capture/Compare 1 value)
若CC1通道配置为输出:
CCR1包含了装入当前捕获/比较1寄存器的值(预装载值)。
若CC1通道配置为输入:
CCR1包含了由上一次输入捕获1事件(IC1)传输的计数器值。
状态寄存器(TIMx_SR) :

CC1IF:捕获/比较1中断标记 (Capture/Compare 1 interrupt flag)
预分频器(TIMx_PSC) :

PSC[15:0]:预分频器的值 (Prescaler value) ---- MAX: 65535
自动重装载寄存器(TIMx_ARR):

ARR[15:0]: 自动重装载的值 (Prescaler value) ---- MAX: 65535
5.3.4.1 案例3—测占空比HAL库相关函数
/*1. 开启TIM4*/

HAL_TIM_IC_Start(&htim4,TIM_CHANNEL_1);

HAL_TIM_IC_Start(&htim4,TIM_CHANNEL_2);

/*1. 开启TIM5*/

HAL_TIM_PWM_Start(&htim5,TIM_CHANNEL_2);

//返回占空比
  return (__HAL_TIM_GetCompare(&htim4,TIM_CHANNEL_2) * 1.0) / __HAL_TIM_GetCompare(&htim4,TIM_CHANNEL_1);



5.3.5 从模式
从模式就是控制CNT的,因此我们可以用从模式代替中断



5.4 高级定时器
5.4.1 高级定时器框图



相比通用定时器增加的功能 :

重复计数器
死区时间可编程的互补输出
断路输入信号(刹车信号)
5.4.2 相关寄存器
时基部分
**预分频器(**TIMx_PSC):

PSC[15:0]:预分频器的值 (Prescaler value)
自动重装载寄存器(TIMx_ARR) :

ARR[15:0]: 自动重装载的值 (Prescaler value)
控制寄存器 1(TIMx_CR1) :

DIR:方向 (Direction)
0:计数器向上计数;
1:计数器向下计数。
URS:更新请求源 (Update request source)
0:如果使能了更新中断或DMA请求,则下述任一事件产生更新中断或DMA请求:
− 计数器溢出/下溢
− 设置UG位
− 从模式控制器产生的更新
1:如果使能了更新中断或DMA请求,则只有计数器溢出/下溢才产生更新中断或DMA请求。
CEN:使能计数器 (Counter enable)
0:禁止计数器;
1:使能计数器。
重复计数寄存器(TIMx_RCR) :

REP[7:0]: 重复计数器的值 (Repetition counter value)
输出比较部分

捕获/比较模式寄存器 1(TIMx_CCMR1) :

CC1S[1:0]:捕获/比较1 选择。(Capture/Compare 1 selection)
这2位定义通道的方向(输入/输出),及输入脚的选择:
00:CC1通道被配置为输出;
01:CC1通道被配置为输入,IC1映射在TI1上;
10:CC1通道被配置为输入,IC1映射在TI2上;
11:CC1通道被配置为输入,IC1映射在TRC上。此模式仅工作在内部触发器输入被选中时(由
TIMx_SMCR寄存器的TS位选择)。
OC1M[2:0]:输出比较1模式 (Output Compare 1 mode)

110:PWM模式1- 在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为有效电平,否则为
无效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为无效电平(OC1REF=0),否
则为有效电平(OC1REF=1)。
111:PWM模式2- 在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为无效电平,否则为
有效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为有效电平,否则为无效电
平。
捕获/比较寄存器 1(TIMx_CCR1) :

CCR1[15:0]: 捕获/比较通道1的值
捕获/比较使能寄存器(TIMx_CCER) :

CC1E:输入/捕获1输出使能
CC1P:输入/捕获1输出极性 (Capture/Compare 1 output polarity)
CC1通道配置为输出:
0:OC1高电平有效;
1:OC1低电平有效。
CC1通道配置为输入:
该位选择是IC1还是IC1的反相信号作为触发或捕获信号。
0:不反相:捕获发生在IC1的上升沿;当用作外部触发器时,IC1不反相。
1:反相:捕获发生在IC1的下降沿;当用作外部触发器时,IC1反相。
高级定时器独有部分

重复计数寄存器(TIMx_RCR) :

REP[7:0]: 重复计数器的值 (Repetition counter value)
刹车和死区寄存器(TIMx_BDTR):

MOE: 主输出使能 (Main output enable)
通用部分

控制寄存器 1(TIMx_CR1) :



URS:更新请求源 (Update request source)
0:如果使能了更新中断或DMA请求,则下述任一事件产生更新中断或DMA请求:
− 计数器溢出/下溢
− 设置UG位
− 从模式控制器产生的更新
1:如果使能了更新中断或DMA请求,则只有计数器溢出/下溢才产生更新中断或DMA请求。
事件产生寄存器(TIMx_EGR):

UG:产生更新事件 (Update generation)

该位由软件置’1’,由硬件自动清0。

0:无动作;
**1:重新初始化计数器,并产生一个更新事件。**注意预分频器的计数器也被清’0’(但是预分频系
数不变)。若在中心对称模式下或DIR=0(向上计数)则计数器被清’0’;若DIR=1(向下计数)则计数
器取TIMx_ARR的值。
DMA/中断使能寄存器(TIMx_DIER) :

UIE:允许更新中断 (Update interrupt enable)
0:禁止更新中断;
1:允许更新中断。
状态寄存器(TIMx_SR) :

UIF:更新中断标记 (Update interrupt flag)
5.4.3 实验:输出有限个周期的PWM波 — HAL库
//开启事件更新中断
__HAL_TIM_ENABLE_IT(&htim1, TIM_IT_UPDATE);
//开启PWM脉冲
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
//产生更新事件,消除才上电第一次频率太快(才上电用的是系统72M时钟)导致速度太快直接进中断
  __HAL_TIM_URS_ENABLE(&htim1);

//中断处理函数---更新事件处理
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    //如果有多个定时器则需要确认是哪个定时器进入中断
  if (htim->Instance == TIM1)
  {
    __HAL_TIM_DISABLE_IT(&htim1, TIM_IT_UPDATE);
    HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_1);
  }
}



6 DMA
6.1 DMA基础图解
6.1.1 DMA框图



6.1.2 DMA各通道请求一览表





6.2 相关寄存器
DMA通道x配置寄存器(DMA_CCRx)(x = 1…7) :

MEM2MEM:存储器到存储器模式 (Memory to memory mode)
该位由软件设置和清除。
0:非存储器到存储器模式;
1:启动存储器到存储器模式
PL[1:0]:通道优先级 (Channel priority level)
这些位由软件设置和清除。
00:低
01:中
10:高
11:最高
MSIZE[1:0]:存储器数据宽度 (Memory size)
这些位由软件设置和清除。
00:8位
01:16位
10:32位
11:保留
PSIZE[1:0]:外设数据宽度 (Peripheral size)
MINC:存储器地址增量模式 (Memory increment mode)
PINC:外设地址增量模式 (Peripheral increment mode)
CIRC:循环模式 (Circular mode)
DIR:数据传输方向 (Data transfer direction)
该位由软件设置和清除。
0:从外设读
1:从存储器读
TCIE:允许传输完成中断 (Transfer complete interrupt enable)
EN:通道开启 (Channel enable)
DMA中断状态寄存器(DMA_ISR) :

TCIFx:通道x的传输完成标志(x = 1 … 7) (Channel x transfer complete flag)
硬件设置这些位。在DMA_IFCR寄存器的相应位写入’1’可以清除这里对应的标志位。
0:在通道x没有传输完成事件(TC);
1:在通道x产生了传输完成事件(TC)。
DMA中断标志清除寄存器(DMA_IFCR):

CTCIFx:清除通道x的传输完成标志(x = 1 … 7) (Channel x transfer complete clear)
这些位由软件设置和清除。
0:不起作用
1:清除DMA_ISR寄存器中的对应TCIF标志。
DMA通道x传输数量寄存器(DMA_CNDTRx)(x = 1…7) :

NDT[15:0]:数据传输数量 (Number of data to transfer)
DMA通道x外设地址寄存器(DMA_CPARx)(x = 1…7) :

PA[31:0]:外设地址 (Peripheral address)
DMA通道x存储器地址寄存器(DMA_CMARx)(x = 1…7) :

MA[31:0]:存储器地址
6.3 HAL库相关函数
uint8_t data[5] = {'a','b','c','d','e'};
  HAL_UART_Transmit_DMA(&huart1,data,5);
1
2
6.4 ROM 与 SRAM



ROM:指令,常量 0x8000000

SRAM:变量 0x20000000

7 ADC
7.1 基本介绍



ADC1~ADC3都搭载了APB2高速时钟总线,ADC的输入时钟不得超过14MHz

中断:

通道转换结束中断(EOC):注入组和规则组转换结束都会产生
注入转换通道转换结束中断(JEOC):只有注入组转换结束会产生
模拟看门狗中断。

DMA 请求:只有ADC1和ADC3可以产生DMA请求。

数据对齐:

16位的寄存器只用到了其中的12位。高12位左对齐。(注入组一般左对齐)
低12位右对齐。(规则组一般右对其)
ADC的输入时钟不得超过14MHz,它是由PCLK2经分频产生。所以最低分频系数为6

数据寄存器:注入数据寄存器(4组 × 16位)、规则数据寄存器(1组 × 16位)

每个ADC最多有16个通道和2个内部信号源总共18个(所以需要5位× 18 / 32 = 2.8几,SQR1 ~SQR3 三个寄存器来配置)。规则组一次最多放16个通道,注入组一次最多放4个通道。

开启转换方式有两种:

一是ADC1->CR2 |= ADC_CR2_ADON;完成上电后再次ADC1->CR2 |= ADC_CR2_ADON;

二是

//1 开启规则组的外部转换
ADC1->CR2 |= ADC_CR2_EXTTRIG;
//2 选择使用软件触发ADC
ADC1->CR2 |= ADC_CR2_EXTSEL;
//3 开始转换规则通道
ADC1->CR2 |= ADC_CR2_SWSTART;


7.2 相关寄存器
时钟配置

时钟配置寄存器(RCC_CFGR) :

ADCPRE[1:0]:ADC预分频 (ADC prescaler)
由软件置’1’或清’0’来确定ADC时钟频率
00:PCLK2 2分频后作为ADC时钟
01:PCLK2 4分频后作为ADC时钟
10:PCLK2 6分频后作为ADC时钟
11:PCLK2 8分频后作为ADC时钟
ADC配置

ADC控制寄存器 1(ADC_CR1) :

SCAN:扫描模式 (Scan mode)
0:关闭扫描模式;
1:使用扫描模式。
ADC控制寄存器 2(ADC_CR2) :

CONT:连续转换 (Continuous conversion)
该位由软件设置和清除。如果设置了此位,则转换将连续进行直到该位被清除。
0:单次转换模式;
1:连续转换模式。
ALIGN:数据对齐 (Data alignment)
0:右对齐;
1:左对齐。
ADC采样时间寄存器 1(ADC_SMPR1) :

SMPx[2:0]:选择通道x的采样时间 (Channel x Sample time selection)
这些位用于独立地选择每个通道的采样时间。在采样周期中通道选择位必须保持不变。

**000:1.5周期 **

001:7.5周期



ADC规则通道配置

ADC规则序列寄存器 1(ADC_SQR1):

L[3:0]:规则通道序列长度 (Regular channel sequence length)
这些位由软件定义在规则通道转换序列中的通道数目。
0000:1个转换
0001:2个转换
……
1111:16个转换
ADC规则序列寄存器 3(ADC_SQR3) :

SQ1[4:0]:规则序列中的第1个转换
外部软件触发方式及启动转换配置

ADC控制寄存器 2(ADC_CR2):

EXTTRIG:规则通道的外部触发转换模式 (External trigger conversion mode for regular
channels)
该位由软件设置和清除,用于开启或禁止可以启动规则通道组转换的外部触发事件。
0:不用外部事件启动转换;
1:使用外部事件启动转换。
EXTSEL[2:0]:选择启动规则通道组转换的外部事件 (External event select for regular group)
这些位选择用于启动规则通道组转换的外部事件
ADC1和ADC2的触发配置如下

111:SWSTART
ADON:开/关A/D转换器 (A/D converter ON / OFF)
该位由软件设置和清除。当该位为’0’时,写入’1’将把ADC从断电模式下唤醒。
当该位为’1’时,写入’1’将启动转换。应用程序需注意,在转换器上电至转换开始有一个延迟
tSTAB
0:关闭ADC转换/校准,并进入断电模式;
1:开启ADC并启动转换。
CAL:A/D校准 (A/D Calibration)
该位由软件设置以开始校准,并在校准结束时由硬件清除。
0:校准完成;
1:开始校准。
SWSTART:开始转换规则通道 (Start conversion of regular channels)
由软件设置该位以启动转换,转换开始后硬件马上清除此位。如果在EXTSEL[2:0]位中选择了
SWSTART为触发事件,该位用于启动一组规则通道的转换,
0:复位状态;
1:开始转换规则通道。
ADC状态寄存器(ADC_SR):

EOC:转换结束位 (End of conversion)
该位由硬件在(规则或注入)通道组转换结束时设置,由软件清除或由读取ADC_DR时清除
0:转换未完成;
1:转换完成。
ADC规则数据寄存器(ADC_DR):

DATA[15:0]:规则转换的数据 (Regular data)
7.3 HAL库
//校准
  HAL_ADCEx_Calibration_Start(&hadc1);
//启动转换
  HAL_ADC_Start(&hadc1);
//获取模拟值
  HAL_ADC_GetValue(&hadc1) * 3.3 / 4095;



7.4 案例:独立模式多通道采集
void Driver_ADC_Init(void)
{
    /*1.配置时钟 adc时钟要小于14M,6分频*/
    // 由于是模拟输入,直接连接了一根导线,所以该输入引脚不用配置时钟
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
    // 6分频
    RCC->CFGR |= RCC_CFGR_ADCPRE_1;
    RCC->CFGR &= ~RCC_CFGR_ADCPRE_0;

    // 2.配置GPIO工作模式,PC0 --- ADC1-10 模拟输入 MODE:00 , CNF : 00
    GPIOC->CRL &= ~GPIO_CRL_CNF0;
    GPIOC->CRL &= ~GPIO_CRL_MODE0;
    // PC2 --- ADC1-12 模拟输入 MODE:00 , CNF : 00
    GPIOC->CRL &= ~GPIO_CRL_CNF2;
    GPIOC->CRL &= ~GPIO_CRL_MODE2;

    /*3.ADC配置*/
    // 3.1 开启扫描
    ADC1->CR1 |= ADC_CR1_SCAN;
    // 3.2 启用连续转换模式
    ADC1->CR2 |= ADC_CR2_CONT;
    // 3.3 数据对齐方式,这里采用右对齐
    ADC1->CR2 &= ~ADC_CR2_ALIGN;
    // 3.4 设置采样时间 001 7.5个周期
    ADC1->SMPR1 &= ~ADC_SMPR1_SMP10;
    ADC1->SMPR1 |= ADC_SMPR1_SMP10_0;

    ADC1->SMPR1 &= ~ADC_SMPR1_SMP12;
    ADC1->SMPR1 |= ADC_SMPR1_SMP12_0;


    /*4.ADC规则通道配置*/
    // 4.1 配置几个通道道需要配置
    ADC1->SQR1 &= ~ADC_SQR1_L;
    ADC1->SQR1 |= ADC_SQR1_L_0;
    // 4.2 把通道号配置到组里面
    ADC1->SQR3 &= ~ADC_SQR3_SQ1;
    ADC1->SQR3 |= (10 << 0);// 将10号通道配置在最后5位,表示组中第一个通道为10号通道

    ADC1->SQR3 &= ~ADC_SQR3_SQ2;
    ADC1->SQR3 |= (12 << 5);
}



void Driver_ADC_DMA_Start(uint32_t srcAddr,uint32_t destAddr,uint16_t datalen)
{
    /*0 DMA使能*/
    //0.1 设置源地址
    DMA1_Channel1->CPAR = srcAddr;
    //0.2设置目标地址
    DMA1_Channel1->CMAR = destAddr;
    //0.3设置长度
    DMA1_Channel1->CNDTR = datalen;
    //0.4使能DMA
    DMA1_Channel1->CCR |= DMA_CCR1_EN;


     /*1.上电,唤醒ADC。*/
    ADC1->CR2 |= ADC_CR2_ADON;
    /*2.校准*/
    ADC1->CR2 |= ADC_CR2_CAL;
    // 等待校准完成
    while (ADC1->CR2 & ADC_CR2_CAL)
        ;
    /*3.开始转换,并等待转换完成*/
    // ADC1->CR2 |= ADC_CR2_ADON; // 再次上电可完成开启转换功能,仅针对规则通道组
    ADC1->CR2 |= ADC_CR2_SWSTART;
    while (!(ADC1->SR & ADC_SR_EOC))
        ;
}



HAL库相关函数
        //校准
        HAL_ADCEx_Calibration_Start(&hadc1);
        //带DMA的ADC启动
          HAL_ADC_Start_DMA(&hadc1,(uint32_t *)arr,2);


8 SPI
8.1 W25Q32框图



1.SPI是一种高速同步、全双工、串行的通信方式
2.用于通信的4条线:
a.NSS(CS/SS) 片选信号线
b.时钟线
c.MOSI:主模式输出,从模式输入
d.MISO:主模式输入,从模式输出
3.只能主机与从机进行通信,从机之间不能互相通信
4.通信本质就是交换数据

该Flash芯片大小为4M,2^22。但我们一般给他24位,高两位保留。

块号:(21~16)2^6 = 64; 块大小为64K

段号:(15~12) 2^4 = 16; 段大小为4K

页地址:(11~8) 2^4 = 16; 页大小为256Byte

业内地址:(7~0) 2^8 = 256;

通信本质是交换数据
一个int32位,与低位做运算时,低位直接被提升到32位。
SPI配置波特率频率的时候建议不要超过18M
写操作时注意事项
(1)写入操作前,必须先进行写使能。

(2)每个数据位只能由1改写为0,不能由0改写为1。(所以若只想读取数据可以在交换函数里发送0xff)

(3)写入数据前必须先檫除原因参考第2点,檫除后,所有数据位变为1。擦除必须按最小擦除单元进行。

(4)连续写入多字节时,最多写入一页的数据,超过页尾位置的数据,会回到页首覆盖写入。

(5)写入操作结束后,芯片进入忙状态,不响应新的读写操作。

读操作时注意事项
(1)直接调用读取时序,无需读使能,无需额外操作,没有页的限制。

(2) 读取操作结束后不会进入忙状态,但不能在忙状态时读取。

8.2 读写命令





8.3 SPI时序图



8.4 相关寄存器
CR1:

MSTR:主设备选择 (Master selection)

0:配置为从设备;
1:配置为主设备。
BR[2:0]:波特率控制 (Baud rate control)

000: fPCLK/2

001: fPCLK/4



CPHA:时钟相位 (Clock phase)

0: 数据采样从第一个时钟边沿开始;
1: 数据采样从第二个时钟边沿开始。
CPOL:时钟极性 (Clock polarity)

0: 空闲状态时,SCK保持低电平;
1: 空闲状态时,SCK保持高电平。
DFF:数据帧格式 (Data frame format)

0:使用8位数据帧格式进行发送/接收;
1:使用16位数据帧格式进行发送/接收。
LSBFIRST:帧格式 (Frame format)

0:先发送MSB;
1:先发送LSB。
SSM:软件从设备管理 (Software slave management)
当SSM被置位时,NSS引脚上的电平由SSI位的值决定。

0:禁止软件从设备管理;
1:启用软件从设备管理。
SSI:内部从设备选择 (Internal slave select)
该位只在SSM位为’1’时有意义。它决定了NSS上的电平,在NSS引脚上的I/O操作无效。

SPE:SPI使能 (SPI enable)

SR:

TXE:发送缓冲为空 (Transmit buffer empty)
RXNE:接收缓冲非空 (Receive buffer not empty)
DR:

DR[15:0]:数据寄存器 (Data register)
HAL库相关函数
//写引脚高低电平
  HAL_GPIO_WritePin(CS_GPIO_Port,CS_Pin,GPIO_PIN_RESET);
//SPI通信交换数据
  HAL_SPI_TransmitReceive(&hspi1,&byte,&rbyte,1,HAL_MAX_DELAY);



8.5 SPI外设框图



9 存储
STM32片内SRAM(64K)



STM32片内Flash(最大可达2M),我们这块512K



STM32最大寻址能力 4G
数据字节以小端格式(先存低位再存高位)存放在存储器中。



10 FSMC
10.1 功能框图





10.2 IS62WV51216
大小为:512 × 16位 = 1M
10.2.1 地址映射范围
64MB:FSMC_Bank1_NORSRAM1:0X6000 0000 ~ 0X63FF FFFF

64MB:FSMC_Bank1_NORSRAM2:0X6400 0000 ~ 0X67FF FFFF

64MB:FSMC_Bank1_NORSRAM3:0X6800 0000 ~ 0X6BFF FFFF

64MB:FSMC_Bank1_NORSRAM4:0X6C00 0000 ~ 0X6FFF FFFF

NE3对应的地址范围就是0x6800 0000 ~ 0x6BFF FFFF

10.3 寄存器



控制寄存器

MBKEN:存储器块使能位 (Memory bank enable bit)
MTYP:存储器类型 (Memory type)
定义外部存储器的类型:
00:SRAM、ROM(存储器块2…4在复位后的默认值)
01:PSRAM(Cellular RAM: CRAM)
10:NOR闪存(存储器块1在复位后的默认值)
FACCEN:闪存访问使能 (Flash access enable)
MUXEN:地址/数据复用使能位 (Address/data multiplexing enable bit)
MWID:存储器数据总线宽度 (Memory databus width)
定义外部存储器总线的宽度,适用于所有类型的存储器。
00:8位,
01:16位(复位后的默认状态)
WREN:写使能位 (Write enable bit)
时序寄存器

ADDSET:地址建立时间 (Address setup phase duration)
这些位定义地址的建立时间(见图162至图174),适用于SRAM、ROM和异步总线复用模式的
NOR闪存操作。

0000:ADDSET建立时间=1个HCLK时钟周期
……
1111:ADDSET建立时间=16个HCLK时钟周期(这是复位后的默认数值)
对于每一种存储器类型和访问方式的地址建立时间,请参考对应的图表(见图162至图174)。
例如:模式2、读操作、ADDSET=1:地址建立时间=ADDSET+1=2个HCLK时钟周期
注:在同步操作中,这个参数不起作用,地址建立时间始终是1个存储器时钟周期。

DATAST:数据保持时间 (Data-phase duration)
这些位定义数据的保持时间(见图162至图174),适用于SRAM、ROM和异步总线复用模式的
NOR闪存操作。

0000 0000:保留
0000 0001:DATAST保持时间=2个HCLK时钟周期
0000 0010:DATAST保持时间=3个HCLK时钟周期
……
1111 1111:DATAST保持时间=256个HCLK时钟周期(这是复位后的默认数值)。
对于每一种存储器类型和访问方式的数据保持时间,请参考对应的图表。
例如:模式1、读操作、DATAST=1:数据保持时间=DATAST+3=4个HCLK时钟周期。
11 LCD
我们使用的液晶显示模块是Z350IT002
分辨率为 320 × 480
液晶模块Z350IT002内部使用的控制芯片是:ILI9486
另外ILI9486支持多种接口类型,包括并行CPU数据总线接口,支持8位、9位、16位和18位。我们固定用的16位并行8080接口。



扩展LCD的时候,使用的是块1的地址,一共4×64MB = 256MB,每部分的地址如下:

64MB:FSMC_Bank1_NORSRAM1:0X6000 0000 ~ 0X63FF FFFF

64MB:FSMC_Bank1_NORSRAM2:0X6400 0000 ~ 0X67FF FFFF

64MB:FSMC_Bank1_NORSRAM3:0X6800 0000 ~ 0X6BFF FFFF

64MB:FSMC_Bank1_NORSRAM4:0X6C00 0000 ~ 0X6FFF FFFF

我们选择的是NE4, 所以地址范围是:0X6C00 0000 ~ 0X6FFF FFFF,寄存器的基地址是6C000000。



当A10=0时,表示写命令,所以地址是:0x6C00 0000。(其实后面可以加偶数)

当A10=1时,表示写数据,所以地址是:0x6C00 0000 + 1<<11 = 0x6C00 0800(其实后面可以加偶数)

LCD连接单片机FSMC的引脚

RS是数据或命令选择引脚RS=1写数据,RS=0写命令。接FSMC-A10(PG0)。
RST是LCD复位引脚,低电平复位。接LCD-RST(PG15)。
CS是片选引脚,低电平有效。接FSMC-NE4(PG12)。
LEDK是背光亮度控制引脚。通过LCD-BG(PB0)
// 3.遍历当前要显示图片的字符数组  每两个一起遍历
    for (uint16_t i = 0; i < 3200; i += 2)
    {
        // 拼接色彩数据 16位  低位在前
        uint16_t byte = gImage_gimage_qq + (gImage_gimage_qq[i + 1] << 8);
        Inf_LCD_WriteData(byte);
    }
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/m0_74967868/article/details/143099163

使用特权

评论回复
沙发
gaoyang9992006| | 2024-10-28 15:47 | 只看该作者
STM32的外设庞大无比,但是很多时候用不了那么多,其实不要害怕

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1931

主题

15650

帖子

12

粉丝