GuoXin Li's Blog

Cortex-M3(STM32F107)_Learning_Notes

字数统计: 11.9k阅读时长: 44 min
2018/11/21 Share

Cortex-M3

概述

  • ARM 是一个公司,一种技术,一类处理器
  • CM3 内核采用哈佛结构,拥有独立的32位指令总线和数据总线
  • CM3 内核不再支持 ARM 指令集,而只支持 Thumb-2 和 Thumb 指令集
  • STM32 系列 MCU 由意法半导体 (ST)公司设计和制造
  • CMSIS 是 ARM 公司和一些编译器厂家以及半导体厂家共同遵循的一套标准,是由 ARM 公司提出,专门针对 Cortex-M 系列的标准
  • STM32 固件库是一个函数包,它由程序、数据结构和宏组成,包括了 STM32 系列 MCU 标准外设的性能特征
  • 采用调用 STM32 固件库的编程方法,既可以较快地掌握外设的使用,又可以加快学习和开发的速度
  • MDK-ARM 开发工具源自德国

总结

  • Cortex-M3 内核支持的汇编指令集有2种
  • CM3内核通过3条总线与其他外设相连,分别是:
    • I-code 总线连 Flash 存储器接口
    • D-code 总线(负责数据访问操作)
    • 系统总线(连接总线矩阵)
  • Cortex内核三个系列和特点:
    • A 系列(应用程序系列):A 系列在 MMU(内存管理单元)、用于多媒体应用程序的可选 NEON (加速多媒体和信号处理算法)处理单元以及支持半精度和精度运算的高级硬件浮点单元的基础上实现了虚拟内存系统架构,适用于高端消费电子设备、网络设备、移动 Internet 设备和企业市场
    • R 系列(实时型):R 系列在 MPU (内存保护单元)的基础上实现了受保护的内存系统架构。适用于高性能实时控制系统(包括汽车和大容量存储设备)例如:硬盘驱动器和汽车系统的电子控制单元
    • M 系列(微控制器型):该系列可快速进行中断处理,适用于需要高度确定的行为和门数最少的成本敏感型设备。主要用于取代“旧时代”的单片机,面向嵌入式以及工业控制行业
  • STM32 固件库组成:
    • _htmresc:含有 CMSIS 和 ST 的 logo
    • Libraries:
      • CMSIS:包含 CM3 的相关说明和代码,内核级和设备级代码
      • STM32F10x_StdPerph_Driver:
        • inc:含有标准外设的头文件
        • src:含有标准外设的函数源文件
    • Project:
      • STM32F10x_StdPerph_Examples:文件夹含有所有标准外设的例程文件
      • STM32F10x_StdPerph_Template:文件夹含有工程模板示例
    • Utilities:意法半导体官方评估板的相关代码和说明
  • 使用固件库的优点:通过使用固件库,无需深入掌握细节,用户可以轻松应用每一个外设,大大减少用户程序编写的时间,进而降低开发成本

STM32F107内核架构

CM3内核架构

  • 哈佛结构
  • 指令总线和数据总线共享一个储存器空间,两条总线的寻址空间总共 4GB
  • 内核提供一个可选的 MPU (存储器保护单元)以应对比较复杂的应用
  • CM3内部还有一些调试组件,用于硬件水平上支持调试操作,如指令断点,数据感测点

CM3处理器主要包括的部件和组件:

  • NVIC(向量中断控制器),它与CM3 内核紧密耦合,是不可分割的一部分,还包括 MPU、SysTick 定时器以及与调试控制器相关的寄存器
  • 三级流水线:流水线的三级分别为:取指、解码和执行
  • 可选的 MPU(存储器保护单元)
  • 调试和跟踪
  • I-Code 总线
  • D-Code 总线
  • 系统总线

存储器组织

  • CM3 支持 4GB 的存储空间,用来映射程序存储器、数据存储器、外设寄存器

堆栈

CM3 使用“向下生长”的堆栈模型,即堆栈指针 SP 指向最后一个被压入堆栈的 32 位数值

电源管理

正常工作电压:2.0~3.6V,实际电路中经常用 3.3V 作为 STM32F107 供压电源。其通过内置的电压调节器提供所需的 1.8V 电源

复位和启动配置

STM32F107 有三种复位方式:系统复位、电源复位和备份区域复位

时钟

STM32F107 有5个时钟:HSI、LSI、HSE、LSE 和 PLL

按照速度划分:

  • HSE(高速外部)
  • HSI(高速内部)
  • PLL(锁相环倍频)
  • LSI(低速内部)
  • LSE(低速外部)

STM32F107 内部有三种不同的时钟源可以用来驱动系统时钟(SYSCLK):HSI 振荡器时钟、HSE 振荡器时钟和 PLL 时钟

注意:要从主内存存储器启动 STM32F107,BOOT1管脚逻辑可以随意,但 BOOT0管教必须置0

复位机制

复位信号 描述
上电复位 在器件上电时,把处理器核心和调试系统一起复位
系统复位 只影响处理器核心、NVIC(与调试相关的除外)以及 MPU(存储器保护单元),
不复位调试系统
测试复位 只复位调试系统

复位序列:

  • 从地址0x0000_0000处读取 MSP (主堆栈栈顶地址)的初始值
  • 从地址 0x0000_0004处读取 PC (程序计数器)的初始值

GPIO 和 AFIO:

概述:

STM32F107有 GPIOA、GPIOB、GPIOC、GPIOD 和 GPIOE 五个16位通用接口,每个 GPIO 端口有16个口线对应的16个管脚。例如:GPIOA 端口有 GPIOA0~GPIOA15共计16个管脚,简写为 PA0~PA15

GPIO 端口的功能,由软件配置为8种模式:

  • 浮空输入:用于不确定高低电平的输入
  • 上拉输入:用于默认为上拉至高电平的输入
  • 输入下拉:用于默认为下拉至低电平的输入
  • 模拟输入:用于模拟量的输入
  • 开漏输出:用于实现电平转换和线与功能的输出
  • 推挽式输出:用于较大功率驱动输出
  • 推挽复用功能:复用功能下的推挽输出
  • 开漏复用功能:复用功能情况下的开漏输出

GPIO 的特点:

  • 复位期间和刚复位后,复用功能未开启,I/O 端口被配置成浮空输入模式
  • 所有 GPIO 引脚有一个内部弱上拉和弱下拉,当配置为输入时,它们可以被激活也可以被断开
  • 可以以推挽模式或开漏模式使用输出驱动器
  • 当作为输出配置时,写到输出数据寄存器上的值(GPIOx_ODR)输出到相应的 I/O 管脚

