stm32学习
一、GPIO的使用

- RCC配置
1
2
3
4RCC_AHBPeriphClockCmd() %外设时钟控制
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE) %GPIOA时钟控制
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE) %AFIO时钟控制
RCC_APB1PeriphClockCmd() %APB1时钟控制
GPIO配置
1 | GPIO_DeInit(GPIOA) %GPIO复位 |
GPIO重映射
- 开启AFIO时钟,开启重映射, 参数1参考stmf10xx参考手册完全重映射与部分重映射映射的引脚不同,有些引脚有默认功能不能重映射,需要先重映射解除默认功能,下面的调试端口不能同时解除默认功能,使用PA15,PB3,PB4解除JTAG,保留SWD

1 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE) %AFIO时钟控制 |
- 初始化重映射之后的GPIO
二、系统中断
NVIC是内部用于中断排序的系统,stm32f10系列有68个可屏蔽中断通道,16个可屏蔽通道
中断优先级
抢占优先级可以进行嵌套,可以不等程序完成,直接让CPU执行中断程序。响应优先级是必须等程序执行完成,在响应中断程序。
NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级 抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队 
EXTI外部中断
EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序 - 支持的触发方式:上升沿/下降沿/双边沿/软件触发 - 支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断 - 通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒 - 触发响应方式:中断响应/事件响应(转到外设处理中断)
AFIO只能选择一组GPIO口所以不能同时触发相同Pin的中断。EXTI9-5,EXTI15-10会触发同一个中断函数。20是触发外部中断
配置步骤 - 配置RCC,打开外设时钟,只用打开GPIO与AFIO时钟,EXTI与NVIC已经默认打开 - 配置GPIO,设置为输入模式
1 | GPIO_InitTypeDef GPIO_InitStructure; |
- 配置AFIO,选择GPIO口
1 | GPIO_EXTILineConfig( GPIO_PortSourceGPIOC, GPIO_PinSource1); |
- 配置EXTI,选择边沿触发方式,触发响应方式,中断响应或者事件响应
1 | EXTI_InitTypeDef EXTI_InitStruct; |
- 配置NVIC,设置优先级
1 | NVIC_PriorityGroupConfig(NVIC_PriorityGroup_x); //设置分组方式,整个工程只设置1个 |
- 中断函数
1 | void EXTI15_10_IRQHandler(void) //每个通道的中断函数名确定的,从启动文件里找 |
定时器
定时器介绍
定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型 
STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4
定时器结构图 
基本定时器,接受系统主频时钟72MHZ,经过分频后,上升沿触发计数器计数,当计数器达到自动重装载寄存器的储存值的时候,会触发定时器中断(ui)。预分配器可设为0-65535,对应1-65536分频。基本定时器只能向上触发。
主模式触发功能是指当计数器达到自动重装载寄存器的储存值的时候,定时器进行事件更新(u),然后映射到触发输出TRGO,TRGO触发DAC,不需要cup参与 
通用/高级定时器有三种计数模式 - 向上计数,从0增加到设定值触发中断 - 向下计数,从设定值增减小到0触发中断 - 中央对齐模式,从0增加到设定值触发中断,从设定值增减小到0再次触发中断
ETR可以作为外部时钟输入,被称为外部时钟模式2,经过极性选择,输入滤波等处理后,作为系统时钟,引脚定义可以看哪些引脚有ETR。
TRGI可以作为外部时钟输入,被称为外部时钟模式1,TRGI的输入有 - ETR,ITR1-ITR3,ITR1-ITR3是来自其他定时器的触发器输出TRG0,从而实现定时器的级联。 - CH1, CH2 - TL1FP1, TL1FP2
下面的功能是输入捕获,输出比较,二者不能同时使用,公用寄存器 
- 重复次数计数器,实现每隔几个周期再进行更新
- 驱动三相无刷电路,互补PWM输出
- 刹车输入
计数器时序图 
- CK_PSC 时钟信号
- CNT_EN 计数器使能,为1计数器打开
- CK_CNT 分频后的时钟信号
- 计数器寄存器上升沿计数,达到FC重新装载
- 预分频寄存器是用户写入的分配系数,当前计数未达到更新值前,就算改变了分频系数,下次计数才会生效。
计数器计数频率:CK_CNT = CK_PSC / (PSC + 1) 计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) = (72MHZ / (PSC + 1) ) * (ARR + 1) 
- 无预装值,在计数到33更改计数器重装载值为36,在36处触发中断

