STM32引脚定义

STM32F103c8t6引脚定义表:(橙色是电源相关的引脚,蓝色是最小系统相关的引脚。绿色的是IO口和功能引脚,FT意为能容忍5V电压,主功能是上电默认的功能,默认复用功能是IO口上连接的外设功能引脚,加粗的引脚是能直接用的,没加粗的引脚可能要进行其他配置才能使用)

2-1 新建工程

image-20240125202451890

3-1 GPIO输出

GPIO叫通用输入输出端口,有八种配置模式

这里解释一下各种模式(原文章:STM32 GPIO八种输入输出模式 - 知乎 ):

1. GPIO_MODE_AIN 模拟输入

输入信号不经施密特触发器直接接入,输入信号为模拟量而非数字量,其余输入方式输入数字量。

2. GPIO_MODE_IN_FLOATING 浮空输入

输入信号经过施密特触发器接入输入数据存储器。当无信号输入时,电压不确定。因为浮空输入既高阻输入,可以认为输入端口阻抗无穷大,这样可以检测到微弱的信号。(相当于电压表测电压,如果电压表内阻不够大而外部阻抗比较大,则电压表分压会比较小)。此时输入高电平即高电平,输入低电平即低电平。但是外界没有输入时输入电平却容易受到外界电磁以及各种玄学干扰的影响。如按键采用浮空输入,则在按键按下时输入电平为低,但是当松开按键时输入端口悬空,外界有微弱的干扰都会被端口检测到。此时端口可能高,也可能低。

3. GPIO_MODE_IPD 下拉输入

浮空输入在外界没有输入时状态不确定,可能对电路造成干扰。为了使得电路更加稳定,不出现没有输入时端口的输入数据被干扰 (比如手碰一下电压就发生变化)。这时就需要下拉电阻(或上拉电阻),此电阻与端口输入阻抗相比仍然较小。有输入信号时端口读取输入信号,无输入信号时端口电平被拉到低电平(高电平)

4. GPIO_MODE_IPU 上拉输入

上拉输入与下拉输入类似,只是无输入信号时端口电平被拉到高电平。例如按键信号,当按下时输入低电平,松开时电平被拉到高电平。这样就不会出现按键松开时端口电平不确定的情况。即不知道时按下还是松开。

5. GPIO-MODE_OUT_OD 开漏输出

开漏输出即漏极开路输出。这种输出方式指场效应管漏极开路输出。需要接上拉电阻才能输出1。漏极经上拉电阻接到电源,栅极输出0时,场效应管截止(阻抗无线大),电压被分到场效应管上,此时输出为1。当栅极输出1时,场效应管导通,输出端口相当于接地,此时输出0。开漏输出高电平时是由外接电源输出的,因此可以实现高于输出端口电压的输出。可以实现电平的转换。开漏输出可以实现线与功能,方法是多个输出共接一个上拉电阻。但是漏极开路输出上升沿慢,因为上升沿是外接电源对上拉电阻以及外接负载充电。当上拉电阻较大或者负载容性较大时时间常数较大,充电较慢。需要较快反映时可以采用下降沿触发,此时没有电阻接入,电路的时间常数较小,充电较快。

6. GPIO_MODE_OUT_PP 推挽输出

推挽输出既可以输出1,又可以输出0。但是无法调节输出电压,因为输出高低电平均为三极管输入端电压,此电压在由芯片内部供电,无法改变。推挽输出任意时刻只有一路工作。上图为输出高电平时电路工作状态。只有三极管导通电阻,无外接电阻。因此推挽输出损耗小、速度快

7. GPIO_MODE_AF_OD 复用开漏输出

STM32单片机内部有其他的外设,比如定时器、DAC等。复用开漏输出与普通开漏输出区别在于,开漏输出输出的是输出数据寄存器中的数据,复用开漏输出输出的是来自外设的数据

8. GOIO_MODE_AF_PP 复用推挽输出

复用推挽输出原理与复用开漏输出原理相同

 官方GPIO相关库函数说明:

 下面是标准库有关GPIO的所有函数,可在”stm32f10x_gpio.h”中查看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//重置函数,将GPIO的所有配置重置,恢复为上电状态void GPIO_DeInit(GPIO_TypeDef* GPIOx); 
//重置函数,将AFIO的所有配置重置,恢复为上电状态void GPIO_AFIODeInit(void);
//GPIO的初始化函数,在GPIO相关时钟使能后,通过这个函数配置GPIO的各种参数void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
//将传入的结构体的所有变量重置,以方便再配置其他端口void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);
//位读取GPIO的输入信号,直接读取指定的端口的信号,返回高SET或低RESETuint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//读取GPIOx的全部端口,返回寄存器中的16位数据uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
//位读取GPIO输出信号,直接读取指定的GPIO的输出信号,返回高SET或低RESET
//当我们不知道当前GPIO输出什么信号,可以用这个函数读取uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//读取GPIOx的全部输出信号,返回寄存器中的16位数据uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
//直接拉高指定端口的电平状态void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//直接拉低指定端口的电平状态void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//直接对指定端口写入高或低电平void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
//对GPIOx写入16位的数据来控制全部端口,参数PortVal以十六进制输入void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
//锁定指定端口的配置,防止再被修改void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//用于配置 GPIO 引脚作为事件输出,以便与其他外设进行事件触发或通知void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
//用于启用或禁用GPIO事件输出功能void GPIO_EventOutputCmd(FunctionalState NewState);
//用来进行引脚重映射的函数,当引脚的默认功能与我们的规划有冲突时,可以将一些引脚重映射
//参数1是重映射的方式,参数2是新的状态void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
//配置AFIO的数据选择器,以此来选择我们想要的中断引脚void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
//以太网相关void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);

3-2 LED闪烁&LED流水灯&蜂鸣器

想要驱动GPIO外设,分三个步骤。使能时钟RCC,初始化GPIO,调函数置高低电平

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
#include "stm32f10x.h"                  // Device header
#include "Delay.h"

int main(void)
{

// 针脚命名为PA、PB、PC···PG,LED灯阴极插的是A0,所以为GPIOA
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

// GPIO:通用输入/输出端口
// GPIO初始化:给GPIO结构体命名GPIO_IntLED
GPIO_InitTypeDef GPIO_IntLED;
GPIO_IntLED.GPIO_Mode=GPIO_Mode_Out_PP;// GPIO模式
GPIO_IntLED.GPIO_Pin=GPIO_Pin_0; // GPIO端口
GPIO_IntLED.GPIO_Speed=GPIO_Speed_50MHz;// GPIO速率
GPIO_Init(GPIOA, &GPIO_IntLED); //指定外设GPIOA寄存器
// 将指定端口设置为高电平
GPIO_SetBits(GPIOA, GPIO_Pin_0);//灭
// 将指定端口设置为低电平
GPIO_ResetBits(GPIOA, GPIO_Pin_0);//亮
while(1)
{
// 通过反复调 高/低电平 来实现LED闪烁
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
Delay_ms(500);
GPIO_SetBits(GPIOA,GPIO_Pin_0);
Delay_ms(500);
// 设置或清除指定数据端口位
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET);
Delay_ms(500);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET);
Delay_ms(500);
// 同上,0 和 1 本不属于枚举BitAction,强转为BitAction
GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)0);
Delay_ms(500);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)1);
Delay_ms(500);
}
}
1
//任务2 LED流水灯  接A0~A7八颗灯珠,低电平点亮void task02(){	//使能PB2总线时钟	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);		//初始化GPIOA A0~A7	GPIO_InitTypeDef GPIO_initstructure;	GPIO_initstructure.GPIO_Pin = GPIO_Pin_All;	GPIO_initstructure.GPIO_Mode = GPIO_Mode_Out_PP;	GPIO_initstructure.GPIO_Speed = GPIO_Speed_50MHz;	GPIO_Init(GPIOA,&GPIO_initstructure);		while(1)	{		GPIO_Write(GPIOA,~0x0001); //0000 0000 0000 0001		Delay_ms(500);		GPIO_Write(GPIOA,~0x0001); //0000 0000 0000 0010		Delay_ms(500);		GPIO_Write(GPIOA,~0x0001); //0000 0000 0000 0100		Delay_ms(500);		GPIO_Write(GPIOA,~0x0001); //0000 0000 0000 1000		Delay_ms(500);		GPIO_Write(GPIOA,~0x0001); //0000 0000 0001 0000		Delay_ms(500);		GPIO_Write(GPIOA,~0x0001); //0000 0000 0010 0000		Delay_ms(500);		GPIO_Write(GPIOA,~0x0001); //0000 0000 0100 0000		Delay_ms(500);		GPIO_Write(GPIOA,~0x0001); //0000 0000 1000 0000		Delay_ms(500);			}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//任务3 蜂鸣器叫唤 接PB12,低电平触发,有源蜂鸣器
#include "stm32f10x.h" // Device header
#include "Delay.h"

int main(void)
{

// 针脚命名为PA、PB、PC···PG,蜂鸣器IO端插的是B12,所以为GPIOB
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

// GPIO:通用输入/输出端口
// GPIO初始化:给GPIO结构体命名GPIO_IntBUZZER
GPIO_InitTypeDef GPIO_IntBUZZER;
GPIO_IntBUZZER.GPIO_Mode=GPIO_Mode_Out_PP;// GPIO模式
GPIO_IntBUZZER.GPIO_Pin=GPIO_Pin_12; // GPIO端口
GPIO_IntBUZZER.GPIO_Speed=GPIO_Speed_50MHz;// GPIO速率
GPIO_Init(GPIOB, &GPIO_IntBUZZER); //指定外设GPIOB寄存器
while(1)
{
// 通过十六进制 ~取反 , <<向左位移 来实现LED流水灯
GPIO_WriteBit(GPIOB, GPIO_Pin_12, Bit_RESET);
Delay_ms(200);
}
}

3-3 GPIO输入image-20240216214506811

image-20240216215508748

image-20240216220955375

image-20240216221130420

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
typedef struct{
char x;
int y;
flout z;
qq} StructName_t;

int main(void)
{
int a;
a = 66;
printf("a = %d\n", a);

int b[5];
b[0] = 66;
b[1] = 77;
b[2] = 88;
printf("b[0] = %d\n", b[0]);
printf("b[1] = %d\n", b[1]);
printf("b[2] = %d\n", b[2]);

StructName c;
c.x = 'A';
c.y = 66;
c.z = 1.23;
printf("c.x = %c\n", c.x);
printf("c.y = %c\n", c.y);
printf("c.z = %c\n", c.z);
}

3-4 按键控制LED&光敏传感器控制LED

使用模块化编程,当遇到多外设时,将每个设备的代码分成.c和.h分开来写,有利于使代码结构清晰,也便于移植。