GPIO 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void GPIO_Config()
{
/*定义一个 GPIO_InitTypeDef 类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;

/*使能 GPIOC 口的时钟,相当于给 GPIOC 供电,使能时钟是外设工作前的第一步*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

/*选择管脚6*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;

/*选择管脚速度为50MHz*/
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

/*配置 PD6管脚为推挽输出模式*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

/*按照 GPIO_InitStructure 的配置进行初始化*/
GPIO_Init(GPIOC,&GPIO_InitStructure);

}
  • 为了优化64脚或者100脚封装的外设数目,可以把一些复用功能重新映射到指定的管脚上
  • 设置 AFIO 可以实现管脚的重新映射,重新映射后原来的管脚将不再具有该功能
  • 重映射功能只需要调用函数库 GPIO_PinRemapConfig() 开启和关闭即可

简述 IO 的通用功能、复用功能和复用功能重映射之间的联系和区别:

  • GPIO (General Purpose Input Output)通用功能:MCU(微控制单元)与外部电路和设备连接的基本外设,即常说的端口或管脚,GPIO 主要用于通用的功能,常用于连接 LED 显示、驱动蜂鸣器、检测按键等
  • 复用功能(Alternate Function):是指某些 GPIO 除了通用功能外还可以设置为一些外设专用的功能。这样做可以减少管脚数目并且根据需求灵活配置
  • 复用功能重映射:可以把一些“复用功能”重映射到“指定的引脚”上。原来的引脚便不再具有该功能。直接好处:PCB电路板的设计人员可以在需要的情况下,不必把某些信号在板上绕一大圈完成联接,方便了PCB的设计同时潜在地减少了信号的交叉干扰。

NVIC 和 EXTI

概述:

  • NVIC 是一个总的中断控制器,其中1~15号对应的系统异常,大于等于16号的全部是外部中断
  • 不管是来自 CM3 内部的异常还是外部的中断都将进入该控制器进行处理和逻辑判断
  • STM32F107 的异常响应系统是 CM3 的裁剪和细化
  • EXTI 由20个产生事件/中断请求的边沿检测器组成

中断优先级

  • NVIC支持中断嵌套,使得高优先级异常会抢占低优先级异常

  • 系统异常有 3 个:

    • 复位
    • NMI(不可屏蔽中断)
    • 硬件失效(Hard Fault)
  • 抢占优先级和从优先级

    • MSB:所在的位段对应抢占优先级,抢占优先级决定了抢占行为
    • LSB:所在的位段对应从优先级,从优先级则处理“内务”
  • STM32F107采用最高有效位对齐,在设计时裁掉表达优先级的4个低端有效位,所以只支持16级优先级

Priority Bits 优先级有效位

中断输入和悬起:

  • 当中断输入脚被置为有效后,该中断就被“悬起”
  • 所谓悬起就是等待、就绪的意思。(即使后来中断撤销了中断请求,已经被标记成悬起的中断也被记录了下来,在系统中,当它的优先级最高时,就会得到响应,并获得其服务程序的开始地址)
  • 当某个中断服务开始执行后,就称此中断进入了“活跃”状态,并且其悬起位会被硬件清除,在一个中断活跃后,直到该服务例程执行完毕,并且返回后,该中断才能对新的请求予以响应。

中断响应序列:

当 NVIC 响应一个中断时,会自动完成以下三项工作,以便安全、准确地跳转到相应的中断服务程序

  • 入栈:把8个寄存器的值压入栈
  • 取向量:从向量表中找出对应的服务程序入口地址
  • 更新寄存器:选择堆栈指针 MSP/PSP,更新堆栈指针 SP,更新连接寄存器 LR,更新程序计数器 PC

中断返回

  • 出栈: 把先前压入栈中的寄存器在这里恢复。内部出栈顺序与入栈顺序相对应,堆栈指针的值也改回先前的值
  • 更新 NVIC 寄存器: 伴随着中断的返回,它的活动位也被硬件清除。对于外部中断,倘若中断输入再次被置为有效,则悬起位也将再次置位,新一次的中断响应序列可随之再次开始

中断嵌套

  • NVIC 和 CM3 处理器会根据优先级的设置来控制抢占与嵌套行为
  • 有了自动入栈和出栈,不用担心在中断发生嵌套时,会使寄存器的数据损毁

SysTick 定时器

SysTick 定时器被捆绑在 NVIC 中,用于产生 SysTick 异常(异常号为15)

中断向量—(异常)向量表

  • 缺省情况下,CM3 认为(异常)向量表位于零地址处,且各向量占用 4 字节
  • 在STM32F107中,这种映射关系具体体现在启动代码startup_stm32f10x_cl.s文件中

NVIC 函数库配置

NVIC 涉及的库函数存放在 misc.c 文件中,并非类似 stm32f10x_nvic.c 这样的命名,这是因为 NVIC 属于 CM3 内核部件,遵从 CMSIS 接口协议。就其函数库来看,NVIC 主要是设置中断优先级,以达到控制其运行顺序及嵌套等功能

配置代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void NVIC_Config(void)
{
/*定义一个 NVIC_InitTypeDef 类型的结构体,该类型在 misc.c 中定义*/
NVIC_InitTypeDef NVIC_InitStructure;

/*在 NVIC_InitStructure 中配置中断通道为 EXTI3*/
NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;

/*在 NVIC_InitStructure 中配置中断抢占优先级为7*/
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x07;

/*在 NVIC_InitStructure 中配置从优先级为7*/
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x07;

/*在 NVIC_InitStructure 中使能该中断*/
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

/*NVIC_Init 函数根据 NVIC_InitStructure 设置进行 NVIC 初始化*/
NVIC_Init(&NVIC_INitStructure);
}

外部中断/事件控制器

外部中断/事件控制器EXTI是STM32F107的一个外设,不属于CM3内核的范畴,主要用于外部中断和事件的控制

EXTI 由20个产生中断/事件请求的边沿检测器组成,特点:

  • 每个输入线可以独立的配置输入类型(脉冲或挂起)和对应的触发事件(上升沿、下降沿或者双边沿都触发)
  • 每个输入线都可以独立地被屏蔽
  • 挂起寄存器保持着状态线的中断请求

EXTI 库函数配置:

配置代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void EXTI_Config(void)
{
/* 定义一个EXTI_InitTypeDef类型的结构体,该类型在stm32f10x_exti.h中定义 */
EXTI_InitTypeDef EXTI_InitStructure;

/* 使能AFIO 时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

/* 配置 PD3管脚作为EXTI线3 */
GPIO_EXTILineConfig(GPIO_PortSourceGPIOD, GPIO_PinSource3);

/* 在EXTI中配置 EXTI线3为中断模式,下降沿有效,并使能该配置 */
EXTI_InitStructure.EXTI_Line = EXTI_Line3;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //配置模式为中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿有效
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能该配置
EXTI_Init(&EXTI_InitStructure); //初始化 EXTI
}
  • 判断两个中断优先级高低的规则:

    优先级的数值越小,则优先级越高,中断优先级支持中断嵌套,高优先级的异常会抢占低优先级的异常。