- 有预装值,在计数到F2时计数器重装载值从F5更改为36,但还是在F5处触发中断,并下个周期更改为36
定时器中断
- 开启RCC时钟
1 | RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启时钟 |
选择时基时钟单元的时钟源(内部时钟源)
1
TIM_InternalClockConfig(TIM2); //内部时钟
配置预分频器,自动重装载器,计数模式等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; //配置采样分配,随便选一个就行
// TIM_CKD_DIV1,配置采样分配,随便选一个就行
// TIM_CKD_DIV2
// TIM_CKD_DIV4
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; //计数模式
// TIM_CounterMode_Up 向上计数
// TIM_CounterMode_Down 向下计数
// TIM_CounterMode_CenterAligned1 中央对其模式1
// TIM_CounterMode_CenterAligned1 中央对其模式2
// TIM_CounterMode_CenterAligned1 中央对其模式3
TIM_TimeBaseInitStruct.TIM_Period = 10000 -1; //ARR自动重装载值
//ARR自动重装载值 0x0000 and 0xFFFF
TIM_TimeBaseInitStruct.TIM_Prescaler = 7200 - 1; //PSC预分频值
//PSC预分频值 0x0000 and 0xFFFF
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0; // 重复计数器的值只有TIM1有
// 重复计数器的值只有TIM1有 0x0000 and 0xFFFF
TIM_ClearFlag(TIM2, TIM_FLAG_Update); // 上电后立即清除定时器中断标志位,防止上电进入中断
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);配置输出中断控制,允许更新中断输出到NVIC
1
2
3
4TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //允许更新中断输出到NVIC
// TIMx
// TIM_IT_Update 更新中断模式
// ENABLE配置NVIC,打开定时器中断通道,分配优先级
1
2
3
4
5
6
7
8
9NVIC_PriorityGroupConfig(NVIC_PriorityGroup_x); //设置分组方式,整个工程只设置1个
// NVIC_PriorityGroup_x
NVIC_InitTypeDef NVIC_InitStructure; //中断初始化
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn; //设置中断通道,使用哪个引脚使用哪个
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //设置响应优先级
NVIC_Init(&NVIC_InitStructure);使能定时器
1
TIM_Cmd(TIMx, ENABLE); // 使能定时器
定时器中断函数,一般放在使用它的地方而不是在定时器函数中
1
2
3
4
5
6
7
8
9void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
定时器外部时钟
开启外部GPIO口
1
2
3
4
5
6GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);开启RCC时钟
1
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启时钟
选择外部时钟源
1
2
3
4
5
6
7
8
9
10
11
12TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_Inverted, 0x02); //选择ETR通过外部时钟模式2的输入时钟
//TIMx
//TIM_ExtTRGPSC_OFF 不分频
//TIM_ExtTRGPSC_DIV2 2分频
//TIM_ExtTRGPSC_DIV4 4分频
//TIM_ExtTRGPSC_DIV8 8分频
//TIM_ExtTRGPolarity_Inverted 上升沿触发
//TIM_ExtTRGPolarity_NonInverted 下升沿触发
// 0x00 and 0x0F 设置采样频率,几次采样一致后再作为时钟源输入其他参考上面的
定时器输出PWM
### 原理 OC(Output Compare)输出比较 - 输出比较可以通过比较CNT(计数器寄存器)与CCR(捕获比较寄存器)寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形 - 每个高级定时器和通用定时器都拥有4个输出比较通道 - 高级定时器的前3个通道额外拥有死区生成和互补输出的功能
周期:(ARR+1) * PSC/72000000 频率:72000000/(ARR+1) * PSC