mian.c

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
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "LED.h"
#include "Key.h"

// 定义全局变量KeyNum
// 用于接收按键数据的载体
uint8_t KeyNum;

int main(void)
{
LED_Init();
Key_Init();

while(1)
{
KeyNum = Key_GetNum();
if (KeyNum == 1)
{
LED_1_Turn();
}
else if(KeyNum == 2)
{
LED_3_Turn();
}
}
}

 KEY.c

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
#include "stm32f10x.h"
#include "Delay.h"

// 初始化
void Key_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_Structure;
GPIO_Structure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Structure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
GPIO_Structure.GPIO_Speed = GPIO_Speed_50MHz; // 输入时速度可不写
GPIO_Init(GPIOB, &GPIO_Structure);
}

// 按键的数据
uint8_t Key_GetNum(void)
{
// 初始数据为 0
uint8_t KeyNum = 0;
// 原理:当读取到(GPIOB, GPIO_Pin_1)端口低电平时,将KeyNum设置为 1
// GPIO_ReadInputDataBit-->**读取指定端口管脚的输入**
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
Delay_ms(20);// 消除抖动
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);
Delay_ms(20);
KeyNum = 1;
}
// 同上,将KeyNum设置为 2
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)
{
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);
Delay_ms(20);
KeyNum = 2;
}


return KeyNum;
}

KEY.h

1
2
3
4
5
6
7
#ifndef __Key_H
#define __Key_H
void Key_Init(void);
uint8_t Key_GetNum(void);

#endif

Delay.c

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
#include "stm32f10x.h"

/**
* @brief 微秒级延时
* @param xus 延时时长,范围:0~233015
* @retval 无
*/
void Delay_us(uint32_t xus)
{
SysTick->LOAD = 72 * xus; //设置定时器重装值
SysTick->VAL = 0x00; //清空当前计数值
SysTick->CTRL = 0x00000005; //设置时钟源为HCLK,启动定时器
while(!(SysTick->CTRL & 0x00010000)); //等待计数到0
SysTick->CTRL = 0x00000004; //关闭定时器
}

/**
* @brief 毫秒级延时
* @param xms 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_ms(uint32_t xms)
{
while(xms--)
{
Delay_us(1000);
}
}

/**
* @brief 秒级延时
* @param xs 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_s(uint32_t xs)
{
while(xs--)
{
Delay_ms(1000);
}
}

Delay.h

1
2
3
4
5
6
7
8
9
#ifndef __DELAY_H
#define __DELAY_H

void Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
void Delay_s(uint32_t s);

#endif

 LED.c

1
2
3
4
5
6
7
8
9
#ifndef __DELAY_H
#define __DELAY_H

void Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
void Delay_s(uint32_t s);

#endif

LED.h

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
// LED.c 用于存放驱动程序的主体代码

#include "stm32f10x.h"

void LED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

GPIO_SetBits(GPIOA, GPIO_Pin_1 | GPIO_Pin_3);
}

// LED_1 开启
void LED_1_ON(void)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_1);
}
// LED_3 开启
void LED_3_ON(void)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_3);
}
// LED_1 关闭
void LED_1_OFF(void)
{
GPIO_SetBits(GPIOA, GPIO_Pin_1);
}
// LED_3 关闭
void LED_3_OFF(void)
{
GPIO_SetBits(GPIOA, GPIO_Pin_3);
}
// 反转LED_1
void LED_1_Turn(void)
{
// 原理:读取端口为 0,则置 1,读取端口为 1,则重置
// **读取指定端口管脚的输出**
if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_1) == 0)
{
GPIO_SetBits(GPIOA, GPIO_Pin_1);
}
else
{
GPIO_ResetBits(GPIOA, GPIO_Pin_1);
}
}
// 同上
void LED_3_Turn(void)
{
if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_3) == 0)
{
GPIO_SetBits(GPIOA, GPIO_Pin_3);
}
else
{
GPIO_ResetBits(GPIOA, GPIO_Pin_3);
}
}

 LED.c (光敏)

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
// LED.c 用于存放驱动程序的主体代码

#include "stm32f10x.h"

void LED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

GPIO_SetBits(GPIOA, GPIO_Pin_1 | GPIO_Pin_3);
}

// LED_1 开启
void LED_1_ON(void)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_1);
}
// LED_3 开启
void LED_3_ON(void)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_3);
}
// LED_1 关闭
void LED_1_OFF(void)
{
GPIO_SetBits(GPIOA, GPIO_Pin_1);
}
// LED_3 关闭
void LED_3_OFF(void)
{
GPIO_SetBits(GPIOA, GPIO_Pin_3);
}
// 反转LED_1
void LED_1_Turn(void)
{
// 原理:读取端口为 0,则置 1,读取端口为 1,则重置
// **读取指定端口管脚的输出**
if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_1) == 0)
{
GPIO_SetBits(GPIOA, GPIO_Pin_1);
}
else
{
GPIO_ResetBits(GPIOA, GPIO_Pin_1);
}
}
// 同上
void LED_3_Turn(void)
{
if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_3) == 0)
{
GPIO_SetBits(GPIOA, GPIO_Pin_3);
}
else
{
GPIO_ResetBits(GPIOA, GPIO_Pin_3);
}
}

 LED.h (光敏)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// LED.h 用于存放驱动程序可以对外提供的函数或变量的声明
// 作为c文件和main文件等外部文件的中转,方便外部文件的调用
#ifndef __LED_H
#define __LED_H
void LED_Init(void);

void LED_1_ON(void);
void LED_1_OFF(void);
void LED_3_ON(void);
void LED_3_OFF(void);
void LED_1_Turn(void);
void LED_3_Turn(void);

#endif

 light_sensor.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "stm32f10x.h"

void photosensitive(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_Structure;
GPIO_Structure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Structure.GPIO_Pin = GPIO_Pin_13;
GPIO_Structure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_Structure);
}

uint8_t sensitive(void)
{
return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13);
}

light_sensor.h

1
2
3
4
5
6
7
#ifndef __photosensitive_H
#define __photosensitive_H
void photosensitive(void);
uint8_t sensitive(void);

#endif

4-1 OLED调试工具

OLED是有机发光二极管的简称,下面提供的OLED驱动函数是江科大自己编写的软件IIC,字库未搭载汉字,不能显示汉字。

相比于硬件IIC,软件IIC更为灵活,任意两个引脚就能驱动OLED,只需在驱动函数稍作修改即可正常使用,但其通信速度相比于硬件IIC来说就较慢了,并且软件IIC会占用CPU的运算资源,硬件IIC和软件IIC各有优劣,按需选择。

4-2 OLED显示屏

1
void main(){	OLED_Init();	OLED_ShowChar(1,1,'A'); //显示单个字符	OLED_ShowString(1,3,"hello world!"); //显示字符串	OLED_ShowNum(2,1,12345,5); //显示无符号数字	OLED_ShowSignedNum(2,6,-66,3); //显示有符号数字	OLED_ShowHexNum(3,1,0xA0,8); //显示十六进制数	OLED_ShowBinNum(4,1,0xAA55,16); //显示二进制,填入的参数依然为十六进制	while(1)	{			}}

 OLED.c

更换不同的引脚使用此驱动库时,只要把.c文件内的宏定义和初始化的引脚配置改成自己想要的即可。

OLED.h

 OLED_Font.h  即字库文件

5-1EXTI外部中断

使用外部中断的步骤:

1.配置RCC,相关时钟都打开

2.配置GPIO,选择端口为输入模式

3.配置AFIO,链接到EXTI

4.配置EXTI,选择边缘触发方式

5.配置NVIC,给中断配置优先级

官方的EXTI外设库函数解释:

下面是EXTI外部中断库函数的调用函数,位置在stm32f10x_exti.h”内

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//将EXTI外设配置恢复位上电时的默认状态
void EXTI_DeInit(void);

//初始化指定的EXTI寄存器
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);

//将EXTI配置的结构体重置,以便重新输入参数来配置其他EXTI
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);

//产生一个软件中断
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);

//查看指定指定线路的EXTI中断标志位的寄存器数值
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);

//清除指定线路的EXTI中断标志位
//当中断要执行的操作完成后,一定要清除此中断的标志位,否则程序会一直卡在中断里面
void EXTI_ClearFlag(uint32_t EXTI_Line);

//检查指定EXTI线路是否触发中断请求
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);

//清除EXTI线路挂起位
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);

“misc.h”中的关于NVIC中断控制器的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//设置中断分组的方式
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);

//初始化NVIC中断控制器
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);

//设置中断向量表
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);

//系统低功耗配置
void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);

//配置SysTick 系统定时器的时钟源
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource);

5-2 对射使红外传感器计次&旋转编码器计次

这里仅贴核心代码

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
#include "stm32f10x.h"                  // Device header
#include "delay.h"
#include "OLED.h"
#include "conter_sensor.h"//二极管计数传感器
#include "encoder_sonser.h"//编码器传感器

/*
1.配置RCC,相关时钟都打开
2.配置GPIO,选择端口为输入模式
3.配置AFIO,链接到EXTI
4.配置EXTI,选择边缘触发方式
5.配置NVIC,给中断配置优先级
*/

//任务1,对射式红外传感器中断计数并将计次显示到OLED上
//OLEDSCL接B8,SDA接B9,红外传感器接B14
void task01()
{
OLED_Init();
ConterSensor_Init();

OLED_ShowString(1,1,"count:");

while(1)
{
OLED_ShowSignedNum(1,6,Get_ConterNum() ,6);
}
}


//任务2,用中断旋转编码器计数并显示到OLED上
//OLEDSCL接B8,SDA接B9,旋转编码器A相接B0,B相接B1
int16_t num;
void task02()
{
OLED_Init();
EncoderSensor_Init();

OLED_ShowString(1,1,"count:");
while(1)
{
num += Get_ConderNum();
OLED_ShowSignedNum(1,6, num,6);
}
}