定时器(TIM)

概述:

  • 定时器是MCU中常用的外设,用于实现计数、定时等功能。常用于脉冲计数、时间控制和波形控制等场合。
  • STM32F107有7个通用功能的定时器(还有几个专用的,例如看门狗、RTC),分别是TIM1-TIM7,它们分为三组:高级控制定时器、通用定时器和基本定时器。

注意:TIM1在APB2总线,而其他定时器在APB1总线,当调用库函数开启它们时钟的时候,需要特别注意函数和参数的一致

使能时钟

/ 使能TIM1时钟 /

RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);

/ 使能 TIM3 时钟 /

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

高级和通用定时器TIMx—TIMx主要特性

  • 基本计数、定时功能,计数范围1-65535。

  • 每个定时器有四个独立通道,TIM1还有额外的三个通道可产生互补信号。

  • 适合多种用途,包含测量输入信号的脉冲宽度(输入捕获),或者产生输出波形。

  • 使用定时器预分频器和RCC时钟控制预分频器,可以实现脉冲宽度和波形周期从几个微秒到几个毫秒的调节。

  • 定时器之间完全独立的,它们不共享任何资源,可以同步操作。

时基单元

  • TIMx的16位计数器、预分频器、自动装载寄存器以及重复次数计数器(TIM1特有)组成了“时基单元”

TIMx的三种计数模式

  • 向下计数:计数器从0计数到自动加载值(TIMx_ARR计数器的内容),然后重新从0开始计数并且产生一个计数器溢出事件
  • 向上计数:计数器从自动装入的值(TIMx_ARR计数器的值) 开始向下计数到0 ,然后从自动装入的值重新开始并且产生一个计数器向下溢出事件
  • 中央对齐:计数器从0 开始计数到自动加载的值(TIMx_ARR寄存器)−1 ,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器下溢事件;然后再从0开始重新计数。这种计数模式下,一个周期可以产生对称的两次更新事件,可用于产生50%占空比的方波

计数模式 工作方式

  • 如果使用重复计数器功能,则在溢出次数达到设置的重复计数时,产生更新事件
  • 发生溢出或更新事件后,自动装载影子寄存器将被重新置入预装载寄存器(TIMx_ARR)的值
  • 发生溢出或更新事件后,预分频器的缓冲区被置入预装载寄存器的值(即 TIMx_PSC)
  • 自动装载寄存器在计数器重载入之前被更新,因此在下一个周期才会生效

计数模式配置

定时公式和计数脉冲数量公式

  • 时间 = 计数脉冲数量 x (1、输入时钟频率)
  • 计数脉冲数量 = 时间 x 输入时钟频率

配置具体步骤:

  • 计算预分频数值
  • 计算计数数值
  • 计算重复次数值

代码配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void TIM1_Config(void)
{
/*定义一个 TIM_TimeBaseInitTypeDef 类型的结构体,该类型在 stm32f10x_tim.h 中定义*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

/*使能一下 TIM1 的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);

/*自动装载的计数值*/
TIM_TimeBaseStructure.TIM_Period = (10000-1);

/*预分频系数设置*/
TIM_TimeBaseStructure.TIM_Perscaler = (7200-1);

/*向上计数*/
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

/*重复次数为0,即每一溢出都中断*/
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;

/*时钟分割,用于数字滤波器等,计数模式中无作用*/
TIM_TimeBaseStructure.TIM_ClockDivision = 0;

/*根据设置的参数初始化 TIM1*/
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

/*使能 TIM1 计数器*/
TIM_Cmd(TIM1, ENABLE);

}

输出模式PWM模式

概述:

PWM(Pulse Width Modulation,脉冲宽度调制)是一种对模拟信号电平进行数字编码的方法,它利用脉冲宽度即占空比来表示一个模拟信号电平的高低,是一种数字信号对模拟电路进行控制的非常有效的技术

  • 在STM32F107中,TIMx模块可以产生一个由TIMx_ARR寄存器确定频率、由TIMx_CCR(捕获/比较)寄存器确定占空比的PWM信号

  • 在 STM32F107中,每一个 TIMx 通道都有一个捕获/比较 模块,用于对输入信号进行捕获和设定值进行比较,可实现测量脉冲宽度和控制波形输出等功能

PWM模式配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
void TIM3_Config(void)
{
/* 定义一个TIM_TimeBaseInitTypeDef类型的结构体,该类型在stm32f10x_tim.h中定义 */
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/* 定义一个TIM_OCInitTypeDef类型的结构体,该类型在stm32f10x_tim.h中定义 */
TIM_OCInitTypeDef TIM_OCInitStructure;
/* 使能 TIM3 时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
/* TIM3完全重映射,即映射到PC6,7,8,9 */
GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE);
/* 计算预分频系数 */
PrescalerValue = (uint16_t) (SystemCoreClock / 24000000) - 1;
/* 时基设置,TIM3为向上计数 */
TIM_TimeBaseStructure.TIM_Period = 665;
TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
/* TIM3为PWM1模式,输出使能,溢出值为CCR1_Val */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR1_Val;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
/* 调用通道1初始化程序进行初始化,使能通道1预装载寄存器 */
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
/* 溢出值为CCR2_Val,进行初始化,使能通道2预装载寄存器 */
TIM_OCInitStructure.TIM_Pulse = CCR2_Val;
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
/* 溢出值为CCR3_Val,进行初始化,使能通道3预装载寄存器 */
TIM_OCInitStructure.TIM_Pulse = CCR3_Val;
TIM_OC3Init(TIM3, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);
/* 溢出值为CCR4_Val,进行初始化,使能通道4预装载寄存器 */
TIM_OCInitStructure.TIM_Pulse = CCR4_Val;
TIM_OC4Init(TIM3, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);
}

看门狗

概念:

一种专门用于检测 MCU 程序运行状态的外设或芯片,俗称“看门狗”

  • 喂狗:用户程序中用专门的语句在看门狗达到溢出条件之前,对计数进行重置,以免 MCU 复位

STM32F107内置两个看门狗:

  • 独立看门狗(IWDG):限制喂狗时间在0~x 内,x 由相关寄存器的值决定(即IWDG_RLR),喂狗时间不能过晚;适用于那些需要看门狗作为一个在主程序之外,能够完全独立工作,并且对时间精度要求较低的场合
  • 窗口看门狗(WWDG):其喂狗时间是一个有上、下限的范围,用户可以通过相关寄存器设定上限时间和下限时间;喂狗的时间不能过早,也不能过晚;使用于那些要求看门狗在精确计时窗口起作用的应用程序

独立看门狗(IWDG)