工作流程:: 当CNT>=CCR时oc1ref会输出信号,给CC1P寄存器写0,从上面输出,电平信号不反转,给CCIP寄存器写1,从下面输出,电平信号反转。CC1E控制输出使能,要不要输出。
输出控制寄存器模式: 
- 冻结: CNT,CCR无效,PWM输出停止,保持为暂停时刻状态
- CNT=CCR时,置高电平,用途不大,只能一次性使用
- CNT=CCR时,置低电平,用途不大,只能一次性使用
- CNT=CCR时,电平翻转,PWM占空比设置为50%时适合使用
- CNT,CCR无效,PWM输出停止,保持为低电平状态
- CNT,CCR无效,PWM输出停止,保持为高电平状态
- PWM频率和占空比都可调模式,多用向上计数模式
- PWM频率和占空比都可调模式
代码

PWM频率: Freq = CK_PSC / (PSC + 1) / (ARR + 1) PWM占空比: Duty = CCR / (ARR + 1) PWM分辨率: Reso = 1 / (ARR + 1)
开启外部GPIO口为复用推挽输出,看文档哪个口有定时器功能
1
2
3
4
5
6GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);开启RCC时钟
1
RCC_APB2PeriphClockCmd(RCC_APB1Periph_TIM1, ENABLE); //开启时钟
选择时基时钟单元的时钟源(内部时钟源)
1
TIM_InternalClockConfig(TIM2); //内部时钟
配置预分频器,自动重装载器,计数模式等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; //配置采样分配,随便选一个就行
// TIM_CKD_DIV1,配置采样分配,随便选一个就行
// TIM_CKD_DIV2
// TIM_CKD_DIV4
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数模式
// TIM_CounterMode_Up 向上计数
// TIM_CounterMode_Down 向下计数
// TIM_CounterMode_CenterAligned1 中央对其模式1
// TIM_CounterMode_CenterAligned1 中央对其模式2
// TIM_CounterMode_CenterAligned1 中央对其模式3
TIM_TimeBaseInitStructure.TIM_Period = 10000 -1; //ARR自动重装载值
//ARR自动重装载值 0x0000 and 0xFFFF
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //PSC预分频值
//PSC预分频值 0x0000 and 0xFFFF
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; // 重复计数器的值只有TIM1有
// 重复计数器的值只有TIM1有 0x0000 and 0xFFFF
//TIM_ClearFlag(TIM2, TIM_FLAG_Update); // 上电后立即清除定时器中断标志位,防止上电进入中断
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);配置输出控制寄存器模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16TIM_OCInitTypeDef TIM_OCInitStucture
TIM_OCStructInit(&TIM_OCInitStucture); //结构体赋默认值,防止使用高级定时器某些变量没赋值
TIM_OCInitStucture.TIM_OCMode = TIM_OCMode_PWM1; //输出比较模式
// TIM_OCMode_PWM1 PWM输出模式1
TIM_OCInitStucture.TIM_OCNPolarity = TIM_OCNPolarity_High; //输出比较极性
// TIM_OCNPolarity_High 输出高电平
// TIM_OCNPolarity_Low 输出低电平
TIM_OCInitStucture.TIM_OutputState = TIM_OutputState_Enable; //输出使能
// TIM_OutputState_Enable 使能
// TIM_OutputState_Disable 不使能
TIM_OCInitStucture.TIM_Pulse = ; //CCR配置
// 0x0000 and 0xFFFF
TIM_OC1Init(TIM1, &TIM_OCInitStucture); //TIM1的通道1初始化,频率相同,占空比不同
TIM_OC2Init(TIM1, &TIM_OCInitStucture); //TIM1的通道2初始化,
TIM_OC3Init(TIM1, &TIM_OCInitStucture); //TIM1的通道3初始化,
TIM_OC4Init(TIM1, &TIM_OCInitStucture); //TIM1的通道4初始化,使能定时器
1
2//TIM_CtrlPWMOutputs(TIM1, ENABLE); 高级定时器输出使能
TIM_Cmd(TIM1, ENABLE); // 使能定时器单独配置CCR
1
2
3
4TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1); //通道1CCR配置
TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2); //通道1CCR配置
TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3); //通道1CCR配置
TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4); //通道1CCR配置
定时器输入捕获
IC(Input Capture)输入捕获 - 输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数 - 每个高级定时器和通用定时器都拥有4个输入捕获通道 - 可配置为PWMI模式,同时测量频率和占空比 - 可配合主从触发模式,实现硬件全自动测量
- 测频法适合高频信号 - 测周法适合低频信号, \(t = 1/f_c\), \(T = N/f_c\) , \(F = f_c/N\)
- 中界频率区别高低频频率的零界点
硬件电路
- ICF配置滤波器的频率以及采样次数 - CC1P配置是上升沿还是下降沿触发,得到TT1P1信号 - CC1S是数据选择器,选择输入数据 - ICPS配置分配器 - CC1E使能输出 - 通过主从触发模式可以自动重装载CCR,TI1FP1,可以作为触发源触发定时器的从模式功能,只能在通道1和通道2使用 - 这里定时器的通道1与通道2的TI2FP1,TI2FP2可以选择交叉还是直连模式,交叉是通道1的TI2FP1接到通道2的TI1
主从触发模式