int main(void)
{

while (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
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
#include "Device/Include/stm32f10x.h"   // Device header
#include "conter_sensor.h"

int16_t num = 0;

//初始化红外对射式计数器
//中断信号: GPIO->AFIO->EXTI->NVIC->CPU
void ConterSensor_Init()
{
//开启GPIOB的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

//开启AFIO的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

//配置GPIO B14
GPIO_InitTypeDef My_GPIO_Initstruct;
My_GPIO_Initstruct.GPIO_Pin = GPIO_Pin_14;
My_GPIO_Initstruct.GPIO_Mode = GPIO_Mode_IPU;
My_GPIO_Initstruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&My_GPIO_Initstruct);

//配置AFIO数据选择器
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);

//配置EXTI外部中断
EXTI_InitTypeDef My_EXTI_InitStructure;
My_EXTI_InitStructure.EXTI_Line = EXTI_Line14;//指定要配置的中断线
My_EXTI_InitStructure.EXTI_LineCmd = ENABLE;//指定中断是否使能
My_EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//配置中断模式,是中断模式还是事件模式
My_EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;//指定触发方式,是上升沿触发还是下降沿触发还是上下均触发,rising上升触发,falling下降沿触发
EXTI_Init(&My_EXTI_InitStructure);

//配置NVIC中断管理器,配置中断优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

//初始化NVIC,每个中断通道对应一个中断函数,在触发中断时系统会自动调用
NVIC_InitTypeDef My_NVIC_Initstructure;
My_NVIC_Initstructure.NVIC_IRQChannel = EXTI15_10_IRQn;//选中要配置的中断通道
My_NVIC_Initstructure.NVIC_IRQChannelCmd = ENABLE;//指定通道使能还是失能
My_NVIC_Initstructure.NVIC_IRQChannelPreemptionPriority = 1;//配置抢占优先级
My_NVIC_Initstructure.NVIC_IRQChannelSubPriority = 1;//配置响应优先级
NVIC_Init(&My_NVIC_Initstructure);
}

int16_t Get_ConterNum()
{
return num;
}

//中断函数在启动文件里面查找
void EXTI15_10_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line14) == SET)
{
num++;
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
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
#include "encoder_sonser.h"

int16_t encoder_num = 0;

//初始化旋转编码器
void EncoderSensor_Init()
{
//开启GPIOB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

//开启AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

//配置B0和B1端口
GPIO_InitTypeDef My_GPIO_InitStructure;
My_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
My_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
My_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&My_GPIO_InitStructure);

//配置AFIO数据选择器
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);

//配置外部中断
EXTI_InitTypeDef My_EXIT_InitStructure;
My_EXIT_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;//选择AFIO的通道
My_EXIT_InitStructure.EXTI_LineCmd = ENABLE;//使能中断
My_EXIT_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//配置为中断模式,并非事件模式
My_EXIT_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;//配置为上下沿均触发
EXTI_Init(&My_EXIT_InitStructure);

//配置NVIC中断管理器,配置中断优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

//初始化NVIC,每个中断通道对应一个中断函数,在触发中断时系统会自动调用
NVIC_InitTypeDef My_NVIC_InitStructure;
My_NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//选中要配置的通道
My_NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//选择使能还是失能
My_NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
My_NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&My_NVIC_InitStructure);

My_NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;//选中要配置的通道
My_NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//选择使能还是失能
My_NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
My_NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&My_NVIC_InitStructure);


}

//获取编码器的变化值
int16_t Get_ConderNum()
{
uint16_t temp;
temp = encoder_num;
encoder_num = 0;
return temp;
}

//中断函数在启动文件里面查找
void EXTI0_IRQHandler(void)
{
if(EXTI_GetFlagStatus(EXTI_Line0) == SET)
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0)
{
encoder_num--;
}
EXTI_ClearITPendingBit(EXTI_Line0);//清除标志位
}
}

void EXTI1_IRQHandler(void)
{
if(EXTI_GetFlagStatus(EXTI_Line1) == SET)
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0) == 0)
{
encoder_num++;
}
EXTI_ClearITPendingBit(EXTI_Line1);
}
}

6-1TIM定时器中断

 

 

 

 

有关定时器的库函数就更多了,先将”stm32f10x_tim.h”内的函数都列出来大致看一下,用到再详细说明

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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
//将时钟配置恢复到上电时的默认状态
void TIM_DeInit(TIM_TypeDef* TIMx);

//时基单元初始化
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);

//这四个函数就是配置输出比较模块的,OC(Output Compare)的意思是输出比较
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

//输入通道的初始化
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);

//PWM输入捕获的设置
void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);


void TIM_BDTRConfig(TIM_TypeDef* TIMx, TIM_BDTRInitTypeDef *TIM_BDTRInitStruct);

//将之前配置的结构体内的变量重置成默认值,以便重新进行配置
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);

//给结构体赋默认值的,当我们要重新配置结构体或有些参数用不上时,就使用此函数把结构体赋默认值
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);

//初始化输入捕获配置结构体,以便于赋默认值或重新配置
void TIM_ICStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct);


void TIM_BDTRStructInit(TIM_BDTRInitTypeDef* TIM_BDTRInitStruct);

//用来使能定时器
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);

//仅高级定时器使用,在使用高级定时器输出PWM时,要调用此函数,否则无法正常输出PWM
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);

//使能定时器产生的中断信号
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);


void TIM_GenerateEvent(TIM_TypeDef* TIMx, uint16_t TIM_EventSource);


void TIM_DMAConfig(TIM_TypeDef* TIMx, uint16_t TIM_DMABase, uint16_t TIM_DMABurstLength);


void TIM_DMACmd(TIM_TypeDef* TIMx, uint16_t TIM_DMASource, FunctionalState NewState);

//选择内部时钟,如果不用此函数,芯片默认也是使用内部时钟,追求严谨可加上此函数
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);

//选择ITRx其他定时器的时钟
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);

//选择TIx捕获通道的时钟
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
uint16_t TIM_ICPolarity, uint16_t ICFilter);

//选择ETR通过外部时钟模式1输入的时钟
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);

//选择ETR通过外部时钟模式2输入的时钟
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,
uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);

//单独用来配置ETR引脚的预分频器、极性、滤波器这些参数
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);

//直接单独配置预分配值PSC,参数2是写入模式,可选择是听从安排,在更新事件生效;或者是在写入后手动
//产生一个更新事件,让这个值立刻生效
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);

//改变计数器的计数模式
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);


void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);


void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode,
uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity);

//配置强制输出模式,运行中想要强制改变输出的波形,直接输出高或低电平就用这个
//不过其效果和改变PWM占空比100%或0%的效果是一样的
void TIM_ForcedOC1Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC2Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC3Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC4Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);

//自动重装器预装功能配置
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);


void TIM_SelectCOM(TIM_TypeDef* TIMx, FunctionalState NewState);


void TIM_SelectCCDMA(TIM_TypeDef* TIMx, FunctionalState NewState);


void TIM_CCPreloadControl(TIM_TypeDef* TIMx, FunctionalState NewState);

//配置CCR寄存器的预装功能,也就是影子寄存器(写入的值不会立即生效,而是在事件更新后才生效)
void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC3PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC4PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);

//配置快速使能
void TIM_OC1FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC2FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC3FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC4FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);

//外部事件时清除REF信号
void TIM_ClearOC1Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC2Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC3Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC4Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);

//用来单独设置输出比较极性的,带N的就是高级定时器的互补通道的配置,OC4无互补通道
void TIM_OC1PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC1NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC2PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC2NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC3PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC3NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC4PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);

//单独修改输出使能参数的
void TIM_CCxCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCx);
void TIM_CCxNCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCxN);

//单独更改输出比较模式
void TIM_SelectOCxM(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode);


void TIM_UpdateDisableConfig(TIM_TypeDef* TIMx, FunctionalState NewState);


void TIM_UpdateRequestConfig(TIM_TypeDef* TIMx, uint16_t TIM_UpdateSource);


void TIM_SelectHallSensor(TIM_TypeDef* TIMx, FunctionalState NewState);


void TIM_SelectOnePulseMode(TIM_TypeDef* TIMx, uint16_t TIM_OPMode);


void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource);


void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode);


void TIM_SelectMasterSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_MasterSlaveMode);

//给计数器直接写入一个值
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);

//给自动重装值直接写入一个值
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);

//单独用来更改CCR寄存器值的,PWM输出更改占空比就用这个函数
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);


void TIM_SetIC1Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);


void TIM_SetIC2Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);


void TIM_SetIC3Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);


void TIM_SetIC4Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);


void TIM_SetClockDivision(TIM_TypeDef* TIMx, uint16_t TIM_CKD);


uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx);


uint16_t TIM_GetCapture2(TIM_TypeDef* TIMx);


uint16_t TIM_GetCapture3(TIM_TypeDef* TIMx);


uint16_t TIM_GetCapture4(TIM_TypeDef* TIMx);

//获取当前计数器的值,返回值就是当前计数器的值
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);

//获取当前预分频器的值,返回值就是当前预分频器的值
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);

//获取TIM标志位
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);

//清除TIM标志位
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);

//获取定时器中断标志位
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);

//清除定时器中断标志位
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);

6-2 定时器定时中断&定时器外部时钟

main.c

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
#include "stm32f10x.h"                  // Device header
#include "delay.h"
#include "timer.h"
#include "oled.h"
int16_t num = 0;
//任务1 定时器定时中断
//OLED的SCL连B8,SDA连B9,显示屏上显示时间秒
void task01() {
OLED_Init();
Task01_Timer_Init();
OLED_ShowString(1,1,"Num:");
while(1) {
OLED_ShowNum(1,5,num,6);
}
}
//任务2 定时器外部时钟
//对射式红外传感器接A0,遮挡传感器计数值count+1,满十次使num+
1void task02() {
OLED_Init();
Task02_Timer_Init();
OLED_ShowString(1,1,"Count:");
while(1) {
OLED_ShowNum(1,6,Timer_GetCounter(),6);
}
}
int main(void) {
while (1) {
}
}
//TIM2中断回调函数
void TIM2_IRQHandler(void) {
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET) {
num++;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}

time.c

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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include "Device/Include/stm32f10x.h"   // Device header
/*
开启时钟步骤
1.RCC开启时钟
2.选择时基单元的时钟源,对于定时中断,选择内部时钟源
3.配置时基单元,包括预分频器PSC,自动重装器ARR,计数器CNT等
4.配置输出中断控制,允许更新中断输出到NVIC
5.配置NVIC,在NVIC中打开定时器中断的通道,并分配优先级
6.运行控制,使能定时器,编写中断函数
*/
void Task01_Timer_Init()//初始化TIM2通用定时器
{
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);

//选择时基单元的时钟
TIM_InternalClockConfig(TIM2);

//初始化时基单元 定时频率 = 72M / (PSC + 1) / (ARR + 1)
TIM_TimeBaseInitTypeDef My_TimerBaseInitstructure;
My_TimerBaseInitstructure.TIM_ClockDivision = TIM_CKD_DIV1; //预分频值
My_TimerBaseInitstructure.TIM_CounterMode = TIM_CounterMode_Up; //计数模式
My_TimerBaseInitstructure.TIM_Period = 10000 - 1; //ARR自动重装器的值
My_TimerBaseInitstructure.TIM_Prescaler = 7200 - 1; //PCS预分频器的值
My_TimerBaseInitstructure.TIM_RepetitionCounter = 0; //重复计数器的值
TIM_TimeBaseInit(TIM2,&My_TimerBaseInitstructure);