主要性能:

  • 自由运行的递减计数器
  • 时钟由独立的 RC 振荡器提供(可在停止和待机模式下工作)
  • 看门狗被激活后,在计数器计数至0x000时产生复位

IWDG 主要部件:

  • 预分频寄存器(IWDG_PR)及控制的8位预分频器,主要用于将专用的 40KHz 的 LSI 时钟进行分频
  • 状态寄存器(IWDG_SR):用于指示分频值和重装载值的更行状态
  • 重装载寄存器(IWDG_RLR):用于配置看门狗计数器的重装载值
  • 12位递减计数器,用于递减计数,一旦计数至末尾0x000,就产生一个复位信号
  • 键寄存器(IWDG_KR):用于设置键值,特定的键值有特定的用途,例如0xCCCC表示启动看门狗,0xAAAA 表示“喂狗”指令,0x5555表示解除写保护指令

IWDG 的开启和喂狗:

开启:

在键寄存器(IWDG_KR)中写入键值0xCCCC,开始启用独立看门狗;此时计数器开始从其复位值0FFF递减计数。当计数器计数到末尾0x000时,产生一个复位信号(IWDG_RESET)

喂狗:

无论何时,只要在键寄存器(IWDG_KR)中写入0xAAAA,重装载寄存器(IWDG_RLR)中的值就会被重新加载到计数器,以避免看门狗复位(即“喂狗”)

寄存器的写入保护:

STM32F107中的预分频寄存器(IWDG_PR)和重装载寄存器(IWDG_RLR)具有写入保护功能,以防止程序被意外修改,要修改这两个寄存器的值必须先向 IWDG_KR 中写入0x5555,以取消其两个寄存器的写保护

IWDG 的配置步骤

    1. 向 IWDG_KR 中写入0x5555:取消掉 IWDG_PR和 IWDG_RLR 的写保护
    1. 设置 IWDG_PR 的值:设置看门狗的分频系数,本例中为32
    1. 设置 IWDG_RLR 的值:设置看门狗的重装载的值,本例中625
    1. 向 IWDG_KR 中写入0xAAAA,以使 STM32F107重新加载 IWDG_RLR 的值到看门狗计数器中
    1. 向 IWDG_KR 中写入0xCCCC,以启动 STM32F107的看门狗

注意:IWDG 有自己独立的时钟,所以不需要像配置其他外设那样另外开启时钟

代码配置

1
2
3
4
5
6
7
8
9
10
11
12
13
IWDG_Config()
{
/*向 IWDG_KR 中写入0x5555*/
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
/*设置分频系数*/
IWDG_SetPrescaler(IWDG_Perscaler 32);
/*设置重装载的值*/
IWDG_SetReload(625);
/*RLR 重装载寄存器*/
IWDG_ReloadCounter();
/*使能 IWDG */
IWDG_Enable;
}

通用同步/异步收发器(USART)

概述:

USART 通用同步/异步收发器是 MCU 常用外设,常用于 MCU 之间、MCU 与 PC 或 MCU 与其他模块之间的通信。

STM32F107 中有5个通用同步/异步收发器(USART),除了支持异步双工通信外,还支持多种工作模式,并且可以使用多缓冲器配置的 DMA方式实现高速数据通信

USART主要特性:

  • 支持全双工,异步通信
  • NRZ(Not Return Zero,不归零码)标准格式
  • 发送和接受共用分数波特率发生器,可灵活设置波特率,最高达4.5Mb/s
  • 可设置8位或9位数据字长度
  • 可设置1或2个停止位
  • 具有单线半双工通信模式
  • 具有 LIN(局域互联网)功能
  • 同步模式时,作为发送方为同步传输提供时钟
  • 具有 IrDA(红外数据组织)SIR 编码器/解码器模块
  • 具有智能卡模拟功能,接口支持 ISO7816-3标准中定义的异步智能卡协议
  • 可在 SRAM 里使用 DMA缓冲/接受发送字节
  • 可单独开启或关闭发送器和接收器

USART 外设模块有多种标志,具体如下:

  • 三种检测标志:接受缓冲器满、发送缓冲器空和传输结束标志
  • 四个错误检测标志:溢出错误、噪音错误、帧错误和校验错误标志
  • 十个中断源标志:CTS 改变、LIN 断开符检测、发送数据寄存器空、发送完成、接受数据寄存器满、检测到总线为空闲、溢出错误、帧错误、噪音错误和校验错误标志

USART异步模式:

  • 概述:

    异步模式不需要专门的时钟信号来控制数据的收发,因此发送数据时位与位的间隙可以任意改变,虽然需要额外的开销用于定义数据的开始位和停止位等,但可省掉一条时钟线,有很大的实用意义

  • 异步模式下重要的参数和概念,需严格匹配

    • 波特率:指串行通信中每秒传送的位数,如波特率115200,表示每秒传送115200个比特位,其中包含起始位和停止位,即实际传输的位数
    • 数据位:指每一帧数据里包含数据的位数。(通常选择8位)
    • 停止位:作为一帧数据的结束符号
    • 奇偶校验:一种串行通信中简单的校验方式:
      • 奇校验:如果一帧数据以及校验位中的“1”的个数为奇数,则校验位为“0”,否则为“1”
      • 偶校验:如果一帧数据以及校验位中的“1”的个数为偶数,则校验位为“0”,否则为“1”
    • 硬件流控制:常用 RTS和 CTS 流控制,各自占一条线,作为硬件握手信号
  • 分数波特率:stm32F107中,使用分数波特率的概念,即将波特率的数值设置为由4位小数(DIV_Fraction)和12位整数(DIV_Mantissa)组成的16位 USARTDIV,并将其存放在USART_BRR(波特率寄存器)低16位中,USART_BRR 的高16位为保留位,无定义。

    fPCLK是外设的时钟。USART1在 APB2总线,fPLCK 最高可达72MHz;其他 USART 在 APB1总线,fPLCK 最高可达36MHz

    例如:USART1时钟频率为72MHz,波特率115200,则:

    USART 根据上式为39.0625

    从而得到:DIV_Fraction = 16 * 0.0625 = 1 = 0x01

    DIV_Mantissa = 39 = 0x27

    所以完整的 USART_BRR 配置字节:0x0271就可以得到115200的波特率

  • DMA 传输

    直接存储器存取(DMA)用来提供外设和寄存器之间或者存储器和存储器之间的高速数据传输。其接受和发送过程如下:

    • DMA 发送:首先设置 DMA 从指定的 SRAM 区传送数据到 USART_DR,然后通过发送器送出
    • DMA 接受:首先设置 SRAM 区作为 DMA 缓存区,而后每次接受到一个字节,DMA 控制器就把数据从 USART_DR 传送到指定的 DMA 缓存区
  • 中断请求

    为了更好的配合通信,STM32F107的 USART 可以产生多个中断请求。USART 的各种中断向量也被连接到同一个中断向量。

    • 发送期间:发送完成、清除发送、发送数据寄存器空
    • 接受期间:空闲总线检测、溢出错误」接受数据寄存器非空、检验错误、LIN 断开符检测、噪音标志和帧错误