代码
- 开启RCC与GPIO时钟
1 | RCC_APB2PeriphClockCmd(RCC_APB1Periph_TIM1, ENABLE); //开启时钟 |
选择时基时钟单元的时钟源(内部时钟源)
1
TIM_InternalClockConfig(TIM2); //内部时钟
GPIO初始化,输入模式,上拉模式或者浮空输入模式
1
2
3
4
5
6GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);配置预分频器,自动重装载器,计数模式等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //配置采样分配,随便选一个就行
// TIM_CKD_DIV1,配置采样分配,随便选一个就行
// TIM_CKD_DIV2
// TIM_CKD_DIV4
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数模式
// TIM_CounterMode_Up 向上计数
// TIM_CounterMode_Down 向下计数
// TIM_CounterMode_CenterAligned1 中央对其模式1
// TIM_CounterMode_CenterAligned1 中央对其模式2
// TIM_CounterMode_CenterAligned1 中央对其模式3
TIM_TimeBaseInitStructure.TIM_Period = 10000 -1; //ARR自动重装载值
//ARR自动重装载值 0x0000 and 0xFFFF
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //PSC预分频值
//PSC预分频值 0x0000 and 0xFFFF
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; // 重复计数器的值只有TIM1有
// 重复计数器的值只有TIM1有 0x0000 and 0xFFFF
//TIM_ClearFlag(TIM2, TIM_FLAG_Update); // 上电后立即清除定时器中断标志位,防止上电进入中断
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);配置输入捕获单元
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22TIM_ICInitTypeDef TIM_ICInitStructture
TIM_ICInitStructture.TIM_Channel = TIM_Channel_1; //配置输入捕获通道
// TIM_Channel_1 输入通道1
// TIM_Channel_2 输入通道2
// TIM_Channel_3 输入通道3
// TIM_Channel_4 输入通道4
TIM_ICInitStructture.TIM_ICFilter = 0xF; //配置输入捕获滤波器
// 0x0-0xF
TIM_ICInitStructture.TIM_ICPolarity = TIM_ICPolarity_Rising; //配置输入极性,上升沿触发还是下降沿触发
// TIM_ICPolarity_Rising 上升沿触发
// TIM_ICPolarity_Falling 下降沿触发
// TIM_ICPolarity_BothEdge 都触发
TIM_ICInitStructture.TIM_ICPrescaler = TIM_ICPSC_DIV1; //分频器
// TIM_ICPSC_DIV1 不分频
// TIM_ICPSC_DIV2 2分频
// TIM_ICPSC_DIV4 4分频
// TIM_ICPSC_DIV8 8分频
TIM_ICInitStructture.TIM_ICSelection = TIM_ICSelection_DirectTI; //配置数字选择器,选择是交叉还是直连通道
// TIM_ICSelection_DirectTI 直连通道
// TIM_ICSelection_IndirectTI 交叉通道
// TIM_ICSelection_TRC
TIM_ICInit(TIM3, &TIM_ICInitStructture);选择从模式触发源为TL1FP1
1
2
3
4
5
6
7
8
9TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
// TIM_TS_ITR0
// TIM_TS_ITR1
// TIM_TS_ITR2
// TIM_TS_ITR3
// TIM_TS_TI1F_ED
// TIM_TS_TI1FP1
// TIM_TS_TI2FP2
// TIM_TS_ETRF配置从模式为reset
1
2
3
4
5TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);
// TIM_SlaveMode_Reset
// TIM_SlaveMode_Gated
// TIM_SlaveMode_Trigger
// TIM_SlaveMode_External1开启定时器
1
TIM_Cmd(TIM3, ENABLE); // 使能定时器; //内部时钟