TIM_ClearFlag(TIM2,TIM_CounterMode_Up);//避免刚初始化完就进行一次中断


//使能定时器中断,TIM_IT_Update为更新中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);

//配置中断控制器NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

//配置中断通道以及优先级相关,开启中断
NVIC_InitTypeDef My_NVICStructure;
My_NVICStructure.NVIC_IRQChannel = TIM2_IRQn;
My_NVICStructure.NVIC_IRQChannelCmd = ENABLE;
My_NVICStructure.NVIC_IRQChannelPreemptionPriority = 2;
My_NVICStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&My_NVICStructure);

//启动定时器
TIM_Cmd(TIM2,ENABLE);
}

//编写TIM2的中断函数模板
//void TIM2_IRQHandler(void)
//{
// if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
// {
//
// TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
// }
//}

void Task02_Timer_Init()//初始化TIM2通用定时器
{
//开启TIM2时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);

//通过ETR引脚的外部时钟模式2配置
TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x00);

//初始化时基单元 定时频率 = 72M / (PSC + 1) / (ARR + 1)
TIM_TimeBaseInitTypeDef My_TimerBaseInitstructure;
My_TimerBaseInitstructure.TIM_ClockDivision = TIM_CKD_DIV1; //预分频值
My_TimerBaseInitstructure.TIM_CounterMode = TIM_CounterMode_Up; //计数模式
My_TimerBaseInitstructure.TIM_Period = 10000 - 1; //ARR自动重装器的值
My_TimerBaseInitstructure.TIM_Prescaler = 7200 - 1; //PCS预分频器的值
My_TimerBaseInitstructure.TIM_RepetitionCounter = 0; //重复计数器的值
TIM_TimeBaseInit(TIM2,&My_TimerBaseInitstructure);


TIM_ClearFlag(TIM2,TIM_CounterMode_Up);//避免刚初始化完就进行一次中断


//使能定时器中断,TIM_IT_Update为更新中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);

//配置中断控制器NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

//配置中断通道以及优先级相关,开启中断
NVIC_InitTypeDef My_NVICStructure;
My_NVICStructure.NVIC_IRQChannel = TIM2_IRQn;
My_NVICStructure.NVIC_IRQChannelCmd = ENABLE;
My_NVICStructure.NVIC_IRQChannelPreemptionPriority = 2;
My_NVICStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&My_NVICStructure);

//启动定时器
TIM_Cmd(TIM2,ENABLE);

//配置GPIO口
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef My_GPIO_InitStructure;
My_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
My_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
My_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&My_GPIO_InitStructure);
}

uint16_t Timer_GetCounter(void)
{
return TIM_GetCounter(TIM2);
}

以上代码是STM32微控制器的一个定时器初始化函数 Timer_Init。这个函数配置了TIM2定时器,设置了定时器的时钟源、分频系数和计数周期,并启用了定时器更新中断。下面是代码逐行解释:

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 Timer_Init(void)
{
// 启用TIM2外设时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

// 配置TIM2使用内部时钟
TIM_InternalClockConfig(TIM2);

// 定义并初始化TIM_TimeBaseInitTypeDef结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;

// 设置定时器时钟分频因子
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;

// 设置计数模式为向上计数
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;

// 设置自动重装载寄存器值,计数周期
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;

// 设置预分频器值,分频系数
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;

// 设置重复计数器值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;

// 初始化TIM2定时器基准时间单位
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);

// 使能TIM2更新中断
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
}
  1. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    • 使能TIM2定时器的时钟。TIM2属于APB1总线上的外设,因此使用 RCC_APB1PeriphClockCmd 函数使能TIM2时钟。
  2. TIM_InternalClockConfig(TIM2);

    • 配置TIM2使用内部时钟源。
  3. TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;

    • 定义一个 TIM_TimeBaseInitTypeDef 类型的结构体变量,用于配置定时器的基本时间参数。
  4. TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;

    • 设置时钟分频因子为1,即不分频。TIM_CKD_DIV1 表示直接使用输入时钟。
  5. TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;

    • 设置定时器的计数模式为向上计数。TIM_CounterMode_Up 表示定时器从0计数到 TIM_Period 的值。
  6. TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;

    • 设置自动重装载寄存器的值为9999。定时器的计数值从0到9999,即计数10000次后产生一次更新事件。
  7. TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;

    • 设置预分频器的值为7199。预分频器将时钟频率分频为 7200。如果输入时钟为72 MHz,分频后的时钟为 10 kHz。
  8. TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;

    • 设置重复计数器值为0,对于基本定时器,这个参数一般不使用。
  9. TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);

    • 使用上述配置初始化TIM2定时器。
  10. TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

    • 使能TIM2的更新中断。当定时器计数达到 TIM_Period 的值时会产生更新中断。

计时周期计算

假设输入时钟为72 MHz,分频系数为7200,自动重装载值为10000:

  • 计数频率 = 输入时钟 / 预分频值 = 72 MHz / 7200 = 10 kHz
  • 计数周期 = (自动重装载值 + 1) / 计数频率 = 10000 / 10 kHz = 1 秒

因此,该配置使得TIM2每秒产生一次更新中断。

6-3TIM输出捕获

PWM配置步骤:

1.开启RCC时钟,将要使用的定时器和GPIO外设时钟打开

2.配置时基单元,时钟源选择

3.配置输出比较单元,设置CCR的值,输出比较模式,极性选择,输出使能这些参数

4.配置GPIO,初始化为复用推挽输出的配置

5.运行控制,启动计数器

6-4 PWM驱动LED呼吸灯&PWM驱动直流电机

一个定时器TIM有四个通道,每个通道均可输出PWM波,但当我们用一个定时器输出4路PWM时,因为共用一个计数器,频率必须是一样的,占空比由各自通道的CCR决定,四路通道的相位也是一样的。

main.c

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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include "stm32f10x.h"                  // Device header
#include "delay.h"
#include "OLED.h"
#include "PWM.h"
#include "servo.h"
#include "motor.h"

//任务1 PWM驱动LED呼吸灯
//A0接LED
void task01()
{
uint8_t i;
OLED_Init();
Task01_PWM_Init();
while(1)
{
for(i = 0;i < 100;i++)
{
Task01_PWM_SetComparel(i);
Delay_ms(10);
}
for(i = 0;i < 100;i++)
{
Task01_PWM_SetComparel(100-i);
Delay_ms(10);
}
}
}



//任务2 PWM驱动舵机
//A1接舵机的信号线 PA1是TIM2的CH2 B1接一个按键控制PWM改变,即把io口配置为上拉输入
void Task02()
{
OLED_Init();
Servo_Init();
Servo_SetAngle(0);//初始把舵机设置为0度

uint16_t Angle = 0;
while(1)
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == RESET)//检测按键是否按下
{
Delay_ms(10);
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == RESET);
Angle += 20;
}
// for(Angle = 0;Angle <= 180; Angle+=20)
// {
// Servo_SetAngle(Angle);
// Delay_ms(50);
// }
// for(Angle = 0;Angle <= 180; Angle+=20)
// {
// Servo_SetAngle(180-Angle);
// Delay_ms(50);
// }
if(Angle > 180)
{
Angle = 0;
}


}
}

//任务3 PWM驱动直流电机
//用TB6612来驱动直流电机
/*
AO1接电机正极,AO2接电机负极 VM接5V VCC接3.3V GND共地 STBY接3.3V
PWMA接PA2,AIN1接PA4,AIN2接PA5 PA2是TIM2的CH3
按键接B1,端口为上拉输入
*/
void Task03()
{
OLED_Init();
Task03_PWM_Init();

int8_t temp = 0;//Speed的范围是0到100
while(1)
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == RESET)
{
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == RESET){}
temp+=20;
if(temp > 100)
{
temp = -100;
}
Motor_SetSpeed(temp);
}
}
}

int main(void)
{

Task03();

while (1)
{

}
}

 PWM.c

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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172

#include "PWM.h"


/*
PWM配置步骤:
1.开启RCC时钟,将要使用的定时器和GPIO外设时钟打开
2.配置时基单元,时钟源选择
3.配置输出比较单元,设置CCR的值,输出比较模式,极性选择,输出使能这些参数
4.配置GPIO,初始化为复用推挽输出的配置
5.运行控制,启动计数器
PWM频率: Freq = CK_PSC / (PSC + 1) / (ARR + 1)
PWM占空比: Duty = CCR / (ARR + 1)
PWM分辨率: Reso = 1 / (ARR + 1)
*/


void Task01_PWM_Init()
{
//RCC开启
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

//重映射将TIM2的CH1的PWM输出转到复用引脚
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
// GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);
// GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);

//选择内部时钟
TIM_InternalClockConfig(TIM2);

//初始化时基单元
TIM_TimeBaseInitTypeDef My_TimeBaceInitStructure;
My_TimeBaceInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //选择预分配倍数
My_TimeBaceInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //选择向上计数模式
My_TimeBaceInitStructure.TIM_Period = 100 - 1; //ARR
My_TimeBaceInitStructure.TIM_Prescaler = 720 - 1; //PSC
My_TimeBaceInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&My_TimeBaceInitStructure);

//配置输出比较单元
TIM_OCInitTypeDef My_TIM_OCInitStructure;
TIM_OCStructInit(&My_TIM_OCInitStructure);
My_TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
My_TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
My_TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
My_TIM_OCInitStructure.TIM_Pulse = 30;//CCR
TIM_OC1Init(TIM2,&My_TIM_OCInitStructure);

//配置GPIO输出,A0为TIM2_CH1
GPIO_InitTypeDef My_GPIO_InitStructure;
My_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIO_Pin_15 TIM2CH1复用时使用
My_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
My_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&My_GPIO_InitStructure);

//启动定时器
TIM_Cmd(TIM2,ENABLE);
}

void Task01_PWM_SetComparel(uint16_t Compare)//输入值改变占空比
{
TIM_SetCompare1(TIM2,Compare);
}

void Task02_PWM_Init()
{
//开启RCC
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //开启TIM2的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //A1是PWM的CH2输出口
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //开启GPIOB的时钟,B1要接一个按键控制pwm值改变,这样就可以靠按键实现舵机转动

//配置GPIO输出,A1为TIM2_CH2
GPIO_InitTypeDef My_GPIOInitStructure;
My_GPIOInitStructure.GPIO_Pin = GPIO_Pin_1;
My_GPIOInitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
My_GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&My_GPIOInitStructure);
//再配置按键的B1
My_GPIOInitStructure.GPIO_Pin = GPIO_Pin_1;
My_GPIOInitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
My_GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&My_GPIOInitStructure);


//选择TIM的时钟源,选内部时钟
TIM_InternalClockConfig(TIM2);