异步模式配置:

下述函数用于实现调用固件库初始化 USART1 为异步模式,波特率9600,8位数据位,1位停止位,偶校验,无硬件流控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
COM_Config()
{
/*声明USART 初始化结构体*/
USART_InitTypeDef USART_InitStructure;

/*开启 USART1时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

/*设置波特率9600*/
USART_InitStructure.USART_BaudRate = 9600;

/*设置8位数据位*/
USART_InitStructure.USART_WordLength = USART_WordLength_8b;

/*设置1位停止位*/
USART_InitStructure.USART_StopBits = USART_StopBits_1;

/*设置偶校验*/
USART_InitStructure.USART_Parity = USART_Parity_Even;

/*设置不开启硬件流控制*/
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

/*设置 USART 模式为收发*/
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

/*按照初始化结构体进行 USART1初始化*/
USART_Init(USART1, &USART_InitStructure);

/*使能 USART1*/
USART_Cmd(USART1, ENABLE);
}

USART编程实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <stdio.h>
void GPIO_Config(void);
void COM_Init(void);

int main(void)
{
uint8_t ch;
GPIO_Config();
COM_Init();
while(1)
{
/*如果 USART2 的接受标志位不为0,即已经接收到数据*/
if(USART_GetFlagStatus(USART2, USART_FLAG_RXNE)!= RESET)
{
/*将 USART2 接受到的数据放入变量 ch 缓存*/
ch = USART_ReceiveData(USART2);
/*通过 USART2 发送 ch 的内容*/
USART_SendData(USART2, ch);
}
}
}

GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_Initstructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOA | RCC_APB2Pefiph_AFIO, ENABLE);

/*重映射 USART2,使能 USART2管脚重映射 */
GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);

/*使能 USART2 时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

/*设置重映射后的 USART2 输出 TX 管脚 PD5为复用推挽*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);

/*设置重映射后的 USART2 输入 RX 管脚 PD6为浮空输入*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructrue.GPIO_Pin = GPIO_Pin_6;
GPIO_Init(GPIOD, &GPIO_InitStructure);

}

COM_Init(void)
{
/*声明USART 初始化结构体*/
USART_InitTypeDef USART_InitStructure;

/*设置波特率115200*/
USART_InitStructure.USART_BaudRate = 115200;

/*设置8位数据位*/
USART_InitStructure.USART_WordLength = USART_WordLength_8b;

/*设置1位停止位*/
USART_InitStructure.USART_StopBits = USART_StopBits_1;

/*设置无校验*/
USART_InitStructure.USART_Parity = USART_Parity_None;

/*设置不开启硬件流控制*/
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

/*设置 USART 模式为收发*/
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

/*按照初始化结构体进行 USART1初始化*/
USART_Init(USART1, &USART_InitStructure);

/*使能 USART1*/
USART_Cmd(USART1, ENABLE);
}

总结:

  • STM32F107共有 5 个通用异步收发器
  • 9600波特率是指每秒钟传输 9600 位
  • 三线制串口需要的三根线分别是:发送、接受和接地
  • 简述异步模式与同步模式的区别
    • 看收发双方的时钟是否是同步的,同步即为同步模式,否则即为异步模式下
    • 同步方式的不同,同步模式与异步模式下相对应,数据的每一位间隙都有严格规定,否则会造成数据错位

直接寄存器访问(DMA)

功能描述:

DMA(Direct Memory Access,直接寄存器访问)是一种提供在外设和存储器(内存)之间或者存储器和存储器之间高速数据传输的数据。大块数据可以通过 DMA 快速地移动,无需 CPU干预,从而节省了 CPU 的资源。因此,DMA 可以在数据采集、数据移动和周期性数据处理等方面有效的降低 CPU 负荷,使系统运行更加高效。

DMA 结构及特性

STM32F107 有两个 DMA 控制器,共计12个通道(DMA1有7个通道,DMA2有5个通道),每个通道管理来自一个或多个外设对存储器访问的请求。

DMA 控制器和 CM3共享系统数据总线,当 CPU 和 DMA 同时访问相同的目标(存储器或外设)时,DMA 请求会暂停 CPU 访问系统总线达若干个周期。为了保证 CPU 的正常工作,可以由总线仲裁器执行循环调度,以保证 CPU 至少可以得到一半的系统总线带宽。

DMA控制器特性:

  • 每个通道都直接连接专用的硬件 DMA 请求,每个通道都支持软件触发
  • 同一个 DMA 模块上,多个请求间的优先权刻意通过软件编程设置(共有四级:很高、高、中等和低),如果优先权设置相同,可以由通道号决定优先权的高低(请求0优先于请求1)
  • 数据源和目标数据区刻意选字节、半字、全字三种传输宽度
  • 源地址和目标地址必须按数据传输宽度对齐
  • 循环的缓冲器,以便用于数据长度固定并且周期操作的场合
  • 每个通道都有三个事件标志(传输过半、传输完成和传输错误),这三个事件标志通过“逻辑或”成为一个单独的中断请求,即三个事件标志使用同一个中断服务程序入口
  • DMA 有三种传输方式:存储器和存储器,外设和存储器以及存储器和外设之间
  • 闪存、SRAM、外设的 SRAM、APB1、APB2和 AHB 外设均可以作为访问源或访问目标
  • DMA 传输数据量最大为65535

DMA 传送

DMA传送由 DMA 控制器控制,在发生一个事件后,外设向 DMA 控制器发送一个请求信号。如果此请求的优先级为目前最高,则 DMA 控制器开始访问该外设,并立即向外设发送一个应答信号。当 DMA 控制器得到应答信号时,外设立即释放它的请求。一旦外设释放了这个请求,DMA 控制器同时撤销应答信号。

DMA 传送由三个操作组成:

  • 取外设地址:第一次传输时的开始地址保存在 DMA_CPARx(通道 x 外设地址)寄存器中,更具传输数目和地址加减操作取当前需要的地址
  • 取存储器地址:第一次传输时的开始地址保存在DMA_CMARx(存储器地址)寄存器中,根据传输数目和地址加减操作取当前需要的地址
  • 指针递减:对 DMA_CNDTRx(传输数量,包含未完成的操作数目)寄存器内的数据进行减一操作

仲裁器和优先级:

DMA 控制器内部有仲裁器。仲裁器根据通道请求的优先权来启动外设/存储器的访问。优先权管理分两个层次:

  • 软件: 需要用户来设置,每个通道优先权由4个等级:最高优先级、高优先级、中等优先级和低优先级
  • 硬件:由硬件定义的,如果两个请求由相同的软件优先级,则低编号的通道比高编号的通道有较高的优先权