PWMI是使用了两个通道,通道1采用上升沿触发,通道2采用下降沿触发,下降沿触发时不会清零CNT,CNT计入CCR2,上升沿到来,CNT计入CCR1同时CNT清零。CCR2/CCR1即使占空比。
定时器编码器接口
编码器接口 - 编码器接口可接收增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT自增或自减,从而指示编码器的位置、旋转方向和旋转速度 - 每个高级定时器和通用定时器都拥有1个编码器接口 - 两个输入引脚借用了输入捕获的通道1和通道2
- 仅在TI1计数是,只在A相的上升沿或者下降沿进行计数,正向都向上计数,反向都向下计数 - 仅在TI2计数是,只在A相的上升沿或者下降沿进行计数 - TI1和TI2计数是,在A相的上升沿以及B相的上升沿进行计数或者,最常使用的模式
- 当TI1FP1为上升沿有效时,TI1FP2为低电平,CCR计数器加一,对应正转。TI1FP2为高电平,CCR计数器减一,对应反转转。通道1设置为上升沿有效,通道2设置为上升沿有效,
代码
- 开启GPIO与定时器时钟 - 配置GPIO - 配置时基单元,不分频,ACC为65536 - 配置输入捕获单元,滤波器与极性 - 配置编码器接口模式 - 启动定时器
1 | GPIO_InitTypeDef GPIO_InitStructure; |
ADC
ADC(Analog-Digital Converter)模拟-数字转换器 - ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁 - 12位逐次逼近型ADC,1us转换时间 输入电压范围:03.3V,转换结果范围:04095 - 18个输入通道,可测量16个外部和2个内部信号源 - 规则组和注入组两个转换单元 - 模拟看门狗自动监测输入电压范围 ,自动监测某些传感器的值,达到条件申请中断,不需要主动if - STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道 
ADC原理与结构
- 规则通道可以一次性可以选择16个需要ADC转换的通道,转换完成后返回对应数字量,但规则通道只有16位,放一个数,需要配合DMA来使用。 - 注入通道,一次性可以选择4个需要ADC转换的通道,有4个对应寄存器存放对应值,使用较少 - ADC时钟只能选择6,8分频 - 转换完成标志位置1,可以申请中断

四种转换模式
连续转换,单次转换,扫描模式,非扫描模式 - 连续转换,扫描模式,选取多个通道,转换每一个通道,标志位置不置1,一直转换 - 连续转换,非扫描模式,选取一个通道,一直转换通道,然后标志位不置1,一直转换 - 单次转换,扫描模式,选取多个通道,只能保留转换的最后一个通道, - 单次转换,非扫描模式,选取一个通道,只能转换一个通道,只转换一次
ADC触发方式

数据对齐

采样时间