//初始化时基单元
TIM_TimeBaseInitTypeDef My_TimeBaceInitStructure;
My_TimeBaceInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
My_TimeBaceInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
My_TimeBaceInitStructure.TIM_Period = 20000 - 1;//ARR
My_TimeBaceInitStructure.TIM_Prescaler = 72 - 1;//PSC
My_TimeBaceInitStructure.TIM_RepetitionCounter = 30;
TIM_TimeBaseInit(TIM2,&My_TimeBaceInitStructure);

//配置输出比较单元
TIM_OCInitTypeDef My_OCInitStructure;
TIM_OCStructInit(&My_OCInitStructure);
My_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
My_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
My_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
My_OCInitStructure.TIM_Pulse = 0;//CCR 500到2500
TIM_OC2Init(TIM2,&My_OCInitStructure);

//启动定时器
TIM_Cmd(TIM2,ENABLE);
}

void Task02_PWM_SetComparel(uint16_t Compare)//输入数值改变PWM占空比
{
TIM_SetCompare2(TIM2,Compare);
}


void Task03_PWM_Init()
{
//开启RCC时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

//配置时基单元,选择时钟源
TIM_InternalClockConfig(TIM2);

TIM_TimeBaseInitTypeDef My_TimeBaseInitStructure;
My_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
My_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
My_TimeBaseInitStructure.TIM_Period = 20000 - 1;//ARR
My_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;//PSC
My_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&My_TimeBaseInitStructure);

//配置输出比较单元
TIM_OCInitTypeDef My_TIM_OCInitStructure;
TIM_OCStructInit(&My_TIM_OCInitStructure);
My_TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
My_TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
My_TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
My_TIM_OCInitStructure.TIM_Pulse = 30;//CCR
TIM_OC3Init(TIM2,&My_TIM_OCInitStructure);

//配置GPIO A4和A5分别是AIN1和AIN2 按键是B1
//并且IO口如果不是默认功能,要使用复用功能时,也要配置 A2是TIM2的CH3
GPIO_InitTypeDef My_GPIOInitStructure;
My_GPIOInitStructure.GPIO_Pin = GPIO_Pin_1;
My_GPIOInitStructure.GPIO_Mode = GPIO_Mode_IPU;
My_GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&My_GPIOInitStructure);//配置PB1为按键,上拉输入

My_GPIOInitStructure.GPIO_Pin = GPIO_Pin_4;
My_GPIOInitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA,&My_GPIOInitStructure);

My_GPIOInitStructure.GPIO_Pin = GPIO_Pin_5;
My_GPIOInitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA,&My_GPIOInitStructure);//PA4和PA5是控制电机旋转方向的,推挽输出即可

My_GPIOInitStructure.GPIO_Pin = GPIO_Pin_2;
My_GPIOInitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//设置PA2为复用推挽输出,这样就可以输出PWM了
GPIO_Init(GPIOA,&My_GPIOInitStructure);

//启动控制器
TIM_Cmd(TIM2,ENABLE);
}

//输入改变TIM2的CH3的PWM占空比
void Task03_PWM_SetComparel(uint16_t Compare)
{
TIM_SetCompare3(TIM2,Compare);
}

servo.c

1
2
3
4
5
6
7
8
9
10
11
12
13
#include "Device/Include/stm32f10x.h"   // Device header
#include "PWM.h"

void Servo_Init(void)
{
Task02_PWM_Init();
}

//0度对于500,180度对于2500
void Servo_SetAngle(float Angle)
{
Task02_PWM_SetComparel(Angle / 180 * 2000 + 500);
}

 motor.c

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
#include "motor.h"


void Motor_Init()
{
//初始化AIN1和AIN2两个引脚,控制正反转
RCC_APB2PeriphClockCmd(RCC_APBPeriph_AIN,ENABLE);

GPIO_InitTypeDef My_GPIO_InitStructure;
My_GPIO_InitStructure.GPIO_Pin = AIN1 | AIN2;
My_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
My_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(AIN1_Port,&My_GPIO_InitStructure);

//默认将电机锁住,防止乱转
GPIO_ResetBits(AIN1_Port,AIN1);
GPIO_ResetBits(AIN1_Port,AIN2);
}

void Motor_SetSpeed(int8_t Speed)
{
if(Speed > 0)//正转
{
GPIO_SetBits(AIN1_Port,AIN1);
GPIO_ResetBits(AIN1_Port,AIN2);
Task03_PWM_SetComparel(Speed);
}

if(Speed < 0)//反转
{
GPIO_ResetBits(AIN1_Port,AIN1);
GPIO_SetBits(AIN1_Port,AIN2);
Task03_PWM_SetComparel(-Speed);
}
}

6-5 TIM输入捕获

高级定时器和基本定时器有输入捕获通道,但基本定时器没有。 

PWMI模式是专门测量输入的频率和占空比的。

TIM输入捕获也可配置为主从触发模式,从而实现硬件全自动测量。

测频率就是测量一秒钟内有几个周期,测频法适用于测量高频率,测周法适用于测低频率,频率是高还是低就看中介频率。

配置输入捕获的步骤:

1.RCC开启时钟,把GPIO和TIM的时钟打开

2.GPIO初始化,将GPIO配置成输入模式,上拉输入或浮空输入

3.配置时基单元,让CNT计数器在内部时钟的驱动下自增运行

4.配置输入捕获单元,包括滤波器、极性、直连通道还是交叉通道,分频器这些参数

5.选择从模式的触发源,这里选择TI1FP1

6.选择触发后执行的操作,执行Reset操作

7.调用TIM_Cmd函数,开启定时器

6-6 输入捕获模式测频率&PWMI模式测频率占空比

main.c

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
#include "stm32f10x.h"                  // Device header
#include "delay.h"
#include "task01.h"
#include "task02.h"
#include "OLED.h"

//任务1 输入捕获模式测频率
//PA0输出PWM信号,用PA6输入捕获来测量信号
//PA0是TIM2的CH1 PA6是TIM3的CH1
void Task01()
{
OLED_Init();
Task01_Init();

OLED_ShowString(1,1,"Freq:00000Hz");

Task01_PWM_SetPrescaler(720 - 1); //Freq = 72M / (PSC + 1) / 100
Task01_PWM_SetComparel(50); //Duty = CCR / 100
while(1)
{
OLED_ShowNum(1,6,Task01_IC_GetFreq(),5);

}

}

//任务2 PWMI模数测频率占空比
void Task02()
{
OLED_Init();
Task02_Init();

OLED_ShowString(1,1,"Freq:00000Hz");
OLED_ShowString(2,1,"Duty:00%");

Task01_PWM_SetPrescaler(7200 - 1); //Freq = 72M / (PSC + 1) / 100
Task01_PWM_SetComparel(50); //Duty = CCR / 100
while(1)
{
OLED_ShowNum(1,6,Task02_IC_GetFreq(),5);
OLED_ShowNum(2,6,Task02_IC_GetDuty(),2);

}

}


int main(void)
{
// Task02();
while (1)
{

}
}

 task01.c

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
87
88
89
#include "task01.h"

//PA0和PA6
void Task01_Init()
{
//初始化时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);

//选择内部时钟
TIM_InternalClockConfig(TIM2);
TIM_InternalClockConfig(TIM3);

//配置TIM2时基单元
TIM_TimeBaseInitTypeDef My_TIM_TimeBaseInitStructure;
My_TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
My_TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
My_TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;//ARR
My_TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;//PSC
My_TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&My_TIM_TimeBaseInitStructure);//TIM2的CH1作为PWM输出

//配置PA0io口作为PWM输出
GPIO_InitTypeDef My_GPIOInitStructure;
My_GPIOInitStructure.GPIO_Pin = GPIO_Pin_0;
My_GPIOInitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
My_GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&My_GPIOInitStructure);

//设置输出比较单元
TIM_OCInitTypeDef My_TIM_OCInitStructure;
TIM_OCStructInit(&My_TIM_OCInitStructure);
My_TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//输出模式选择
My_TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//极性选择
My_TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//使能
My_TIM_OCInitStructure.TIM_Pulse = 20;//RCC
TIM_OC1Init(TIM2,&My_TIM_OCInitStructure);

//启动定时器TIM2
TIM_Cmd(TIM2,ENABLE);

//配置TIM3时基单元
My_TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
My_TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
My_TIM_TimeBaseInitStructure.TIM_Period = 65535 - 1;//ARR
My_TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;//PSC
My_TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3,&My_TIM_TimeBaseInitStructure);//TIM3的CH1作为输入捕获

//配置TIM3输入捕获单元
TIM_ICInitTypeDef My_TIM_InitStructure;
My_TIM_InitStructure.TIM_Channel = TIM_Channel_1;//选择定时器通道
My_TIM_InitStructure.TIM_ICFilter = 0xF;//滤波数值
My_TIM_InitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//触发模式,选上升沿触发
My_TIM_InitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//预分频值
My_TIM_InitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//选择触发信号从哪个引脚输入
TIM_ICInit(TIM3,&My_TIM_InitStructure);

//配置PA6GPIO口为输入模式
My_GPIOInitStructure.GPIO_Pin = GPIO_Pin_6;
My_GPIOInitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
My_GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&My_GPIOInitStructure);

//配置从模式的触发源
TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);

//配置从模式为Reset
TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);

//启动定时器TIM3
TIM_Cmd(TIM3,ENABLE);
}

void Task01_PWM_SetComparel(uint16_t Comparel)
{
TIM_SetCompare1(TIM2,Comparel);
}

void Task01_PWM_SetPrescaler(uint16_t Prescaler)
{
TIM_PrescalerConfig(TIM2,Prescaler,TIM_PSCReloadMode_Immediate);
}

uint32_t Task01_IC_GetFreq(void)
{
return 1000000 / (TIM_GetCapture1(TIM3) + 1);//+1消除误差
}

 task02.c

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
87
88
89
90
91
92
93
94
95
96
#include "task02.h"

void Task02_Init()
{
//初始化时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);

//选择内部时钟
TIM_InternalClockConfig(TIM2);
TIM_InternalClockConfig(TIM3);

//配置TIM2时基单元
TIM_TimeBaseInitTypeDef My_TIM_TimeBaseInitStructure;
My_TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
My_TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
My_TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;//ARR
My_TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;//PSC
My_TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&My_TIM_TimeBaseInitStructure);//TIM2的CH1作为PWM输出

//配置PA0io口作为PWM输出
GPIO_InitTypeDef My_GPIOInitStructure;
My_GPIOInitStructure.GPIO_Pin = GPIO_Pin_0;
My_GPIOInitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
My_GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&My_GPIOInitStructure);