存储器到存储器模式:

在 DMA通道的操作可以在没有外设请求的情况下进行,这种模式下,一旦开启 DMA 外设,DMA 传输将立即开始。当配置传输数目减为0时,DMA 传输结束。但存储器到存储器模式不能与循环模式同时使用

DMA 中断

中断事件 事件标志位
传输过半 HTIF
传输完成 TCIF
传输错误 TEIF

指针增量

DMA 中的“指针”其实是外设和存储器到“地址值”。“指针增量”是指启动 DMA 之后,外设和存储器到“地址值”在每次传输后可以有选择的完成自动增加的“数值”

DMA 传输有两种“地址模式”

  • 普通地址模式:不开启增量模式,DMA 传输完毕后地址不变
  • 指针增量模式:当设置为增量模式时,下一个要传输的地址将是前一个地址加上增量值

“指针增量模式”的增量值取决于所选的数据宽度,其值可以为1、2或4,即数据宽度是8位、16位还是32位。

DMA 配置步骤

  • 定义初始化结构体
  • 使能DMA1时钟
  • 设置外设地址
  • 设置存储器(内存)地址为缓存去的首地址
  • 设置传送数目
  • 设置 DMA 的其他配置
  • 设置存储器(内存)和外设的数据宽度
  • 设置 DMA 模式
  • 设置优先级
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
void DMA_Config(void)
{
/*定义 DMA 初始化结构体*/
DMA_InitTypeDef DMA_InitStructure;

/*使能 DMA1时钟*/
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);

/*外设地址为 USART1的 DR 寄存器即数据寄存器*/
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(USART2->DR));

/*内存地址为 dma_buffer[500]首地址*/
DMA_INitStructure.DMA_MemoryBaseAddr = (uint32_t)&dma_buffer;

/*DMA 以外设作为目的地,即内存向外设传输*/
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;

/*传送数目为5000*/
DMA_InitStructure.DMA_BufferSize = 5000;

/*外设地址不增加*/
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

/*内存地址自动加1*/
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

/*外设数据宽度为 Byte,即8位*/
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Bye;

/*内存数据宽度为 Bye*/
DMA_InitStructure.DMA_MemoryDtaSize = DMA_MemoryDtaSize_Byte;

/*DMA 模式为循环模式*/
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;

/*优先级为高*/
DMA_InitStructure.DMA_Priority = DMA_priority_High;

/*禁用内存至内存模式*/
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

/*根据设定的参数初始化 USART1的 TX 所在的 DMA1的通道4*/
DMA_Init(DMA1_Channel4, &DMA_InitStructure);
/*使能 DMA */
DMA_Cmd(DMA1_Channel4, ENABLE);

}

小结:

  • STM32F107有两个DMA控制器共计12个通道(DMA1有7 个通道,DMA2 有5 个通道)

  • 在每个DMA控制器中,还有一个仲裁器来协调各个请求的优先权

  • 每个通道都可以在有固定地址的外设寄存器和存储器地址之间执行DMA传输

  • DMA通道的操作可以在没有外设请求的情况下进行,这种操作就是存储器到存储器模式

  • DMA中的“指针”其实是外设和存储器的“地址值”

  • “指针增量”是指在启动DMA之后,外设和存储器的“地址值”在每次传输后可以有选择地完成自动增加的“数值”

简述 DMA 的概念、作用和优点

DMA(直接寄存器访问)是一种提供在外设和存储器(内存)之间或者存储器和存储器之间的高速数据传输的技术。

优点:大块数据可以通过 DMA 快速的移动,无需 CPU 的干预,从而节省了 CPU 的资源。可以用在数据采集、数据移动和和周期性数据处理等方面

ADC和 DAC

模拟/数字转换器(ADC)

概述:

  • 模拟/数字转换器(Analog to Digtal Converter,ADC,也称为“A/D转换器”)是 MCU 上重要和常用的外设,主要用来将模拟的数值数字化,以便进行数字化处理。
  • ADC 与 DAC 架起了一座沟通模拟世界与数字世界的桥梁。
  • AD转换用来将MCU不方便处理的模拟量转换为数字量,常用于数据采集,例如电压、电流、电阻和温度等,为MCU的计算和控制提供数据。

ADC 功能简介

STM32F107内置了两个12位逐次逼近型 ADC 转换器(ADC1和 ADC2),其主要特点:

  • 多达18个通道,可测量16个外部信号源和2个内部信号源
  • 各通道的 A/D 转换可以单词、连续、扫描或间断模式执行
  • ADC 的结果可以在左对齐或右对齐方式存储在16为数据寄存器中
  • 转换结束、注入转换结束和发生模拟看门狗事件事产生中断
  • ADC 转换器可进行自校准
  • 采样间隔可以按通道分别编程
  • 规则转换和注入转换均可由外部触发
  • 规则通道转换期间有 DMA 请求产生
  • 双 ADC 模式
  • 时钟频率为72MHz 时,转换时间为1.17us
  • ADC 的供电要求为2.4~3.6V
  • ADC 的输入范围为$V{REN}≤V{IN}≤V_{REF+}$

ADC 主要构成部件

  • 多路选择器:用于选择16各来自 GPIO 端口和温度传感器以及$V_{REFINT}$的信号
  • ADC:用于管理最多4个注入通道和16个规则通道
  • 注入通道数据寄存器:用于存放注入通道转换数据
  • 规则通道数据寄存器:用于存放规则通道转换数据

image-20181223145739337

模拟/数字转换(ADC)—ADC校准

ADC有一个内置自校准模式。一旦校准结束,可以开始正常转换。建议在上电时执行一次ADC校准。(启动校准前,ADC 必须处于关电状态超过至少两个 ADC 时钟周期)

使用下述代码调用库函数进行ADC1的校准

1
2
3
4
5
6
7
8
9
10
/* 重置ADC1校验寄存器 */   
ADC_ResetCalibration(ADC1);
/* 等待重置ADC1校验寄存器完成 */
while(ADC_GetResetCalibrationStatus(ADC1))
{}
/* 开始ADC1校验 */
ADC_StartCalibration(ADC1);
/* 等待校验完成 */
while(ADC_GetCalibrationStatus(ADC1))
{}

ADC 通道选择

STM32F107有16个多路通道,可以在任意多个通道上以任意顺序进行一系列转换。可以把转换组织成两组:“规则组”和“注入组”。这两个分组比较灵活,例如可以如下顺序完成转换

  • “规则组”最多可以设置16个转换,而“注入组”则最多可设置4个
  • “规则组”一般用于普通的AD转换分组
  • “注入组”一般用于不常用或突发的AD转换
  • 可以认为“注入组”是对“规则组”的一种中断行为。