校准
- ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差
- 建议在每次上电后执行一次校准
- 启动校准前, ADC必须处于关电状态超过至少两个ADC时钟周期
代码
单次转换,非扫描模式
- 开启GPIO,ADC的时钟,分频器
- 配置GPIO模拟输入
- 配置多路开关
- 配置ADC转换器
- 开启ADC
- ADC校准
1 | void AD_Init(void) |
连续转换,非扫描模式
1 | void AD_Init(void) |
单次转换,非扫描模式来实现读取多个传感器
1 | uint16_t AD_GatValue(uint8_t ADC_Channel) |
DMA
原理
DMA(Direct Memory Access)直接存储器存取 - DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源 - 12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道) 每个通道都支持软件触发和特定的硬件触发
- STM32F103C8T6 DMA资源:DMA1(7个通道)
- DMA1有7个通道,每个通道可以设置访问地址,但只有一个DMA总线,只能分时复用,如果两个通道访问同一个地址,则会由仲裁器决定哪一个先访问。 - AHB从设备是CUP来配置DMA,主设备能写从设备,从设备不能写主设备,寄存器啥的都是从设备
- 数据宽度:字节8位,半字16位,字32位 - 传输寄存器:每传输一次后计数器自动减一,减到0,会触发重装,可以设置是否重装 - M2M控制是软件触发还是硬件触发,软件触发多用于从存储器到存储器,不需要等待存储器数据采集到数据 - DMA启动有三个条件 1.启动开关控制,2.传输计数器的值大于0, 3.选择触发源 - 写传输寄存器时必须先关闭开关控制,在写入传输计数器,最后打开开关控制
- 触发源需要开启DMA,用到DMA函数,例如ADC_DMACMD 
代码
- 开启RCC时钟
- DMA初始化
- 通道使能
1 |
|
扫描模式+单次转换代码
1 |
|
1 |
|
扫描模式+多次转换代码
1 |
|
1 |
|
串口通信
串口通信相关知识
- 全双工是两个设备可以同时通信 - 有单独的时钟线是同步的,异步没有时钟线,需要事先约定一个采样频率 - 单端信号需要公地,差分信号是两个引脚的电压差来通信的
电平标准
电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:
- TTL电平:+3.3V或+5V表示1,0V表示0 - RS232电平:-3-15V表示1,+3+15V表示0 - RS485电平:两线压差+2+6V表示1,-2-6V表示0(差分信号)
串口参数及时序
- 波特率:串口通信的速率 bit/s
- 起始位:标志一个数据帧的开始,固定为低电平 ,因为空闲状态是高电平,发送数据置0表示要发送数据
- 数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行
- 校验位:用于数据验证,根据数据位计算得来
- 停止位:用于数据帧间隔,固定为高电平,恢复默认高电平状态

USART串口
硬件接线图
- 简单双向串口通信有两根通信线(发送端TX和接收端RX)
- TX与RX要交叉连接 当只需单向的数据传输时,可以只接一根通信线
- 当电平标准不一致时,需要加电平转换芯片

- USART(Universal Synchronous/Asynchronous Receiver/Transmitter)通用同步/异步收发器
- USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里
- 自带波特率发生器,最高达4.5Mbits/s
- 可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2)
- 可选校验位(无校验/奇校验/偶校验)
- 支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN
- STM32F103C8T6 USART资源: USART1、 USART2、 USART3

中断控制:TXE发送寄存器空标志位,RXNE接受寄存器非空标志位
发送+接受字节程序
- 发送器和接收器的波特率由波特率寄存器BRR里的DIV确定 - 计算公式:波特率 = fPCLK2/1 / (16 * DIV)
步骤: - 开启GPIO与USART时钟 - GPIO初始化,复用推挽输出,RX配置为输入 - 配置USART - 需要发送直接初始化就行,接受需要配置中断,开启中断,配置优先级
Serial.h
1 |
|
Serial.c
1 |
|
发送+接受数据包程序

Serial.h
1 |
|
Serial.h
1 |
|
main.c
1 | int main(void) |
IIC串口
硬件与软件原理
- I2C(Inter IC Bus)是由Philips公司开发的一种通用数据总线
- 两根通信线:SCL(Serial Clock)、SDA(Serial Data)
- 同步,半双工
- 带数据应答
- 支持总线挂载多设备(一主多从、多主多从)

软件I2C代码
MyI2C.h
1 |
|
MyI2C.h
1 |
|
硬件I2C代代码
- 发送数据时当数据寄存器为空 TEX为1 - 接受数据时当数据寄存器为非空 RXNE为1
- GPIO为复用开漏输出模式
- 10位地址帧率头是11110 
代码
- 开启I2C外设与GPIO口时钟
- GPIO口初始化为复用开漏输出模型
- I2C配置
- 使能IIC