//设置输出比较单元
TIM_OCInitTypeDef My_TIM_OCInitStructure;
TIM_OCStructInit(&My_TIM_OCInitStructure);
My_TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//输出模式选择
My_TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//极性选择
My_TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//使能
My_TIM_OCInitStructure.TIM_Pulse = 20;//RCC
TIM_OC1Init(TIM2,&My_TIM_OCInitStructure);

//启动定时器TIM2
TIM_Cmd(TIM2,ENABLE);

//配置TIM3时基单元
My_TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
My_TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
My_TIM_TimeBaseInitStructure.TIM_Period = 65535 - 1;//ARR
My_TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;//PSC
My_TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3,&My_TIM_TimeBaseInitStructure);//TIM3的CH1作为输入捕获

//配置TIM3输入捕获单元
TIM_ICInitTypeDef My_TIM_InitStructure;
My_TIM_InitStructure.TIM_Channel = TIM_Channel_1;//选择定时器通道
My_TIM_InitStructure.TIM_ICFilter = 0xF;//滤波数值
My_TIM_InitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//触发模式,选上升沿触发
My_TIM_InitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//预分频值
My_TIM_InitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//选择触发信号从哪个引脚输入
TIM_ICInit(TIM3,&My_TIM_InitStructure);

//裴PWMI
TIM_PWMIConfig(TIM3,&My_TIM_InitStructure);

//配置PA6GPIO口为输入模式
My_GPIOInitStructure.GPIO_Pin = GPIO_Pin_6;
My_GPIOInitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
My_GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&My_GPIOInitStructure);

//配置从模式的触发源
TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);

//配置从模式为Reset
TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);

//启动定时器TIM3
TIM_Cmd(TIM3,ENABLE);
}

void Task02_PWM_SetComparel(uint16_t Comparel)
{
TIM_SetCompare1(TIM2,Comparel);
}

void Task02_PWM_SetPrescaler(uint16_t Prescaler)
{
TIM_PrescalerConfig(TIM2,Prescaler,TIM_PSCReloadMode_Immediate);
}

uint32_t Task02_IC_GetFreq(void)
{
return 1000000 / (TIM_GetCapture1(TIM3) + 1);//+1消除误差
}

uint32_t Task02_IC_GetDuty(void)
{
return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1);
}

6-7 TIM编码器接口

配置编码器测量步骤:

1.RCC开启时钟,开启GPIO和定时器时钟

2.配置相关GPIO,将CH1和CH2的IO口设置为输入模式

3.配置时基单元,预分频器一般不分频,自动重装一般给最大65535

4.配置输入捕获单元,只需配置通道、滤波器和极性即可,其他参数用不上

5.配置编码器接口模式,直接调用库函数

6.调用TIM_Cmd启动定时器

6-8 编码器接口测速

main.c

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
#include "stm32f10x.h"                  // Device header
#include "encoder.h"
#include "OLED_I2C.h"
#include "delay.h"
#include <stdio.h>
//编码器接口测速
//PA6接A相 PA7接B相 分别是TIM3的CH1 和 TIM3的CH2

uint16_t temp = 0;

void DisplayIntegerValue(int value)
{
char str[16]; // 用于存储转换后的字符串
snprintf(str, sizeof(str), "temp: %d", value); // 将整数转换为字符串
OLED_ShowStr(1, 1, (unsigned char *)str, 1); // 在OLED上显示字符串
}

int main(void)
{
DelayInit();
I2C_Configuration();
OLED_Init();

Encoder_Init();
OLED_Fill(0xFF);
DelayS(2);

OLED_CLS();
while (1)
{
temp = Encoder_Get();
DisplayIntegerValue(temp);
DelayS(1);
}
}

 encoder.c

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
#include "encoder.h"

void Encoder_Init()
{
//开启RCC时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

//配置GPIO,CH1和CH2都配成输入模式 PA6 PA7
GPIO_InitTypeDef My_GPIO_InitStructure;
My_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
My_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
My_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&My_GPIO_InitStructure);

//配置时基单元
TIM_TimeBaseInitTypeDef My_TIM_TimeBaseInitStructure;
My_TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
My_TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
My_TIM_TimeBaseInitStructure.TIM_Period = 65535 - 1;//ARR
My_TIM_TimeBaseInitStructure.TIM_Prescaler = 1-1;//PSC
My_TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3,&My_TIM_TimeBaseInitStructure);

//配置输入捕获单元
TIM_ICInitTypeDef My_TIM_ICInitStructure;
TIM_ICStructInit(&My_TIM_ICInitStructure);
My_TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
My_TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
My_TIM_ICInitStructure.TIM_ICFilter = 0xF;
TIM_ICInit(TIM3,&My_TIM_ICInitStructure);
My_TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
My_TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
My_TIM_ICInitStructure.TIM_ICFilter = 0xF;
TIM_ICInit(TIM3,&My_TIM_ICInitStructure);

//配置编码器接口
TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);

//启动定时器
TIM_Cmd(TIM2,ENABLE);
}

int16_t Encoder_Get(void)
{
int16_t temp;
temp = TIM_GetCounter(TIM3);
TIM_SetCounter(TIM3,0);
return temp;
}

7-1 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
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
87
88
89
90
91
//恢复初始化
void ADC_DeInit(ADC_TypeDef* ADCx);

//ADC模块初始化
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);

//ADC配置结构体初始化
void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);

//控制ADC开启或关闭
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);

//开启DMA输出信号的
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);

//中断输出控制
void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState);

//复位校准
void ADC_ResetCalibration(ADC_TypeDef* ADCx);

//获取复位校准状态
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);

//开始校准
void ADC_StartCalibration(ADC_TypeDef* ADCx);

//获取开始校准状态
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);

//ADC软件开始转换控制,调用就能软件触发转换
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);

//ADC获取软件开始转换状态,一般很少用
FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx);

//下面两个函数是配置间断模式的
//每个几个通道间断一次
void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number);
//是否启动间断模式
void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);

//ADC规则组通道测试
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);

//ADC外部触发转换控制,即是否允许外部触发转换
void ADC_ExternalTrigConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);

//ADC获取转换值
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);

//ADC获取双模式转换值
uint32_t ADC_GetDualModeConversionValue(void);

//这部分都是对注入组进行配置的
void ADC_AutoInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_InjectedDiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_ExternalTrigInjectedConvConfig(ADC_TypeDef* ADCx, uint32_t
ADC_ExternalTrigInjecConv);
void ADC_ExternalTrigInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_SoftwareStartInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
FlagStatus ADC_GetSoftwareStartInjectedConvCmdStatus(ADC_TypeDef* ADCx);
void ADC_InjectedChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
void ADC_InjectedSequencerLengthConfig(ADC_TypeDef* ADCx, uint8_t Length);
void ADC_SetInjectedOffset(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel, uint16_t Offset);
uint16_t ADC_GetInjectedConversionValue(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel);

//是否启动模拟看门狗
void ADC_AnalogWatchdogCmd(ADC_TypeDef* ADCx, uint32_t ADC_AnalogWatchdog);

//配置高低阈值
void ADC_AnalogWatchdogThresholdsConfig(ADC_TypeDef* ADCx, uint16_t HighThreshold, uint16_t
LowThreshold);

//配置看门的通道
void ADC_AnalogWatchdogSingleChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel);

//ADC温度传感器,内部参考电压控制
void ADC_TempSensorVrefintCmd(FunctionalState NewState);

//获取标志位状态
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);

//清除标志位
void ADC_ClearFlag(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);

//获取中断标志位
ITStatus ADC_GetITStatus(ADC_TypeDef* ADCx, uint16_t ADC_IT);

//清除中断挂起位
void ADC_ClearITPendingBit(ADC_TypeDef* ADCx, uint16_t ADC_IT);

7-2 AD单通道&AD多通道

STM32f103c8t6只有ADC1和ADC2,且无PC部分的端口

有四种转换模式

配置ADC的步骤:

1.开启RCC时钟,包括ADC和GPIO时钟,ADCCLK的分配器也需配置RCC_用ADCCLKConfig()

2.配置GPIO,将信号输入的端口配置成模拟输入

3.配置多路开关,将左边的通道接入到右边的规则组列表里

4.配置ADC转换器,结构体里面的参数有选择转换模式,是否扫描,通道个数设置,触发源设置,数据对齐方式等。

5.开启ADC,调用ADC_Cmd函数。如若需要,可加入校准步骤。

 main.c

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
#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "delay.h"
#include "task01.h"
#include "task02.h"


//任务1 AD单通道
//电位器:一个可调电阻,有三个端口,其中两个连接正负极,一个连接A0
uint16_t task01_AD_value;
float task01_Volatge;

void Task01()
{
OLED_Init();
Task01_Init();
OLED_ShowString(1,1,"ADValue:");
OLED_ShowString(2,1,"Volatge:0.00V");
while(1)
{
task01_AD_value = Task01_AD_GetValue();
task01_Volatge = (float)task01_AD_value / 4095 * 3.3;
OLED_ShowNum(1,9,task01_AD_value,4);
OLED_ShowNum(2,9,task01_Volatge,1);
OLED_ShowNum(2,11,(uint16_t)(task01_Volatge * 100) % 100,2);

Delay_ms(100);
}
}


//任务2 AD多通道
//PA0接电位器 PA1接光温传感器 PA2接热敏传感器 PA3接反射式红外传感器
uint16_t AD0;
uint16_t AD1;
uint16_t AD2;
uint16_t AD3;
void Task02()
{
OLED_Init();
Task02_Init();
OLED_ShowString(1,1,"AD0:0000");
OLED_ShowString(2,1,"AD1:9999");
OLED_ShowString(3,1,"AD2:9999");
OLED_ShowString(4,1,"AD3:9999");
while(1)
{
AD0 = Task02_AD_GetValue(ADC_Channel_0);
AD1 = Task02_AD_GetValue(ADC_Channel_1);
AD2 = Task02_AD_GetValue(ADC_Channel_2);
AD3 = Task02_AD_GetValue(ADC_Channel_3);

OLED_ShowNum(1,5,AD0,4);
OLED_ShowNum(2,5,AD1,4);
OLED_ShowNum(3,5,AD2,4);
OLED_ShowNum(4,5,AD3,4);

Delay_ms(100);
}
}

int main(void)
{
// Task02();
while (1)
{

}
}

task01.c

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
#include "task01.h"

//配置单次转换,非扫描模式ADC
void Task01_Init()
{
//开启RCC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);

//ADCCLK分配器配置
RCC_ADCCLKConfig(RCC_PCLK2_Div6);

//配置GPIO为模拟输入模式
GPIO_InitTypeDef My_GPIO_InitStructure;
My_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
My_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模拟输入模式,AIN模式是ADC的专属模式
My_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&My_GPIO_InitStructure);

//选择规则组的输入通道
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);