image-20181223152452350

ADC 中断

ADC中断有以下两种情况,这两种中断都有独立的中断使能位:
  • 规则组和注入组转换结束时能产生中断。

  • 当模拟看门狗状态位被设置时也能产生中断。

/ 使能ADC2 EOC 和 AWDOG 中断 /

ADC_ITConfig(ADC2, ADC_IT_EOC | ADC_IT_AWD, ENABLE);

DMA 请求

  • 因为规则通道转换的值储存在一个数据寄存器(ADC_DR)中,所以当转换多个规则通道时需要使用DMA,这可以避免丢失已经存储在寄存器中的数据。
  • 只有在规则通道的转换结束时才产生DMA请求,并将转换的数据从ADC_DR寄存器传输到用户指定的目的地址。

/ 使能 ADC1的 DMA /

ADC_DMACmd(ADC1, ENABLE);

ADC 的工作模式

转换模式:

STM32F107的 ADC 有单次转换和连续转换两种方式,而在不同的通道分组中,当转换完成时,其转换数据存放的位置和相关标志位也不相同

image-20181223153525613

扫描模式:

  • 扫描模式用来扫描一组模拟通道,这组通道可以是已经配置好的规则组也可以是注入组。

  • 当开启扫描模式后,ADC将扫描“被选中组”中的所有通道。如果此时的转换模式为单次模式,则在扫描完本组所有通道后,ADC自动停止。

  • 如果转换模式为连续转换,则转换不会在所选择组的最后一个通道上停止,而是再次从选择组的第一个通道继续转换。

间断模式:

  • 所谓间断模式,即可以对选择的组执行一个短序列的n次转换(n<=8),一个外部触发信号可以启动下一轮n次转换,直到此序列所有的转换完成为止,以规则组为例:

  • 被转换的通道 = 0、1、2、3、6、7、9、10,当n=3时。

  • 第一次触发:转换的序列为 0、1、2

  • 第二次触发:转换的序列为 3、6、7

  • 第三次触发:转换的序列为 9、10,并产生EOC事件

  • 第四次触发:转换的序列 0、1、2

数据对齐:

由于STM32F107的ADC只有12位,所以转换后数据储存在16位的寄存器中,其数据可以左对齐或右对齐

image-20181223154332052

通道采样时间:

  • ADC使用若干个ADC_CLK周期对输入电压采样,采样周期数目可以通过固件库函数进行配置。每个通道可以分别用不同的时间采样。总转换时间如下计算:

  • TCONV = 采样时间+ 12.5 个周期

  • 例如,当ADCCLK=14MHz ,采样时间为1.5周期,则:

  • TCONV = 1.5 + 12.5 = 14 周期 = 1μs

外部触发转换:

  • 转换可以由外部事件触发( 例如定时器捕获、EXTI线)。

  • 用户选择8个事件中的某一个,来触发规则和注入组的采样。

  • 注意,当外部触发信号被选为ADC规则或注入转换时,只有它的上升沿可以启动转换。

ADC 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void ADC_Config(void)
{
/* ADC初始化结构体声明 */
ADC_InitTypeDef ADC_InitStructure;

/* 开启ADC1时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

/* 独立的转换模式 */
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;

/* 开启扫描模式 */
ADC_InitStructure.ADC_ScanConvMode = ENABLE;

/* 开启连续转换模式 */
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;

/* 关闭ADC外部触发 */
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;

/* 对齐方式,ADC为12位右对齐方式 */
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;

/* 开启通道数,1个 */
ADC_InitStructure.ADC_NbrOfChannel = 1;

/* ADC初始化 */
ADC_Init(ADC1, &ADC_InitStructure);

/*通道组,第10个通道 采样顺序1,转换时间55.5周期 */
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1,ADC_SampleTime_55Cycles5);
}
ADC 编程实例

下述代码用于实现任务描述10.D.1,使用ADC1通道10连续转换电压值,其源代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
main.c
#include "stm32f10x.h"
/* 声明GPIO初始化函数 */
void GPIO_Config(void);
/* 声明ADC初始化函数 */
void ADC_Config(void);
/* 声明延时函数 */
void delay(void);
int main(void)
{ /* 声明一个变量用于存放转换数值 */
uint16_t ad_v=0;
GPIO_Config();
ADC_Config();
/* 开启ADC1 */
ADC_Cmd(ADC1, ENABLE);
/* 重置ADC1校准寄存器 */
ADC_ResetCalibration(ADC1);
/* 等待重置完成 */
while(ADC_GetResetCalibrationStatus(ADC1));
/* 开始校准ADC1 */
ADC_StartCalibration(ADC1);
/* 等待校准完成 */
while(ADC_GetCalibrationStatus(ADC1));
/* 开始软件触发转换 */
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(1)
{
delay();
/* 获取转换数值 */
ad_v=ADC_GetConversionValue(ADC1);
/* 只保留右对齐的12位 */
ad_v&=0x0fff;
}
}

void GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);

/*PC0作为模拟通道10的输入管脚*/
GPIO_InitStructure.GPIO_Pin = GPIP_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOC,&GPIO_InitStructure);
}

void delay(void)
{
uint32_t i;
for(i=0;i<100000;i++)
{}
}


void ADC_Config(void)
{
/* ADC初始化结构体声明 */
ADC_InitTypeDef ADC_InitStructure;

/* 开启ADC1时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

/* 独立的转换模式 */
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;

/* 开启扫描模式 */
ADC_InitStructure.ADC_ScanConvMode = ENABLE;

/* 开启连续转换模式 */
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;

/* 关闭ADC外部触发 */
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;

/* 对齐方式,ADC为12位右对齐方式 */
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;

/* 开启通道数,1个 */
ADC_InitStructure.ADC_NbrOfChannel = 1;

/* ADC初始化 */
ADC_Init(ADC1, &ADC_InitStructure);

/*通道组,第10个通道 采样顺序1,转换时间55.5周期 */
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1,ADC_SampleTime_55Cycles5);
}

数字/模拟转换器(DAC)

  • 数字/模拟转换(Digital to Analog Converter,简称DAC),是一种将数字信号转换为模拟信号的外设,是ADC转换的相反过程。

  • DAC用于产生MCU不方便产生的模拟信号,以便直接控制或者输出给模拟器件和设备。

DAC 功能简介:

  • 2个DAC转换器,每个转换器对应1个输出通道。

  • 8位或者12位数字输出。

  • 12位模式下数据左对齐或者右对齐。

  • 同步更新功能。

  • 噪声波形生成。

  • 三角波形生成。≈≈

  • 双DAC通道同时或者分别转换。

  • 每个通道都有DMA功能。

  • 外部触发转换。

  • 输入参考电压VREF+(2.4V< VREF+<3.3V)。