//初始化ADC
ADC_InitTypeDef My_ADC_InitStructure;
My_ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//单次转换模式
My_ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//数据右对齐
My_ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//外部触发源选择,这里是无触发源,用软件触发就选这个参数
My_ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//独立模式
My_ADC_InitStructure.ADC_NbrOfChannel = 1;//选择通道一
My_ADC_InitStructure.ADC_ScanConvMode = DISABLE;//单通道模式
ADC_Init(ADC1,&My_ADC_InitStructure);

//开启ADC电源
ADC_Cmd(ADC1,ENABLE);

//校准
//1.复位校准
ADC_ResetCalibration(ADC1);
//2.等待复位校准完成
while(ADC_GetResetCalibrationStatus(ADC1) == SET);
//3.开始校准
ADC_StartCalibration(ADC1);
//4.获取校准标志位,等待校准完成
while(ADC_GetCalibrationStatus(ADC1) == SET);
}

//获取ADC的数据
uint16_t Task01_AD_GetValue(void)
{
ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发转换
while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET);//等待转换完成
return ADC_GetConversionValue(ADC1);
}
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
#include "task02.h"

void Task02_Init()
{
//开启RCC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);

//配置ADCCLK预分频器
RCC_ADCCLKConfig(RCC_PCLK2_Div6);

//配置GPIO为模拟输入
GPIO_InitTypeDef My_GPIO_InitStructure;
My_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
My_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
My_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&My_GPIO_InitStructure);

//ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);

//初始化ADC
ADC_InitTypeDef My_ADC_InitStructure;
My_ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//单次转换
My_ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//数据右对齐
My_ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//无触发源,由软件触发
My_ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//独立模式
My_ADC_InitStructure.ADC_NbrOfChannel = 1;//选择通道1
My_ADC_InitStructure.ADC_ScanConvMode = DISABLE;//单通道模式
ADC_Init(ADC1,&My_ADC_InitStructure);

//开启ADC电源
ADC_Cmd(ADC1,ENABLE);

//校准数据
//复位校准
ADC_ResetCalibration(ADC1);
//等待复位校准完成
while(ADC_GetResetCalibrationStatus(ADC1) == SET);
//开始校准
ADC_StartCalibration(ADC1);
//等待校准完成
while(ADC_GetCalibrationStatus(ADC1) == SET);
}

uint16_t Task02_AD_GetValue(uint8_t ADC_Channel)
{
//选择规则组的输入通道
ADC_RegularChannelConfig(ADC1,ADC_Channel,1,ADC_SampleTime_55Cycles5);
ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发转换
while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET);//等待转换完成
return ADC_GetConversionValue(ADC1); //获取数据
}

8-1 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
 
//恢复初始化DMA
void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx);

//初始化DMA
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);

//DMA结构体赋默认值
void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct);

//DMA是否启动
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);

//DMA中断配置
void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);

//DMA设置当前数据寄存器,即设置传输计数器
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber);

//DMA获取当前数据寄存器
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);

//获取标志位
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);

//清除标志位
void DMA_ClearFlag(uint32_t DMAy_FLAG);

//获取中断标志位
ITStatus DMA_GetITStatus(uint32_t DMAy_IT);

//清除中断标志位
void DMA_ClearITPendingBit(uint32_t DMAy_IT);

8-2 DMA数据转运&DMA+AD多通道

配置DMA的步骤:

1.启动RCC时钟

2.调用DMA初始化函数DMA_Init,配置参数

3.开启DMA,调用DMA_Cmd

4.(如果要硬件触发,要调用XXX_MDACmd,开启触发信号的输出)

5.(如果要DMA中断,调用DMA_ITConfig,开启中断输出)

 main.c

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 "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "task01.h"
#include "task02.h"
#include "delay.h"


//任务1 软件触发DMA转运数据
uint8_t DataA[] = {0x01,0x02,0x03,0x04};
uint8_t DataB[] = {00,00,00,00};

void Task01()
{
OLED_Init();
Task01_Init((uint32_t)DataA,(uint32_t)DataB,4);
OLED_ShowHexNum(1,1,DataA[0],2);
OLED_ShowHexNum(1,5,DataA[1],2);
OLED_ShowHexNum(1,9,DataA[2],2);
OLED_ShowHexNum(1,14,DataA[3],2);
while(1)
{
OLED_ShowHexNum(4,1,DataB[0],2);
OLED_ShowHexNum(4,5,DataB[1],2);
OLED_ShowHexNum(4,9,DataB[2],2);
OLED_ShowHexNum(4,14,DataB[3],2);

for(uint16_t i = 0;i < 4;i++)
{
DataA[i] += 0x02;
}

Task01_DMA_Transfer();
Delay_ms(100);

}
}


//ADC作为外设多通道DMA运输
//PA0接电位器 PA1接光温传感器 PA2接温度传感器 PA3接红外传感器

void Task02()
{
OLED_Init();
Task02_Init();
while(1)
{
Task02_AD_GetValue();
// AD_Value[0] = Task02_AD_GetValue(ADC_Channel_0);
// AD_Value[1] = Task02_AD_GetValue(ADC_Channel_1);
// AD_Value[2] = Task02_AD_GetValue(ADC_Channel_2);
// AD_Value[3] = Task02_AD_GetValue(ADC_Channel_3);

// Task02_DMA_Transfer();

OLED_ShowNum(1,1,AD_Value[0],4);
OLED_ShowNum(1,6,AD_Value[1],4);
OLED_ShowNum(2,1,AD_Value[2],4);
OLED_ShowNum(2,6,AD_Value[3],4);

// OLED_ShowNum(3,1,Value[0],4);
// OLED_ShowNum(3,6,Value[1],4);
// OLED_ShowNum(4,1,Value[2],4);
// OLED_ShowNum(4,6,Value[3],4);
Delay_ms(300);
}
}

int main(void)
{
// Task02();
while (1)
{

}
}

 task01.c

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
#include "task01.h"

uint16_t My_Size;

void Task01_Init(uint32_t ADDrA,uint32_t ADDrB,uint16_t Size)//数据从ADDrA->ADDrB,A是外设,B是存储器
{
My_Size = Size;
//初始化DMA
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);//开启RCC时钟

DMA_Cmd(DMA1_Channel1,DISABLE);//先失能DMA

DMA_InitTypeDef My_DMA_InitStructure;
My_DMA_InitStructure.DMA_BufferSize = Size;//缓存区大小,即传输计数器 0~65535
My_DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//传输方向
//DST是指外设站点作为目的地,SRC是指外设站点作为源头
My_DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;//是否使用存储器到存储器,即选择软件触发还是硬件触发
My_DMA_InitStructure.DMA_MemoryBaseAddr = ADDrB;//存储器站点的起始地址
My_DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//存储器站点的数据宽度
My_DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器站点地址是否递增
My_DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//传输模式,选择是否使用自动重装
//DMA的软件触发和自动重装不能同时启用,否则无法停下
My_DMA_InitStructure.DMA_PeripheralBaseAddr = ADDrA;//外设站点的起始地址
My_DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设站点的数据宽度
My_DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;//外设外设站点地址是否递增。
My_DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//优先级配置。
DMA_Init(DMA1_Channel1,&My_DMA_InitStructure);

DMA_Cmd(DMA1_Channel1,ENABLE);
}

void Task01_DMA_Transfer(void)
{

DMA_Cmd(DMA1_Channel1,DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel1,My_Size);
DMA_Cmd(DMA1_Channel1,ENABLE);

//等待转运完成
while(DMA_GetFlagStatus(DMA1_FLAG_GL1) == RESET);
DMA_ClearFlag(DMA1_FLAG_GL1);
}

task02.c 

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
#include "task02.h"

uint16_t AD_Value[4];

void Task02_Init()
{

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);//开启DMA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);//开启ADC1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GPIOA时钟

//配置ADC
GPIO_InitTypeDef My_GPIO_InitStructure;
My_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 ;
My_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
My_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&My_GPIO_InitStructure); //配置GPIO

RCC_ADCCLKConfig(RCC_PCLK2_Div6); //ADC预分频

ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1,ADC_Channel_3,4,ADC_SampleTime_55Cycles5);

ADC_InitTypeDef My_ADC_InitStructure;
My_ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//是否连续转换模式
My_ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//数据右对齐
My_ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//不接触发源
My_ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC为独立模式
My_ADC_InitStructure.ADC_NbrOfChannel = 4;
My_ADC_InitStructure.ADC_ScanConvMode = ENABLE;//配置多通道模式
ADC_Init(ADC1,&My_ADC_InitStructure);//初始化ADC


DMA_Cmd(DMA1_Channel1,DISABLE);//先失能DMA

DMA_InitTypeDef My_DMA_InitStructure;
My_DMA_InitStructure.DMA_BufferSize = 4;//缓存区大小,即传输计数器 0~65535
My_DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//传输方向
//DST是指外设站点作为目的地,SRC是指外设站点作为源头
My_DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//是否使用存储器到存储器,即选择软件触发还是硬件触发
My_DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;//存储器站点的起始地址
My_DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord;//存储器站点的数据宽度
My_DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器站点地址是否递增
My_DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//传输模式,选择是否使用自动重装
//DMA的软件触发和自动重装不能同时启用,否则无法停下
My_DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;//外设站点的起始地址
My_DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外设站点的数据宽度
My_DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设外设站点地址是否递增。
My_DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//优先级配置。
DMA_Init(DMA1_Channel1,&My_DMA_InitStructure);

DMA_Cmd(DMA1_Channel1,ENABLE);//使能DMA
ADC_DMACmd(ADC1,ENABLE);//将ADC触发DMA开启
ADC_Cmd(ADC1,ENABLE);//开启ADC

//因使用多通道的ADC,要检验数据
//复位校准
ADC_ResetCalibration(ADC1);
//等待校准复位完毕
while(ADC_GetResetCalibrationStatus(ADC1) == SET);
//开启校准
ADC_StartCalibration(ADC1);
//等待校准完毕
while(ADC_GetCalibrationStatus(ADC1) == SET);
}


void Task02_AD_GetValue(void)
{
DMA_Cmd(DMA1_Channel1,DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel1,4);
DMA_Cmd(DMA1_Channel1,ENABLE);

ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发转换

//等待转运完成
while(DMA_GetFlagStatus(DMA1_FLAG_GL1) == RESET);
DMA_ClearFlag(DMA1_FLAG_GL1);
}

9-1 USART串口协议

9-2 USART串口外设

 

 

 

 

9-3 串口发送&串口发送+接收

配置串口步骤:
1.开启时钟,将要用的USART和GPIO时钟打开
2.GPIO初始化,将TX配置成复用输出,RX配置成输入
3.USART初始化,若需要接收功能,则另外要配置中断,只发送就不用配置中断
4.配置中断,加上ITConfig和NVIC的配置
5.开启USART

 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
77
78
79
80
81
82
83
84
85
86
87
 
//恢复串口初始化
void USART_DeInit(USART_TypeDef* USARTx);

//串口初始化配置函数
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);

//给串口配置结构体赋默认值
void USART_StructInit(USART_InitTypeDef* USART_InitStruct);

//配置同步时钟输出
void USART_ClockInit(USART_TypeDef* USARTx, USART_ClockInitTypeDef* USART_ClockInitStruct);

//给串口同步时钟配置结构体赋默认值
void USART_ClockStructInit(USART_ClockInitTypeDef* USART_ClockInitStruct);

//串口启停控制
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);

//串口中断配置
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);

//串口DMA启停,开启USART到MDA的通道
void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState NewState);

//设置地址
void USART_SetAddress(USART_TypeDef* USARTx, uint8_t USART_Address);

//唤醒串口
void USART_WakeUpConfig(USART_TypeDef* USARTx, uint16_t USART_WakeUp);

//接收唤醒启停
void USART_ReceiverWakeUpCmd(USART_TypeDef* USARTx, FunctionalState NewState);

//设置串口LINBreak检测长度
void USART_LINBreakDetectLengthConfig(USART_TypeDef* USARTx, uint16_t USART_LINBreakDetectLength);

//串口LINBreak启停
void USART_LINCmd(USART_TypeDef* USARTx, FunctionalState NewState);

//串口发送数据,写DR寄存器
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);

//串口接收数据,读DR寄存器
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);

//串口发送返回字符
void USART_SendBreak(USART_TypeDef* USARTx);

//设置指定的串口保护时间
void USART_SetGuardTime(USART_TypeDef* USARTx, uint8_t USART_GuardTime);

//设置串口系统时钟预调器
void USART_SetPrescaler(USART_TypeDef* USARTx, uint8_t USART_Prescaler);

//串口智能卡模式启停
void USART_SmartCardCmd(USART_TypeDef* USARTx, FunctionalState NewState);

//串口NACK传输启停
void USART_SmartCardNACKCmd(USART_TypeDef* USARTx, FunctionalState NewState);

//串口半双工通信启停
void USART_HalfDuplexCmd(USART_TypeDef* USARTx, FunctionalState NewState);

//串口8倍过采样模式启停
void USART_OverSampling8Cmd(USART_TypeDef* USARTx, FunctionalState NewState);

//串口USART的一位采样方法启停
void USART_OneBitMethodCmd(USART_TypeDef* USARTx, FunctionalState NewState);

//配置串口的IrDA接口
void USART_IrDAConfig(USART_TypeDef* USARTx, uint16_t USART_IrDAMode);

//串口的IrDA接口启停
void USART_IrDACmd(USART_TypeDef* USARTx, FunctionalState NewState);

//获取串口标志位
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);

//清除串口标志位
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);

//获取串口中断标志位
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);

//清除串口中断标志位
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);

 main.c

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
#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "task01.h"
#include "task02.h"
#include "delay.h"

//任务1 串口发送 PA9是RX PA10TX
void Task01()
{
OLED_Init();
Task01_Init();
Task01_USART1_SendByte(0x41);

/第一种串口输出,通过对USART_SendData的封装达成功能
uint16_t Array[4] = {0x11,0x12,0x13,0x14};
Task01_Serial_SendArray(Array,4);
Task01_Serial_SendString("HELLOWORLD");
Task01_Serial_SendNumber(12313123,8);
Task01_USART1_SendByte('A');

printf("你好\r\n");//重定义printf输出到串口中

/第二种串口输出:使用sprintf,适合多串口输出//
char String[100];
sprintf(String,"hello%d\r\n",1214123);
Task01_Serial_SendString(String);

/第三种串口输出:变参函数封装///
Task01_Serial_Printf("NUM = %d\r\n",123);
while(1)
{
// USART_SendData(USART1,'A');
// while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);//等待发送完成
// Delay_ms(50);
}
}

//任务2 串口发送+接收
void Task02()
{
OLED_Init();
OLED_ShowString(4,1,"HELLO");
Task02_Init();
uint8_t Data;
while(1)
{
if(Task02_GetSerial_RxFlag() == 1)
{
Data = Task02_GetSerial_RxData();
Task02_USART1_SendByte(Data);
OLED_ShowHexNum(1,1,Data,2);
}
}
}


int main(void)
{
Task01();
while (1)
{


}
}

 task01.c

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
87
88
89
90
91
92
93
94
95
96
97
98
99
#include "task01.h"

void Task01_Init(void)
{
//开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);


//GPIO初始化 配置成复用输入输出 PA9是TX PA10RX
GPIO_InitTypeDef My_GPIO_InitStructure;
My_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
My_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
My_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&My_GPIO_InitStructure);
// My_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
// My_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
// GPIO_Init(GPIOA,&My_GPIO_InitStructure);

//串口初始化
USART_InitTypeDef My_USART_InitStructure;
My_USART_InitStructure.USART_BaudRate = 9600;//波特率
My_USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//不使用硬件流控模式
My_USART_InitStructure.USART_Mode = USART_Mode_Tx;//配置为发送模式
My_USART_InitStructure.USART_Parity = USART_Parity_No;//无校验位
My_USART_InitStructure.USART_StopBits = USART_StopBits_1;//1个停止位
My_USART_InitStructure.USART_WordLength = USART_WordLength_8b;//数据位数
USART_Init(USART1,&My_USART_InitStructure);

//使能串口
USART_Cmd(USART1,ENABLE);

}

void Task01_USART1_SendByte(uint8_t Data)
{
USART_SendData(USART1,Data);//发送后需要等待数据全部发送完成
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);//等待发送完成
//标志位无需清零,每次调用SendData函数的时候会清零
}

//发送数组
void Task01_Serial_SendArray(uint16_t *Array,uint32_t Length)
{
uint16_t i;
for(i = 0;i < Length; i++)
{
Task01_USART1_SendByte(Array[i]);
}
}

//发送字符串
void Task01_Serial_SendString(char *String)// /r/n可以回车换行
{
uint16_t i;
for(i = 0;String[i] != '\0';i++)
{
Task01_USART1_SendByte(String[i]);
}
}

//辅助发送数字的函数,输出的是x的y次方
uint32_t Task01_Serial_Pow(uint32_t X,uint32_t Y)
{
uint32_t Resuit = 1;
while(Y--)
{
Resuit *= X;
}
return Resuit;
}

//发送数字
void Task01_Serial_SendNumber(uint32_t Number,uint8_t Length)
{
uint16_t i;
for(i = 0;i < Length;i++)
{
Task01_USART1_SendByte(Number / Task01_Serial_Pow(10,Length - i - 1) % 10 + '0');
}
}

//printf重定向
int fputc(int ch,FILE *f)
{
Task01_USART1_SendByte(ch);
return ch;

}

void Task01_Serial_Printf(char *format,...)
{
char String[100];
va_list arg;
va_start(arg,format);
vsprintf(String,format,arg);
va_end(arg);
Task01_Serial_SendString(String);
}

task01.c 

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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include "task02.h"


void Task02_Init(void)
{
//初始化串口,让其发送+接收

//开始RCC
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);


//初始化GPIO PA9是单片机的TX PA10是单片机的RX
GPIO_InitTypeDef My_GPIO_IniterStructure;
My_GPIO_IniterStructure.GPIO_Pin = GPIO_Pin_9;
My_GPIO_IniterStructure.GPIO_Mode = GPIO_Mode_AF_PP;//输出口配为复用输出
My_GPIO_IniterStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&My_GPIO_IniterStructure);
My_GPIO_IniterStructure.GPIO_Pin = GPIO_Pin_10;
My_GPIO_IniterStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//输入口配为复用浮空输入
GPIO_Init(GPIOA,&My_GPIO_IniterStructure);

//USART初始化
USART_InitTypeDef My_USART_InitStructure;
My_USART_InitStructure.USART_BaudRate = 9600;//波特率设置
My_USART_InitStructure.USART_HardwareFlowControl = DISABLE;//禁用硬件流控制
My_USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//配置为发送+接收的模式
My_USART_InitStructure.USART_Parity = USART_Parity_No;//不使用校验位
My_USART_InitStructure.USART_StopBits = USART_StopBits_1;//1个停止位
My_USART_InitStructure.USART_WordLength = USART_WordLength_8b;//有效数据长度为八位
USART_Init(USART1,&My_USART_InitStructure);

//接收模式可配置中断
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//开启串口的接收中断、
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//NVIC分组
NVIC_InitTypeDef My_NVIC_InitStructure;
My_NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
My_NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
My_NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
My_NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&My_NVIC_InitStructure);//初始化NVIC(中断管理器)


//使能串口
USART_Cmd(USART1,ENABLE);
}

//串口发送函数
void Task02_USART1_SendByte(uint8_t Data)
{
USART_SendData(USART1,Data);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);//等待发送完成
//标志位不用清零,每次使用USART_SendData时会自动清零
}

//串口发送数组
void Task02_Serial_SendArray(uint16_t *Array,uint32_t Length)
{
uint16_t i;
for(i = 0;i < Length;i++)
{
Task02_USART1_SendByte(Array[i]);
}
}

//串口发送字符串
void Task02_Serial_SendString(char *String)
{
uint16_t i;
for(i = 0;String[i] != '\0';i++)
{
Task02_USART1_SendByte(String[i]);
}
}

//辅助发送数字的函数
uint32_t Task02_Serial_Pow(uint32_t X,uint32_t Y)
{
uint32_t Resuit = 1;
while(Y--)
{
Resuit *= X;
}
return Resuit;
}

//串口发送数字
void Task02_Serial_SendNumber(int32_t Number,uint8_t Length)
{
uint16_t i;
for(i = 0;i < Length;i++)
{
Task02_USART1_SendByte(Number / Task02_Serial_Pow(10,Length - i - 1) % 10 + '0');
}
}

printf重定向
//int fputc(int ch,FILE *f)
//{
// Task02_USART1_SendByte(ch);
// return ch;
//
//}

uint8_t Task02_Serial_RxData;
uint8_t Task02_Serial_RxFlag;

uint8_t Task02_GetSerial_RxFlag(void)
{
if(Task02_Serial_RxFlag == 1)
{
Task02_Serial_RxFlag = 0;
return 1;
}
return 0;
}

uint8_t Task02_GetSerial_RxData(void)
{
return Task02_Serial_RxData;
}

void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1,USART_IT_RXNE) == SET)
{
Task02_Serial_RxData = USART_ReceiveData(USART1);
Task02_Serial_RxFlag = 1;
USART_ClearITPendingBit(USART1,USART_IT_RXNE);//手动清除中断标志位
}
}