DAC模块主要有以下部分组成:

  • 触发选择开关,用于触发源的选择。

  • DAC控制寄存器,用于DAC的配置。

  • 逻辑控制器,用于对触发及控制综合配置,并将转换数值输出到转换器。

  • 数据保持寄存器,用于存放和保持需要转换为模拟量的数字量。

  • 数字至模拟转换器,用于将数字量转换为模拟量并由输出引脚输出。

image-20181223160757675

DAC输出模式:

  • 可以利用LFSR产生幅度变化的伪噪声。

  • 可以在DC或者缓慢变化的信号上加上一个小幅度的三角波。

DAC 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
	DAC_Config( )
void DAC_Config(void)
{
DAC_InitTypeDef DAC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);

/* DAC配置*/
/* DAC 配置为软件触发,产生三角波 */
DAC_InitStructure.DAC_Trigger = DAC_Trigger_Software; //设置为软触发
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_ Triangle; //TIM1_CC3事件
DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude=DAC_TriangleAmplitude_63; //三角波幅值为63
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; //输出缓存器使能
DAC_Init(DAC_Channel_1, &DAC_InitStructure);
/* 使能DAC1 */
DAC_Cmd(DAC_Channel_1, ENABLE);
/* 设置DAC为12位左对齐 */
DAC_SetChannel1Data(DAC_Align_12b_L, 0x7FF0);
}
DAC编程实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
 main.c
#include "stm32f10x.h"
/* 声明初始化函数 */
void GPIO_Config(void);
void DAC_Config(void);
void delay(void);
int main(void)
{
GPIO_Config();
DAC_Config();
while (1)
{
/* 软件触发产生三角波*/
DAC_SoftwareTriggerCmd(DAC_Channel_1, ENABLE);
}
}

void GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void delay(void)
{
uint32_t i;
for(i=0;i<100000;i++)
{}
}

void DAC_Config(void)
{
DAC_InitTypeDef DAC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);

/* DAC配置*/
/* DAC 配置为软件触发,产生三角波 */
DAC_InitStructure.DAC_Trigger = DAC_Trigger_Software;
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_ Triangle; //TIM1_CC3事件
DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude=DAC_TriangleAmplitude_63;
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; //输出缓存器使能
DAC_Init(DAC_Channel_1, &DAC_InitStructure);
/* 使能DAC1 */
DAC_Cmd(DAC_Channel_1, ENABLE);
/* 设置DAC为12位左对齐 */
DAC_SetChannel1Data(DAC_Align_12b_L, 0x7FF0);
}

小结:

  • STM32F107内置两个12位逐次逼近型ADC转换器

  • STM32F107的ADC共有18个通道,可测量16个外部信号源和2个内部信号源

  • ADC规则组最多可以设置16个转换,而注入组则最多可设置4个

  • STM32F107的DAC是12位数字输入、电压输出的数字/模拟转换器

  • DAC可以配置为8位或12位模式,也可以与DMA控制器配合使用

  • 常规的DA转换外,还可以利用LFSR产生各种波形

CATALOG
  1. 1. Cortex-M3
    1. 1.1. 概述
      1. 1.1.1. 总结
    2. 1.2. STM32F107内核架构
      1. 1.2.1. CM3内核架构
      2. 1.2.2. 存储器组织
      3. 1.2.3. 堆栈
      4. 1.2.4. 电源管理
      5. 1.2.5. 复位和启动配置
      6. 1.2.6. 时钟
      7. 1.2.7. 复位机制
        1. 1.2.7.1. 复位序列:
    3. 1.3. GPIO 和 AFIO:
      1. 1.3.1. 概述:
      2. 1.3.2. GPIO 配置
      3. 1.3.3. 简述 IO 的通用功能、复用功能和复用功能重映射之间的联系和区别:
    4. 1.4. NVIC 和 EXTI
      1. 1.4.1. 概述:
      2. 1.4.2. 中断优先级
      3. 1.4.3. 中断输入和悬起:
      4. 1.4.4. 中断响应序列:
      5. 1.4.5. 中断返回
      6. 1.4.6. 中断嵌套
      7. 1.4.7. SysTick 定时器
      8. 1.4.8. 中断向量—(异常)向量表
      9. 1.4.9. NVIC 函数库配置
      10. 1.4.10. 外部中断/事件控制器
      11. 1.4.11. EXTI 库函数配置:
    5. 1.5. 定时器(TIM)
      1. 1.5.0.1. 使能时钟
    6. 1.5.1. 高级和通用定时器TIMx—TIMx主要特性
    7. 1.5.2. 时基单元
    8. 1.5.3. TIMx的三种计数模式
    9. 1.5.4. 计数模式 工作方式
    10. 1.5.5. 计数模式配置
    11. 1.5.6. 输出模式PWM模式
  2. 1.6. 看门狗
    1. 1.6.1. 概念:
    2. 1.6.2. 独立看门狗(IWDG)
    3. 1.6.3. IWDG 的配置步骤
    4. 1.6.4. 代码配置
  3. 1.7. 通用同步/异步收发器(USART)
  4. 1.8. 直接寄存器访问(DMA)
    1. 1.8.1. DMA 结构及特性
      1. 1.8.1.1. DMA控制器特性:
    2. 1.8.2. DMA 传送
    3. 1.8.3. 仲裁器和优先级:
    4. 1.8.4. 存储器到存储器模式:
    5. 1.8.5. DMA 中断
    6. 1.8.6. 指针增量
    7. 1.8.7. DMA 配置步骤
      1. 1.8.7.1. 小结:
      2. 1.8.7.2. 简述 DMA 的概念、作用和优点
  5. 1.9. ADC和 DAC
    1. 1.9.1. 模拟/数字转换器(ADC)
      1. 1.9.1.1. ADC 功能简介
    2. 1.9.2. ADC 主要构成部件
    3. 1.9.3. 模拟/数字转换(ADC)—ADC校准
    4. 1.9.4. ADC 通道选择
    5. 1.9.5. ADC 中断
      1. 1.9.5.0.1. ADC中断有以下两种情况,这两种中断都有独立的中断使能位:
  6. 1.9.6. DMA 请求
  7. 1.9.7. ADC 的工作模式
    1. 1.9.7.1. 转换模式:
    2. 1.9.7.2. 扫描模式:
    3. 1.9.7.3. 间断模式:
    4. 1.9.7.4. 数据对齐:
    5. 1.9.7.5. 通道采样时间:
    6. 1.9.7.6. 外部触发转换:
      1. 1.9.7.6.1. ADC 编程实例
  8. 1.9.8. 数字/模拟转换器(DAC)
    1. 1.9.8.1. DAC 功能简介:
    2. 1.9.8.2. DAC模块主要有以下部分组成:
    3. 1.9.8.3. DAC输出模式:
      1. 1.9.8.3.1. DAC编程实例: