stm32

1-1 stm32介绍

在STM32微控制器中,GPIO(通用输入输出)引脚可以配置为多种模式,以适应不同的应用场景。STM32的GPIO引脚共有8种模式,具体如下:

模式 功能 应用
GPIO_Mode_AIN(模拟输入模式) 将GPIO引脚配置为模拟输入模式。 用于连接模拟信号源,例如ADC(模数转换器)输入。
GPIO_Mode_IN_FLOATING(浮空输入模式) 将GPIO引脚配置为浮空输入模式。 用于读取外部数字信号,例如按键输入。
GPIO_Mode_IPD(下拉输入模式) 将GPIO引脚配置为下拉输入模式。 用于读取外部数字信号,并确保引脚在未连接时保持低电平。
GPIO_Mode_IPU(上拉输入模式) 将GPIO引脚配置为上拉输入模式。 用于读取外部数字信号,并确保引脚在未连接时保持高电平。
GPIO_Mode_Out_OD(开漏输出模式) 将GPIO引脚配置为开漏输出模式。 用于需要电平转换或线与逻辑的场景,例如I2C通信。
GPIO_Mode_Out_PP(推挽输出模式) 将GPIO引脚配置为推挽输出模式。 用于驱动数字信号或外部设备,例如LED、继电器等。
GPIO_Mode_AF_OD(复用开漏模式) 将GPIO引脚配置为复用开漏模式。 用于需要复用功能的开漏输出场景,例如I2C通信
GPIO_Mode_AF_PP(复用推挽模式) 将GPIO引脚配置为复用推挽模式。 用于需要复用功能的推挽输出场景,例如SPI、USART通信。

2-1工程模板

#include "stm32f10x.h"                  // Device header

int main(void)
{
	//使能GPIOA端口的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//定义一个GPIO初始化结构体变量
	GPIO_InitTypeDef GPIO_InitStructure;
	//设置GPIO引脚为推挽输出模式。
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; 
	//选择要配置的GPIO引脚。你可以通过或运算(|)来选择多个引脚,例如GPIO_Pin_0 | GPIO_Pin_1。
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
	//设置GPIO引脚的输出速度。
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	//根据配置初始化GPIO引脚,GPIOA表示要初始化的GPIO端口(这里是GPIOA),&GPIO_InitStructure是配置结构体的地址。
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	while(1)
	{
		
	}

}

工程模板

新建三个文件夹
image
image
image
image
点击
image
image
其他两个文件全部添加
image
添加三个文件路径
image
添加DEfine USE_STDPERIPH_DRIVER
image
image
使用该exe可以删除一些中间文件
image

3-1LED闪烁

接线图(led长脚接正极)
image
代码

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
int main(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_0;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	
	
	GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);//点亮
	while(1)
	{
		//LED闪烁
		//方法一
		//GPIO_ResetBits(GPIOA,GPIO_Pin_0);//输出低电平
		//Delay_ms(100);
		//GPIO_SetBits(GPIOA,GPIO_Pin_0);//输出高电平
		//Delay_ms(100);
		
		//方法二
		GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);
		Delay_ms(500);
		GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);//熄灭
		Delay_ms(500);
		
		//方法三(BitAction)强制转换
		//(BitAction) 用于将数值转换为 BitAction 类型,确保参数类型匹配。0 转换为 BIT_RESET,1 转换为 BIT_SET。
		//GPIO_WriteBit(GPIOA,GPIO_Pin_0,(BitAction)0);
		//Delay_ms(500);
		//GPIO_WriteBit(GPIOA,GPIO_Pin_0,(BitAction)1);//熄灭
		//Delay_ms(500);
	}

}

3-2LED流水

接线图
image
代码

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
int main(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_0;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);

	GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);//点亮
	while(1)
	{
		//低电平亮,所以要加上~取反
		GPIO_Write(GPIOA,~0x0001);// 0000 0000 0000 0001
		Delay_ms(500);
		GPIO_Write(GPIOA,~0x0002);// 0000 0000 0000 0010
		Delay_ms(500);
		GPIO_Write(GPIOA,~0x0004);// 0000 0000 0000 0100
		Delay_ms(500);
		GPIO_Write(GPIOA,~0x0008);// 0000 0000 0000 1000
		Delay_ms(500);
		GPIO_Write(GPIOA,~0x0010);// 0000 0000 0001 0000
		Delay_ms(500);
		GPIO_Write(GPIOA,~0x0020);// 0000 0000 0010 0000
		Delay_ms(500);
		GPIO_Write(GPIOA,~0x0040);// 0000 0000 0100 0000
		Delay_ms(500);
		GPIO_Write(GPIOA,~0x0080);// 0000 0000 1000 0000
		Delay_ms(500);
	}

}

3-3蜂鸣器

接线图
image

代码

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
int main(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; //推挽模式
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	
	
	
	while(1)
	{
		//LED闪烁
		//方法一
		GPIO_ResetBits(GPIOB,GPIO_Pin_12);//输出低电平
		Delay_ms(100);
		GPIO_SetBits(GPIOB,GPIO_Pin_12);//输出高电平
		Delay_ms(100);
		GPIO_ResetBits(GPIOB,GPIO_Pin_12);//输出低电平
		Delay_ms(100);
		GPIO_SetBits(GPIOB,GPIO_Pin_12);//输出高电平
		Delay_ms(700);
		
	}

}

3-4按键控制

模块化设计
创建Hardware文件夹,放.c .h文件
例子LED.c

#include "stm32f10x.h"                  // Device header
/**
  * 函    数:LED初始化
  * 参    数:无
  * 返 回 值:无
  */
void LED_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		//开启GPIOA的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);						//将PA1和PA2引脚初始化为推挽输出
	
	/*设置GPIO初始化后的默认电平*/
	GPIO_SetBits(GPIOA, GPIO_Pin_1 | GPIO_Pin_2);				//设置PA1和PA2引脚为高电平
}

/**
  * 函    数:LED1开启
  * 参    数:无
  * 返 回 值:无
  */
void LED1_ON(void)
{
	GPIO_ResetBits(GPIOA, GPIO_Pin_1);		//设置PA1引脚为低电平
}

/**
  * 函    数:LED1关闭
  * 参    数:无
  * 返 回 值:无
  */
void LED1_OFF(void)
{
	GPIO_SetBits(GPIOA, GPIO_Pin_1);		//设置PA1引脚为高电平
}

/**
  * 函    数:LED1状态翻转
  * 参    数:无
  * 返 回 值:无
  */
void LED1_Turn(void)
{
	if (GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_1) == 0)		//获取输出寄存器的状态,如果当前引脚输出低电平
	{
		GPIO_SetBits(GPIOA, GPIO_Pin_1);					//则设置PA1引脚为高电平
	}
	else													//否则,即当前引脚输出高电平
	{
		GPIO_ResetBits(GPIOA, GPIO_Pin_1);					//则设置PA1引脚为低电平
	}
}

/**
  * 函    数:LED2开启
  * 参    数:无
  * 返 回 值:无
  */
void LED2_ON(void)
{
	GPIO_ResetBits(GPIOA, GPIO_Pin_2);		//设置PA2引脚为低电平
}

/**
  * 函    数:LED2关闭
  * 参    数:无
  * 返 回 值:无
  */
void LED2_OFF(void)
{
	GPIO_SetBits(GPIOA, GPIO_Pin_2);		//设置PA2引脚为高电平
}

/**
  * 函    数:LED2状态翻转
  * 参    数:无
  * 返 回 值:无
  */
void LED2_Turn(void)
{
	if (GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_2) == 0)		//获取输出寄存器的状态,如果当前引脚输出低电平
	{                                                  
		GPIO_SetBits(GPIOA, GPIO_Pin_2);               		//则设置PA2引脚为高电平
	}                                                  
	else                                               		//否则,即当前引脚输出高电平
	{                                                  
		GPIO_ResetBits(GPIOA, GPIO_Pin_2);             		//则设置PA2引脚为低电平
	}
}

LED.h

#ifndef __LED_H
#define __LED_H

void LED_Init(void);
void LED1_ON(void);
void LED1_OFF(void);
void LED1_Turn(void);
void LED2_ON(void);
void LED2_OFF(void);
void LED2_Turn(void);

#endif

KEY.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

/**
  * 函    数:按键初始化
  * 参    数:无
  * 返 回 值:无
  */
void Key_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启GPIOB的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);						//将PB1和PB11引脚初始化为上拉输入
}

/**
  * 函    数:按键获取键码
  * 参    数:无
  * 返 回 值:按下按键的键码值,范围:0~2,返回0代表没有按键按下
  * 注意事项:此函数是阻塞式操作,当按键按住不放时,函数会卡住,直到按键松手
  */
uint8_t Key_GetNum(void)
{
	uint8_t KeyNum = 0;		//定义变量,默认键码值为0
	
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)			//读PB1输入寄存器的状态,如果为0,则代表按键1按下
	{
		Delay_ms(20);											//延时消抖
		while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);	//等待按键松手
		Delay_ms(20);											//延时消抖
		KeyNum = 1;												//置键码为1
	}
	
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)			//读PB11输入寄存器的状态,如果为0,则代表按键2按下
	{
		Delay_ms(20);											//延时消抖
		while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);	//等待按键松手
		Delay_ms(20);											//延时消抖
		KeyNum = 2;												//置键码为2
	}
	
	return KeyNum;			//返回键码值,如果没有按键按下,所有if都不成立,则键码为默认值0
}

KEY.h

#ifndef __KEY_H
#define __KEY_H

void Key_Init(void);
uint8_t Key_GetNum(void);

#endif

按键控制代码

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "Key.h"
#include "LED.h"

uint8_t KeyNum;
int main(void)
{
	LED_Init();
	Key_Init();
	while(1)
	{
	 KeyNum = Key_GetNum();
		if(KeyNum ==1)
		{
		LED1_Turn();
		}
		if(KeyNum ==2)
		{
			LED2_Turn();
		}
		
	}

}

4-1OLED显示屏

image
OLED.c

#include "stm32f10x.h"
#include "OLED_Font.h"

/*引脚配置*/
#define OLED_W_SCL(x)		GPIO_WriteBit(GPIOB, GPIO_Pin_8, (BitAction)(x))
#define OLED_W_SDA(x)		GPIO_WriteBit(GPIOB, GPIO_Pin_9, (BitAction)(x))

/*引脚初始化*/
void OLED_I2C_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	OLED_W_SCL(1);
	OLED_W_SDA(1);
}

/**
  * @brief  I2C开始
  * @param  无
  * @retval 无
  */
void OLED_I2C_Start(void)
{
	OLED_W_SDA(1);
	OLED_W_SCL(1);
	OLED_W_SDA(0);
	OLED_W_SCL(0);
}

/**
  * @brief  I2C停止
  * @param  无
  * @retval 无
  */
void OLED_I2C_Stop(void)
{
	OLED_W_SDA(0);
	OLED_W_SCL(1);
	OLED_W_SDA(1);
}

/**
  * @brief  I2C发送一个字节
  * @param  Byte 要发送的一个字节
  * @retval 无
  */
void OLED_I2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for (i = 0; i < 8; i++)
	{
		OLED_W_SDA(!!(Byte & (0x80 >> i)));
		OLED_W_SCL(1);
		OLED_W_SCL(0);
	}
	OLED_W_SCL(1);	//额外的一个时钟,不处理应答信号
	OLED_W_SCL(0);
}

/**
  * @brief  OLED写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void OLED_WriteCommand(uint8_t Command)
{
	OLED_I2C_Start();
	OLED_I2C_SendByte(0x78);		//从机地址
	OLED_I2C_SendByte(0x00);		//写命令
	OLED_I2C_SendByte(Command); 
	OLED_I2C_Stop();
}

/**
  * @brief  OLED写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void OLED_WriteData(uint8_t Data)
{
	OLED_I2C_Start();
	OLED_I2C_SendByte(0x78);		//从机地址
	OLED_I2C_SendByte(0x40);		//写数据
	OLED_I2C_SendByte(Data);
	OLED_I2C_Stop();
}

/**
  * @brief  OLED设置光标位置
  * @param  Y 以左上角为原点,向下方向的坐标,范围:0~7
  * @param  X 以左上角为原点,向右方向的坐标,范围:0~127
  * @retval 无
  */
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
	OLED_WriteCommand(0xB0 | Y);					//设置Y位置
	OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4));	//设置X位置高4位
	OLED_WriteCommand(0x00 | (X & 0x0F));			//设置X位置低4位
}

/**
  * @brief  OLED清屏
  * @param  无
  * @retval 无
  */
void OLED_Clear(void)
{  
	uint8_t i, j;
	for (j = 0; j < 8; j++)
	{
		OLED_SetCursor(j, 0);
		for(i = 0; i < 128; i++)
		{
			OLED_WriteData(0x00);
		}
	}
}

/**
  * @brief  OLED显示一个字符
  * @param  Line 行位置,范围:1~4
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的一个字符,范围:ASCII可见字符
  * @retval 无
  */
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{      	
	uint8_t i;
	OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);		//设置光标位置在上半部分
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i]);			//显示上半部分内容
	}
	OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);	//设置光标位置在下半部分
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]);		//显示下半部分内容
	}
}

/**
  * @brief  OLED显示字符串
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串,范围:ASCII可见字符
  * @retval 无
  */
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i++)
	{
		OLED_ShowChar(Line, Column + i, String[i]);
	}
}

/**
  * @brief  OLED次方函数
  * @retval 返回值等于X的Y次方
  */
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y--)
	{
		Result *= X;
	}
	return Result;
}

/**
  * @brief  OLED显示数字(十进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~4294967295
  * @param  Length 要显示数字的长度,范围:1~10
  * @retval 无
  */
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
	}
}

/**
  * @brief  OLED显示数字(十进制,带符号数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-2147483648~2147483647
  * @param  Length 要显示数字的长度,范围:1~10
  * @retval 无
  */
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
	uint8_t i;
	uint32_t Number1;
	if (Number >= 0)
	{
		OLED_ShowChar(Line, Column, '+');
		Number1 = Number;
	}
	else
	{
		OLED_ShowChar(Line, Column, '-');
		Number1 = -Number;
	}
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
	}
}

/**
  * @brief  OLED显示数字(十六进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFFFFFF
  * @param  Length 要显示数字的长度,范围:1~8
  * @retval 无
  */
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i, SingleNumber;
	for (i = 0; i < Length; i++)							
	{
		SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
		if (SingleNumber < 10)
		{
			OLED_ShowChar(Line, Column + i, SingleNumber + '0');
		}
		else
		{
			OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
		}
	}
}

/**
  * @brief  OLED显示数字(二进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
	}
}

/**
  * @brief  OLED初始化
  * @param  无
  * @retval 无
  */
void OLED_Init(void)
{
	uint32_t i, j;
	
	for (i = 0; i < 1000; i++)			//上电延时
	{
		for (j = 0; j < 1000; j++);
	}
	
	OLED_I2C_Init();			//端口初始化
	
	OLED_WriteCommand(0xAE);	//关闭显示
	
	OLED_WriteCommand(0xD5);	//设置显示时钟分频比/振荡器频率
	OLED_WriteCommand(0x80);
	
	OLED_WriteCommand(0xA8);	//设置多路复用率
	OLED_WriteCommand(0x3F);
	
	OLED_WriteCommand(0xD3);	//设置显示偏移
	OLED_WriteCommand(0x00);
	
	OLED_WriteCommand(0x40);	//设置显示开始行
	
	OLED_WriteCommand(0xA1);	//设置左右方向,0xA1正常 0xA0左右反置
	
	OLED_WriteCommand(0xC8);	//设置上下方向,0xC8正常 0xC0上下反置

	OLED_WriteCommand(0xDA);	//设置COM引脚硬件配置
	OLED_WriteCommand(0x12);
	
	OLED_WriteCommand(0x81);	//设置对比度控制
	OLED_WriteCommand(0xCF);

	OLED_WriteCommand(0xD9);	//设置预充电周期
	OLED_WriteCommand(0xF1);

	OLED_WriteCommand(0xDB);	//设置VCOMH取消选择级别
	OLED_WriteCommand(0x30);

	OLED_WriteCommand(0xA4);	//设置整个显示打开/关闭

	OLED_WriteCommand(0xA6);	//设置正常/倒转显示

	OLED_WriteCommand(0x8D);	//设置充电泵
	OLED_WriteCommand(0x14);

	OLED_WriteCommand(0xAF);	//开启显示
		
	OLED_Clear();				//OLED清屏
}

OLED.h

#ifndef __OLED_H
#define __OLED_H

void OLED_Init(void);
void OLED_Clear(void);
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char);
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String);
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length);
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);

#endif

OLED_Font.h

#ifndef __OLED_FONT_H
#define __OLED_FONT_H

/*OLED字模库,宽8像素,高16像素*/
const uint8_t OLED_F8x16[][16]=
{
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//  0
	
	0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00,//! 1
	
	0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//" 2
	
	0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,
	0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00,//# 3
	
	0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,
	0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00,//$ 4
	
	0xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,
	0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00,//% 5
	
	0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,
	0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10,//& 6
			.
			.
			.
			.
	0x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//~ 94
};

#endif

mian.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"

int main(void){
	
	OLED_Init();//初始化
	OLED_ShowChar(1,1,'A');//第一行第一列输出字符A
	OLED_ShowString(1,3,"CHENJIAWEI");//第一行第三列打印字符串
	OLED_ShowSignedNum(3,1,-66,2);//第三行弟1列打印一个有符号的整数-66
	OLED_ShowBinNum(2,1,0xAA55,16); //二进制显示1010101001010101
	OLED_ShowHexNum(3,1,0xAA55,4);//十六进制形式显示AA55
	
	while(1)
	{
	
	}
}

5-1对射式红外传感器计次

EXTI(外部中断)基本结构
image
配置四个
第一步,配置RCC,把我们这里涉及的外设的时钟都打开
第二步,配置GPIO选择我们的端口为输入模式
第三步,配置AFIO。选择我们用的这一路GPI,连接到后面的EXTI
第四步,配置EXTI,选择边沿触发方式,比如上升沿、下降沿或者双边沿,还有选择触发响应方式,可以选择中断响应和事件响应,一般选择终端响应
第五步,配置NVIC,给我们这个中断选择一个合适的优洗级,最后,通过NVIC,外部中断信号就能进入CPU了

CountSensor.c

#include "stm32f10x.h"                  // Device header


uint16_t CountSensor_Count;

void CountSensor_Init(void){

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	
	//配置GPIO
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_14;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	//用于 配置外部中断(EXTI)的 GPIO 引脚映射
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);
	
	//第四步配置EXTI
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line=EXTI_Line14;
	EXTI_InitStructure.EXTI_LineCmd=ENABLE;
	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising_Falling;//可以选择上升沿或者下降沿,或者上升下降
	//(EXTI_Trigger_Rising = 0x08,
    // EXTI_Trigger_Falling = 0x0C,  
    //EXTI_Trigger_Rising_Falling = 0x10)
	
	EXTI_Init(&EXTI_InitStructure);
	
	//第五步配置NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//选择两位抢占两位响应,比较平均
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//响应优先级
	NVIC_Init(&NVIC_InitStructure);
}
uint16_t CountSensor_Get(void)
{
	return CountSensor_Count;
}

void EXTI15_10_IRQHandler(void) //中断函数起动文件
{
	if(EXTI_GetITStatus(EXTI_Line14)== SET)
	{
	CountSensor_Count++;
	EXTI_ClearITPendingBit(EXTI_Line14);
	}
}

CountSensor.h

#ifndef __COUNT_SENSOR_H
#define __COUNT_SENSOR_H

void CountSensor_Init(void);
uint16_t CountSensor_Get(void);
#endif

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "CountSensor.h"
int main(void){
	OLED_Init();
	CountSensor_Init();
	
	
	OLED_ShowString(1,1,"Count:");

	while(1)
	{
		OLED_ShowNum(1,7,CountSensor_Get(),5);
	}
}

6-1定时器定时中断

image
第一步,R©C开启时钟,这个基本上每个代码都是第一步
第三步,选择时基单元的时钟源,对手定时中断。我们就选择内部时钟源
第三步。配置时基弹元,包括这里的预分频器,自动重装器,计数模式等等(使用结构体)
第四步,配置输出中断控制,允许更新中断输出到NVIC
第五步,配置NVIC,在NVM©中打研定时器中断的通道,并分配一个优先级
第六步,就是运行控制了,整个模块配置完成后,我们还需要使能-下计数器

主要函数
void TIM_DeInit(TIM_TypeDef* TIMx);
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);

计数器溢出频率:CK_CNT_OV=CK_CNT/(ARR+1)=CK_PSC/(PSC+1)/(ARR+1)
main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"

uint16_t Num;
int main(void){
	
	OLED_Init();
	Timer_Init();
	
	OLED_ShowString(1,1,"Num:");
	while(1)
	{
	OLED_ShowNum(1,5,Num,5);
		OLED_ShowNum(2,5,TIM_GetCounter(TIM2),5);
	}
}

void TIM2_IRQHandler(void)
{

	if (TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
		{
				Num++;
				TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
		}
}

Timer.c

#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period=10000-1; //自动重装载值 规定10000就是一秒,如果1000就是0.1秒就加一
	TIM_TimeBaseInitStructure.TIM_Prescaler=7200-1; // 预分频值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);

	TIM_ClearFlag(TIM2,TIM_FLAG_Update);
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
	
	//NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVIC_InitStructure);
	
	TIM_Cmd(TIM2,ENABLE);
}

//定时中断启动函数,作为模板复制到main函数中使用
//void TIM2_IRQHandler(void)
//{

//if (TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
//{
//	TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
//}
//}

Timer.h

#ifndef __TIMER_H
#define __TIMER_H

void Timer_Init(void);
void TIM2_IRQHandler(void);
#endif

6-2定时器外部中断

通过 TIM2 的 ETR 模式 实现了外部脉冲计数,每 10 个上升沿 触发一次中断,Num 加 1。
关键配置:

TIM_ETRClockMode2Config:外部时钟模式。
TIM_Period = 9:10 个脉冲溢出一次。
TIM_ITConfig:使能更新中断。
image

Timer.c

#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	//定时器配置为外部时钟模式
	TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x00);
	//将 TIM2 的时钟源配置为 外部触发(ETR)模式,即 PA0 引脚(TIM2_ETR) 的上升沿作为计数时钟。
	//TIM_ExtTRGPSC_OFF:不使用预分频(每个上升沿直接计数)。
	//TIM_ExtTRGPolarity_NonInverted:上升沿触发。
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period=10-1; //计数器从 0 开始,计数到 9 时溢出(共 10 个脉冲)
	TIM_TimeBaseInitStructure.TIM_Prescaler=1-1; // 预分频值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);

	TIM_ClearFlag(TIM2,TIM_FLAG_Update);
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//使能 更新中断,当计数器溢出(从 9 回到 0)时触发中断,在 TIM2_IRQHandler 中执行 Num++。
	
	//NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVIC_InitStructure);
	
	TIM_Cmd(TIM2,ENABLE);
}

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

}
//定时中断启动函数,作为模板复制到main函数中使用
//void TIM2_IRQHandler(void)
//{

//if (TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
//{
//	TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
//}
//}

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"

uint16_t Num;
int main(void){
	
	OLED_Init();
	Timer_Init();
	
	OLED_ShowString(1,1,"Num:");
	OLED_ShowString(2,1,"CNT:");
	while(1)
	{
	OLED_ShowNum(1,5,Num,5);
		OLED_ShowNum(2,5,Timer_GetCounter(),5);
		//OLED_ShowNum(2,5,TIM_GetCounter(TIM2),5);
	}
}

void TIM2_IRQHandler(void)
{

	if (TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
		{
				Num++;
				TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
		}
}

6-3 PWM点亮LED呼吸灯

image
image

第一步,RCC开启时钟,把我们要用的TIM外设和GPIO外设的时钟打开
第三步,配置时基单元,包括这前面的时钟源选择
第三步,配置输出比较单晁
第四步,配置GPIO,把PWM对应的GPIO口,初始化为复用推挽输出的配置

重要函数:

函数 说明
GPIO_Init() 配置 PWM 引脚
TIM_TimeBaseInit() 设置 PWM 频率和分辨率
TIM_OCxInit() 启用 PWM 输出
TIM_SetComparex() 占空比调节,动态调整亮度

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"

uint8_t i;
int main(void){
	
	OLED_Init();
	PWM_Init();

	while(1)
	{
		for(i=0;i<=100;i++)
		{
			PWM_SetCompare1(i);
			Delay_ms(20);
			OLED_ShowNum(1,1,i,4);
		}
		for(i=0;i<=100;i++)
		{
			PWM_SetCompare1(100-i);
			OLED_ShowNum(1,1,100-i,4);
			Delay_ms(20);
		}
	}
}

PWM.h

#ifndef __PWM_H
#define __PWM_H
void PWM_Init(void);
void PWM_SetCompare1(uint16_t Compare);
#endif

PWM.c

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		//开启GPIOA的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);	
	
	//配置 TIM2 定时器使用内部时钟源
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period=100-1; //ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler=720-1; //PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
	
	TIM_OCInitTypeDef TIM_OCInitStructure;
	//PWM结构体初始化赋值,再修改其中部分参数
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_Pulse=50; //CCR
	TIM_OC1Init(TIM2,&TIM_OCInitStructure);
	
	// 启动 TIM2 定时器
	TIM_Cmd(TIM2,ENABLE);
	

}
//设置TIM2定时器通道1的PWM占空比
void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2,Compare);
}

6-4 PWM驱动舵机

image
定时器通道 默认 GPIO 引脚(STM32F10x)
TIM2_CH1--->PA0
TIM2_CH2--->PA1
TIM2_CH3--->PA2
TIM2_CH4--->PA3

Servo.c

#include "stm32f10x.h"                  // Device header
#include "PWM.h"

void Servo_Init(void)
{
	PWM_Init();
	

}

// 0--> 500
// 180度-->2500
void Servo_SetAngle(float Angle)
{

	PWM_SetCompare2(Angle /180*2000+500);
}

Servo.h

#ifndef __SERVO_h
#define __SERVO_H

void Servo_Init(void);
void Servo_SetAngle(float Angle);

#endif

PWM.c

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		//开启GPIOA的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;//Pin1对应通道2
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);	
	
	//配置 TIM2 定时器使用内部时钟源
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period=20000-1; //ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler=72-1; //PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
	
	TIM_OCInitTypeDef TIM_OCInitStructure;
	//PWM结构体初始化赋值,再修改其中部分参数
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_Pulse=0; //CCR
	//初始化通道2
	TIM_OC2Init(TIM2,&TIM_OCInitStructure);
	
	// 启动 TIM2 定时器
	TIM_Cmd(TIM2,ENABLE);
	

}
//设置TIM2定时器通道1的PWM占空比
void PWM_SetCompare2(uint16_t Compare)
{
	TIM_SetCompare2(TIM2,Compare);
}

PWM.h

#ifndef __PWM_H
#define __PWM_H
void PWM_Init(void);
void PWM_SetCompare2(uint16_t Compare);
#endif

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Servo.h"
#include "Key.h"

uint8_t KeyNum;
float Angle;
int main(void){
	
	OLED_Init();
	Servo_Init();
	Key_Init();
	Servo_SetAngle(90);
	
	OLED_ShowString(1,1,"Angle:");
	while(1)
	{
		KeyNum = Key_GetNum();
		if(KeyNum ==1)
		{
			Angle+=30;//每按一次加30°
			if(Angle >180)
			{
				Angle = 0;
			}
		}
		Servo_SetAngle(Angle);
		OLED_ShowNum(1,7,Angle,3);
	}
}

6-5 PWM驱动直流电机

image
由于利用pwm驱动PA2端口,所以是通道3
image
image
Motor.c

#include "stm32f10x.h"                  // Device header
#include "PWM.h"                  // Device header

void Motor_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_4 | GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	PWM_Init();
}
void Motor_SetSpeed(int8_t Speed)
{
	if(Speed >=0)
	{
		GPIO_SetBits(GPIOA,GPIO_Pin_4);
		GPIO_ResetBits(GPIOA,GPIO_Pin_5);
		PWM_SetCompare3(Speed);
	}
	else{
		GPIO_ResetBits(GPIOA,GPIO_Pin_4);
		GPIO_SetBits(GPIOA,GPIO_Pin_5);
		PWM_SetCompare3(-Speed);
	}

}

Motor.h

#ifndef __MOTOR_H
#define __MOTOR_H

void Motor_Init(void);

void Motor_SetSpeed(int8_t Speed);

#endif

PWM.c

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		//开启GPIOA的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;//pin2是通道3
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);	
	
	//配置 TIM2 定时器使用内部时钟源
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period=100-1; //ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler=72-1; //PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
	
	TIM_OCInitTypeDef TIM_OCInitStructure;
	//PWM结构体初始化赋值,再修改其中部分参数
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_Pulse=0; //CCR
	//初始化通道3
	TIM_OC3Init(TIM2,&TIM_OCInitStructure);
	
	// 启动 TIM2 定时器
	TIM_Cmd(TIM2,ENABLE);
	

}
//设置TIM2定时器通道3的PWM占空比
void PWM_SetCompare3(uint16_t Compare)
{
	TIM_SetCompare3(TIM2,Compare);
}

PWM.h

#ifndef __PWM_H
#define __PWM_H
void PWM_Init(void);
void PWM_SetCompare3(uint16_t Compare);
#endif

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Motor.h"
#include "Key.h"


uint8_t KeyNum;
int8_t Speed;
//uint8_t KeyNum uint8_t 无符号 8位整数(0 ~ 255)
//int8_t Speed	int8_t	有符号 8位整数(-128 ~ 127)

int main(void){
	
	OLED_Init();
	Motor_Init();
	Key_Init();
	Motor_SetSpeed(20);
	OLED_ShowString(1,1,"Speed:");
	OLED_ShowString(2,1,"leftButton:=0");//归零
	OLED_ShowString(3,1,"rightButton:+20");
	while(1)
	{
		KeyNum = Key_GetNum();//获取按键值,左边为2,右边为1
		if(KeyNum==1)
		{
			Speed += 20;
			if (Speed>100)
			{
				Speed = -100;
			}
		}
		else
		{
			if(KeyNum==2)
			{
					Speed = 0;
			}
		}
		Motor_SetSpeed(Speed);
		OLED_ShowSignedNum(1,7,Speed,3);
	}
}


6-6输入捕获模式测频率

image
第一步,RCC开启时钟,把GPIO和TIM的时钟打开
第二步,GPIO初始化,把GPIO配置成输入模式
第三步,配置时基单元,让CNT计数器在内部时钟的驱动下自增运行
第四步,配置输入捕获单元,用结构体实现(包括滤波器、极性、直连通道还是交叉通道、分频器这些参数)
第五步,选择从模式的触发源,触发源选择为TI1FP1
第六步。选择触发之后执行的操作,执行reset操作
IC.c

#include "stm32f10x.h"                  // Device header

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

	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	
	TIM_InternalClockConfig(TIM3);
	//配置定时器基本参数
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period=65535-1; //ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler=72-1; //PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
	
	//初始化输入捕获单元
	TIM_ICInitTypeDef TIM_ICInitSturcutre;
	TIM_ICInitSturcutre.TIM_Channel=TIM_Channel_1;
	TIM_ICInitSturcutre.TIM_ICFilter=0xF;
	TIM_ICInitSturcutre.TIM_ICPolarity=TIM_ICPolarity_Rising;
	TIM_ICInitSturcutre.TIM_ICPrescaler=TIM_ICPSC_DIV1;
	TIM_ICInitSturcutre.TIM_ICSelection=TIM_ICSelection_DirectTI;
	TIM_ICInit(TIM3,&TIM_ICInitSturcutre);
	
	//配置TRGI的触发源为TI1FP1
	TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);
	//选择从模式为Reset
	TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);
	//启动定时器
	TIM_Cmd(TIM3,ENABLE);

}
//返回一个最新周期的频率值,单位Hz
uint32_t IC_GetFreq(void)
{
	// 1000000μs = 1秒
	return 1000000/ (TIM_GetCapture1(TIM3)+1);

}

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "IC.h"
uint8_t i;
int main(void){
	
	OLED_Init();
	PWM_Init();
	IC_Init();
	OLED_ShowString(1,1,"Freq:00000Hz");
	PWM_SetPrescaler(720-1); //Freq =72M/(PSC+1)/100
	PWM_SetCompare1(50); //Duty = CCR /100 
	while(1)
	{
		OLED_ShowNum(1,6,IC_GetFreq(),5);
	}
}

6-7PWMI模式测频率占空比

PWMI = PWM Input Mode(PWM输入模式),是STM32定时器的一种高级输入捕获模式,可以同时测量PWM信号的频率和占空比。
PWMI基本结构
image
关键函数
void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
参数说明
TIMx:定时器编号(如TIM3)
TIM_ICInitStruct:输入捕获配置结构体
步骤:
1.配置两个输入捕获通道(如TI1和TI2)
2.设置一个通道为上升沿触发,另一个为下降沿触发
3.启用定时器的从模式复位
4.配置触发源和滤波器
IC.c

#include "stm32f10x.h"                  // Device header

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

	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	
	TIM_InternalClockConfig(TIM3);
	//配置定时器基本参数
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period=65535-1; //ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler=72-1; //PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
	
	//初始化输入捕获单元
	TIM_ICInitTypeDef TIM_ICInitSturcutre;
	TIM_ICInitSturcutre.TIM_Channel=TIM_Channel_1;
	TIM_ICInitSturcutre.TIM_ICFilter=0xF;
	TIM_ICInitSturcutre.TIM_ICPolarity=TIM_ICPolarity_Rising;
	TIM_ICInitSturcutre.TIM_ICPrescaler=TIM_ICPSC_DIV1;
	TIM_ICInitSturcutre.TIM_ICSelection=TIM_ICSelection_DirectTI;
	TIM_ICInit(TIM3,&TIM_ICInitSturcutre);
	
	TIM_PWMIConfig(TIM3,&TIM_ICInitSturcutre);//你只需要传入一个通道的参数就行了,在这个函数里,会自动把剩下的一个通道初始化成相反的配置
	
	//配置TRGI的触发源为TI1FP1
	TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);
	//选择从模式为Reset
	TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);
	//启动定时器
	TIM_Cmd(TIM3,ENABLE);

}
//返回一个最新周期的频率值,单位Hz
uint32_t IC_GetFreq(void)
{
	// 1000000μs = 1秒
	return 1000000/ (TIM_GetCapture1(TIM3)+1);

}

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

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "IC.h"
uint8_t i;
int main(void){
	
	OLED_Init();
	PWM_Init();
	IC_Init();
	OLED_ShowString(1,1,"Freq:00000Hz");
	OLED_ShowString(2,1,"Duty:00%");
	
	PWM_SetPrescaler(720-1); //Freq =72M/(PSC+1)/100
	PWM_SetCompare1(80); //Duty = CCR /100 
	
	
	while(1)
	{
		OLED_ShowNum(1,6,IC_GetFreq(),5);
		OLED_ShowNum(2,6,IC_GetDuty(),2);
	}
}

OLED显示中文

image
image
oled_font.h

const char Font_Chinese[][32]=
{
{0x00,0xFE,0x22,0x5A,0x86,0x08,0x88,0x68,0x18,0x0F,0xE8,0x08,0x08,0x08,0x08,0x00},
{0x00,0xFF,0x04,0x08,0x07,0x20,0x11,0x0D,0x41,0x81,0x7F,0x01,0x05,0x09,0x30,0x00},/*"陈",0*/
{0x10,0x2C,0x24,0x24,0xA4,0xA4,0x65,0xA6,0x24,0x24,0x24,0x24,0xA4,0x34,0x0C,0x00},
{0x40,0x49,0x49,0x29,0x24,0x14,0x52,0x89,0x7F,0x04,0x0A,0x11,0x10,0x20,0x20,0x00},/*"家",1*/
{0x00,0x80,0x60,0xF8,0x07,0x08,0x48,0x48,0x48,0xFF,0x48,0x48,0x48,0x48,0x08,0x00},
{0x01,0x00,0x00,0xFF,0x00,0x02,0x02,0x02,0x02,0xFF,0x02,0x02,0x12,0x22,0x1E,0x00},/*"伟",2*/
};

oled.c

#include "stm32f10x.h"
#include "OLED_Font.h"

/*引脚配置*/
#define OLED_W_SCL(x)		GPIO_WriteBit(GPIOB, GPIO_Pin_8, (BitAction)(x))
#define OLED_W_SDA(x)		GPIO_WriteBit(GPIOB, GPIO_Pin_9, (BitAction)(x))

/*引脚初始化*/
void OLED_I2C_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	OLED_W_SCL(1);
	OLED_W_SDA(1);
}

/**
  * @brief  I2C开始
  * @param  无
  * @retval 无
  */
void OLED_I2C_Start(void)
{
	OLED_W_SDA(1);
	OLED_W_SCL(1);
	OLED_W_SDA(0);
	OLED_W_SCL(0);
}

/**
  * @brief  I2C停止
  * @param  无
  * @retval 无
  */
void OLED_I2C_Stop(void)
{
	OLED_W_SDA(0);
	OLED_W_SCL(1);
	OLED_W_SDA(1);
}

/**
  * @brief  I2C发送一个字节
  * @param  Byte 要发送的一个字节
  * @retval 无
  */
void OLED_I2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for (i = 0; i < 8; i++)
	{
		OLED_W_SDA(!!(Byte & (0x80 >> i)));
		OLED_W_SCL(1);
		OLED_W_SCL(0);
	}
	OLED_W_SCL(1);	//额外的一个时钟,不处理应答信号
	OLED_W_SCL(0);
}

/**
  * @brief  OLED写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void OLED_WriteCommand(uint8_t Command)
{
	OLED_I2C_Start();
	OLED_I2C_SendByte(0x78);		//从机地址
	OLED_I2C_SendByte(0x00);		//写命令
	OLED_I2C_SendByte(Command); 
	OLED_I2C_Stop();
}

/**
  * @brief  OLED写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void OLED_WriteData(uint8_t Data)
{
	OLED_I2C_Start();
	OLED_I2C_SendByte(0x78);		//从机地址
	OLED_I2C_SendByte(0x40);		//写数据
	OLED_I2C_SendByte(Data);
	OLED_I2C_Stop();
}

/**
  * @brief  OLED设置光标位置
  * @param  Y 以左上角为原点,向下方向的坐标,范围:0~7
  * @param  X 以左上角为原点,向右方向的坐标,范围:0~127
  * @retval 无
  */
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
	OLED_WriteCommand(0xB0 | Y);					//设置Y位置
	OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4));	//设置X位置高4位
	OLED_WriteCommand(0x00 | (X & 0x0F));			//设置X位置低4位
}

/**
  * @brief  OLED清屏
  * @param  无
  * @retval 无
  */
void OLED_Clear(void)
{  
	uint8_t i, j;
	for (j = 0; j < 8; j++)
	{
		OLED_SetCursor(j, 0);
		for(i = 0; i < 128; i++)
		{
			OLED_WriteData(0x00);
		}
	}
}

/**
  * @brief  OLED显示一个字符
  * @param  Line 行位置,范围:1~4
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的一个字符,范围:ASCII可见字符
  * @retval 无
  */
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{      	
	uint8_t i;
	OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);		//设置光标位置在上半部分
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i]);			//显示上半部分内容
	}
	OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);	//设置光标位置在下半部分
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]);		//显示下半部分内容
	}
}

/**
  * @brief  OLED显示字符串
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串,范围:ASCII可见字符
  * @retval 无
  */
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i++)
	{
		OLED_ShowChar(Line, Column + i, String[i]);
	}
}

/**
  * @brief  OLED次方函数
  * @retval 返回值等于X的Y次方
  */
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y--)
	{
		Result *= X;
	}
	return Result;
}

/**
  * @brief  OLED显示数字(十进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~4294967295
  * @param  Length 要显示数字的长度,范围:1~10
  * @retval 无
  */
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
	}
}

/**
  * @brief  OLED显示数字(十进制,带符号数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-2147483648~2147483647
  * @param  Length 要显示数字的长度,范围:1~10
  * @retval 无
  */
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
	uint8_t i;
	uint32_t Number1;
	if (Number >= 0)
	{
		OLED_ShowChar(Line, Column, '+');
		Number1 = Number;
	}
	else
	{
		OLED_ShowChar(Line, Column, '-');
		Number1 = -Number;
	}
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
	}
}

/**
  * @brief  OLED显示数字(十六进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFFFFFF
  * @param  Length 要显示数字的长度,范围:1~8
  * @retval 无
  */
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i, SingleNumber;
	for (i = 0; i < Length; i++)							
	{
		SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
		if (SingleNumber < 10)
		{
			OLED_ShowChar(Line, Column + i, SingleNumber + '0');
		}
		else
		{
			OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
		}
	}
}

/**
  * @brief  OLED显示数字(二进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
	}
}

void OLED_ShowChinese(uint8_t Line,uint8_t Column,uint8_t num)
{
	uint8_t i;
	OLED_SetCursor((Line-1)*2,(Column-1)*16);
	for(i=0;i<16;i++)
	{
		OLED_WriteData(Font_Chinese[2*num][i]);
	}
	OLED_SetCursor((Line-1)*2+1,(Column-1)*16);
	for(i=0;i<16;i++)
	{
		OLED_WriteData(Font_Chinese[2*num+1][i]);
	}
}

/**
  * @brief  OLED初始化
  * @param  无
  * @retval 无
  */
void OLED_Init(void)
{
	uint32_t i, j;
	
	for (i = 0; i < 1000; i++)			//上电延时
	{
		for (j = 0; j < 1000; j++);
	}
	
	OLED_I2C_Init();			//端口初始化
	
	OLED_WriteCommand(0xAE);	//关闭显示
	
	OLED_WriteCommand(0xD5);	//设置显示时钟分频比/振荡器频率
	OLED_WriteCommand(0x80);
	
	OLED_WriteCommand(0xA8);	//设置多路复用率
	OLED_WriteCommand(0x3F);
	
	OLED_WriteCommand(0xD3);	//设置显示偏移
	OLED_WriteCommand(0x00);
	
	OLED_WriteCommand(0x40);	//设置显示开始行
	
	OLED_WriteCommand(0xA1);	//设置左右方向,0xA1正常 0xA0左右反置
	
	OLED_WriteCommand(0xC8);	//设置上下方向,0xC8正常 0xC0上下反置

	OLED_WriteCommand(0xDA);	//设置COM引脚硬件配置
	OLED_WriteCommand(0x12);
	
	OLED_WriteCommand(0x81);	//设置对比度控制
	OLED_WriteCommand(0xCF);

	OLED_WriteCommand(0xD9);	//设置预充电周期
	OLED_WriteCommand(0xF1);

	OLED_WriteCommand(0xDB);	//设置VCOMH取消选择级别
	OLED_WriteCommand(0x30);

	OLED_WriteCommand(0xA4);	//设置整个显示打开/关闭

	OLED_WriteCommand(0xA6);	//设置正常/倒转显示

	OLED_WriteCommand(0x8D);	//设置充电泵
	OLED_WriteCommand(0x14);

	OLED_WriteCommand(0xAF);	//开启显示
		
	OLED_Clear();				//OLED清屏
}


oled.h

#ifndef __OLED_H
#define __OLED_H

void OLED_Init(void);
void OLED_Clear(void);
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char);
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String);
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length);
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowChinese(uint8_t Line,uint8_t Column,uint8_t num);
#endif


main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"

int main(void){
	
	OLED_Init();//初始化
//	OLED_ShowChar(1,1,'A');//第一行第一列输出字符A
//	OLED_ShowString(1,3,"CHENJIAWEI");//第一行第三列打印字符串
//	OLED_ShowSignedNum(3,1,-66,2);//第三行弟1列打印一个有符号的整数-66
//	OLED_ShowBinNum(2,1,0xAA55,16); //二进制显示1010101001010101
//	OLED_ShowHexNum(3,1,0xAA55,4);//十六进制形式显示AA55
	OLED_ShowChinese(1,1,0);
	OLED_ShowChinese(1,2,1);
	OLED_ShowChinese(1,3,2);
	while(1)
	{
	
	}
}

项目一 倒计时器

main.c

#include "stm32f10x.h"
#include "OLED.h"
#include "Key.h"
#include "timer.h"
#include "Delay.h"

uint8_t countdown_time = 0; // 单位:秒
uint8_t countdown_active = 0;

int main(void)
{
    SystemInit();
    
    OLED_Init();       // OLED初始化
    KEY_Init();        // 按键初始化
    Timer_Init();      // 定时器初始化

    OLED_Clear();
    OLED_ShowString(0, 0, "Set Time: 0s");
	
    static uint8_t last_time = 255;  // 记录上一次显示的秒数

    while (1)
    {
        // 设置时间,每次按下+60秒
        if (KEY1_Pressed())
        {
            if (!countdown_active && countdown_time < 300)
            {
                countdown_time += 20;
                OLED_Clear();
                OLED_ShowString(0, 0, "Set Time:");
                OLED_ShowNum(72, 0, countdown_time, 3, 16);
                OLED_ShowString(100, 0, "s");
            }
        }

        // 启动倒计时
        if (KEY2_Pressed())
        {
            if (countdown_time > 0 && !countdown_active)
            {
                countdown_active = 1;
                Timer_Start();
                last_time = 255;  // 触发强制刷新
            }
        }

        // 倒计时过程,每秒刷新一次屏幕
        if (countdown_active)
        {
            if (last_time != countdown_time)
            {
                last_time = countdown_time;

                OLED_Clear();
                OLED_ShowString(0, 2, "Counting:");
                OLED_ShowNum(72, 2, countdown_time, 3, 16);
                OLED_ShowString(100, 2, "s");

                if (countdown_time == 0)
                {
                    countdown_active = 0;
                    Timer_Stop();

                    OLED_Clear();
                    OLED_ShowString(16, 2, "*** DONE ***");
                    OLED_ShowString(32, 4, "Time's Up!");
										
								
                    Delay_ms(3000);

                    OLED_Clear();
                    OLED_ShowString(0, 0, "Set Time: 0s");
                }
            }
        }
    }
}

oled.c

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "i2c.h"
#include "OLED_Font.h"
#include "Delay.h"
#include <math.h>
void OLED_WR_Byte(uint8_t dat, uint8_t cmd)
{
    I2C_Start();
    I2C_SendByte(0x78); // OLED地址
    I2C_WaitAck();
    I2C_SendByte(cmd ? 0x40 : 0x00);
    I2C_WaitAck();
    I2C_SendByte(dat);
    I2C_WaitAck();
    I2C_Stop();
}

void OLED_Set_Pos(uint8_t x, uint8_t y)
{
    OLED_WR_Byte(0xb0 + y, 0);
    OLED_WR_Byte(((x & 0xf0) >> 4) | 0x10, 0);
    OLED_WR_Byte((x & 0x0f), 0);
}

void OLED_Clear(void)
{
    for (uint8_t i = 0; i < 8; i++)
    {
        OLED_WR_Byte(0xb0 + i, 0);
        OLED_WR_Byte(0x00, 0);
        OLED_WR_Byte(0x10, 0);
        for (uint8_t n = 0; n < 128; n++)
            OLED_WR_Byte(0x00, 1);
    }
}

void OLED_ShowChar(uint8_t x, uint8_t y, char chr)
{
    uint8_t c = chr - ' ';
    OLED_Set_Pos(x, y);
    for (uint8_t i = 0; i < 8; i++)
        OLED_WR_Byte(F8X16[c * 16 + i], 1);
    OLED_Set_Pos(x, y + 1);
    for (uint8_t i = 0; i < 8; i++)
        OLED_WR_Byte(F8X16[c * 16 + i + 8], 1);
}

void OLED_ShowString(uint8_t x, uint8_t y, char *str)
{
    while (*str)
    {
        OLED_ShowChar(x, y, *str);
        x += 8;
        if (x > 120) { x = 0; y += 2; }
        str++;
    }
}

void OLED_ShowNum(uint8_t x, uint8_t y, uint16_t num, uint8_t len, uint8_t size)
{
    for (uint8_t t = 0; t < len; t++)
    {
        uint8_t temp = (num / (uint16_t)pow(10, len - t - 1)) % 10;
        OLED_ShowChar(x + (size / 2) * t, y, temp + '0');
    }
}

void OLED_Init(void)
{
    Delay_ms(100);
    OLED_WR_Byte(0xAE, 0); // 关闭显示
    OLED_WR_Byte(0xA8, 0); OLED_WR_Byte(0x3F, 0); // 设置多重比率
    OLED_WR_Byte(0xD3, 0); OLED_WR_Byte(0x00, 0);
    OLED_WR_Byte(0x40, 0); OLED_WR_Byte(0xA1, 0);
    OLED_WR_Byte(0xC8, 0); OLED_WR_Byte(0xDA, 0); OLED_WR_Byte(0x12, 0);
    OLED_WR_Byte(0x81, 0); OLED_WR_Byte(0x7F, 0); OLED_WR_Byte(0xA4, 0);
    OLED_WR_Byte(0xA6, 0); OLED_WR_Byte(0xD5, 0); OLED_WR_Byte(0x80, 0);
    OLED_WR_Byte(0x8D, 0); OLED_WR_Byte(0x14, 0); OLED_WR_Byte(0xAF, 0); // 开启显示
    OLED_Clear();
}

oled.h

#ifndef __OLED_H
#define __OLED_H

#include "stm32f10x.h"

void OLED_Init(void);
void OLED_Clear(void);
void OLED_ShowString(uint8_t x, uint8_t y, char *str);
void OLED_ShowChar(uint8_t x, uint8_t y, char chr);
void OLED_ShowNum(uint8_t x, uint8_t y, uint16_t num, uint8_t len, uint8_t size);

#endif

key.c

#include "Key.h"
#include "Delay.h"

void KEY_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

uint8_t KEY1_Pressed(void)
{
    if (!GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1))
    {
        Delay_ms(20);
        if (!GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1))
        {
            while (!GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1));
            return 1;
        }
    }
    return 0;
}

uint8_t KEY2_Pressed(void)
{
    if (!GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11))
    {
        Delay_ms(20);
        if (!GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11))
        {
            while (!GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11));
            return 1;
        }
    }
    return 0;
}

key.h

#ifndef __KEY_H
#define __KEY_H

#include "stm32f10x.h"

void KEY_Init(void);
uint8_t KEY1_Pressed(void);
uint8_t KEY2_Pressed(void);

#endif


timer.c

#include "timer.h"

extern uint8_t countdown_time;

void Timer_Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    TIM_TimeBaseStructure.TIM_Period = 10000-1;
    TIM_TimeBaseStructure.TIM_Prescaler =  7200-1;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
}

void Timer_Start(void)
{
    TIM_Cmd(TIM2, ENABLE);
}

void Timer_Stop(void)
{
    TIM_Cmd(TIM2, DISABLE);
}

void TIM2_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
    {
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
        if (countdown_time > 0)
            countdown_time--;
    }
}

timer.h

#ifndef __TIMER_H
#define __TIMER_H

#include "stm32f10x.h"
extern uint8_t countdown_time;
void Timer_Init(void);
void Timer_Start(void);
void Timer_Stop(void);

#endif

i2c.c

#include "i2c.h"
#include "Delay.h"

#define I2C_SCL_PIN  GPIO_Pin_8
#define I2C_SDA_PIN  GPIO_Pin_9
#define I2C_PORT     GPIOB

void I2C_GPIO_Config(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = I2C_SCL_PIN | I2C_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(I2C_PORT, &GPIO_InitStructure);
}

static void SDA_OUT(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = I2C_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(I2C_PORT, &GPIO_InitStructure);
}

static void SDA_IN(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = I2C_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(I2C_PORT, &GPIO_InitStructure);
}

void I2C_Start(void)
{
    I2C_GPIO_Config();
    SDA_OUT();
    GPIO_SetBits(I2C_PORT, I2C_SCL_PIN | I2C_SDA_PIN);
    Delay_us(4);
    GPIO_ResetBits(I2C_PORT, I2C_SDA_PIN);
    Delay_us(4);
    GPIO_ResetBits(I2C_PORT, I2C_SCL_PIN);
}

void I2C_Stop(void)
{
    SDA_OUT();
    GPIO_ResetBits(I2C_PORT, I2C_SCL_PIN);
    GPIO_ResetBits(I2C_PORT, I2C_SDA_PIN);
    Delay_us(4);
    GPIO_SetBits(I2C_PORT, I2C_SCL_PIN);
    GPIO_SetBits(I2C_PORT, I2C_SDA_PIN);
    Delay_us(4);
}

uint8_t I2C_WaitAck(void)
{
    uint8_t ucErrTime = 0;
    SDA_IN();
    GPIO_SetBits(I2C_PORT, I2C_SDA_PIN);
    Delay_us(1);
    GPIO_SetBits(I2C_PORT, I2C_SCL_PIN);
    Delay_us(1);

    while (GPIO_ReadInputDataBit(I2C_PORT, I2C_SDA_PIN))
    {
        ucErrTime++;
        if (ucErrTime > 250)
        {
            I2C_Stop();
            return 1;
        }
    }

    GPIO_ResetBits(I2C_PORT, I2C_SCL_PIN);
    return 0;
}

void I2C_Ack(void)
{
    GPIO_ResetBits(I2C_PORT, I2C_SCL_PIN);
    SDA_OUT();
    GPIO_ResetBits(I2C_PORT, I2C_SDA_PIN);
    Delay_us(2);
    GPIO_SetBits(I2C_PORT, I2C_SCL_PIN);
    Delay_us(2);
    GPIO_ResetBits(I2C_PORT, I2C_SCL_PIN);
}

void I2C_NAck(void)
{
    GPIO_ResetBits(I2C_PORT, I2C_SCL_PIN);
    SDA_OUT();
    GPIO_SetBits(I2C_PORT, I2C_SDA_PIN);
    Delay_us(2);
    GPIO_SetBits(I2C_PORT, I2C_SCL_PIN);
    Delay_us(2);
    GPIO_ResetBits(I2C_PORT, I2C_SCL_PIN);
}

void I2C_SendByte(uint8_t byte)
{
    SDA_OUT();
    GPIO_ResetBits(I2C_PORT, I2C_SCL_PIN);
    for (uint8_t i = 0; i < 8; i++)
    {
        if (byte & 0x80)
            GPIO_SetBits(I2C_PORT, I2C_SDA_PIN);
        else
            GPIO_ResetBits(I2C_PORT, I2C_SDA_PIN);
        byte <<= 1;
       Delay_us(2);
        GPIO_SetBits(I2C_PORT, I2C_SCL_PIN);
       Delay_us(2);
        GPIO_ResetBits(I2C_PORT, I2C_SCL_PIN);
     Delay_us(2);
    }
}

uint8_t I2C_ReadByte(uint8_t ack)
{
    uint8_t receive = 0;
    SDA_IN();
    for (uint8_t i = 0; i < 8; i++)
    {
        GPIO_ResetBits(I2C_PORT, I2C_SCL_PIN);
        Delay_us(2);
        GPIO_SetBits(I2C_PORT, I2C_SCL_PIN);
        receive <<= 1;
        if (GPIO_ReadInputDataBit(I2C_PORT, I2C_SDA_PIN)) receive++;
        Delay_us(1);
    }

    if (!ack)
        I2C_NAck();
    else
        I2C_Ack();

    return receive;
}

i2c.h

#ifndef __I2C_H
#define __I2C_H

#include "stm32f10x.h"

void I2C_Start(void);
void I2C_Stop(void);
void I2C_SendByte(uint8_t byte);
uint8_t I2C_ReadByte(uint8_t ack);
uint8_t I2C_WaitAck(void);
void I2C_Ack(void);
void I2C_NAck(void);
void I2C_GPIO_Config(void);

#endif

串口
image
image
image
image

初始化
Ctrl+Alt+空格
image
image
image

9-1 串口发送HEX数据包

image

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include "Key.h"

uint8_t KeyNum;

	int main(void)
	{

		OLED_Init();		
		Key_Init();
		Serial_Init();
		
		OLED_ShowString(1,1,"TxPacket");
		OLED_ShowString(3,1,"RxPacket");
		
		Serial_TxPacket[0]=0x01;
		Serial_TxPacket[1]=0x02;
		Serial_TxPacket[2]=0x03;
		Serial_TxPacket[3]=0x04;
		
		
		while (1)
		{
			KeyNum = Key_GetNum();
			if(KeyNum ==1)
			{
				Serial_TxPacket[0] ++;
				Serial_TxPacket[1] ++;
				Serial_TxPacket[2] ++;
				Serial_TxPacket[3] ++;
				
				Serial_SendPacket();
				
				OLED_ShowHexNum(2,1,Serial_TxPacket[0],2);
				OLED_ShowHexNum(2,4,Serial_TxPacket[1],2);
				OLED_ShowHexNum(2,7,Serial_TxPacket[2],2);
				OLED_ShowHexNum(2,10,Serial_TxPacket[3],2);
			}
			if(Serial_GetRxFlag()==1)
			{	
				OLED_ShowHexNum(4,1,Serial_RxPacket[0],2);
				OLED_ShowHexNum(4,4,Serial_RxPacket[1],2);
				OLED_ShowHexNum(4,7,Serial_RxPacket[2],2);
				OLED_ShowHexNum(4,10,Serial_RxPacket[3],2);
			}
		}
	}

serial.c

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>

uint8_t Serial_TxPacket[4];
uint8_t Serial_RxPacket[4]; 
uint8_t Serial_RxFlag; //收到一个数据包就值1


/**
串口初始化
*/
void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);						//将PA1和PA2引脚初始化为推挽输出
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	USART_InitTypeDef USART_InitStruture;
	USART_InitStruture.USART_BaudRate= 9600;
	USART_InitStruture.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
	USART_InitStruture.USART_Mode=USART_Mode_Tx | USART_Mode_Rx;  // 同时使能发送和接收
	USART_InitStruture.USART_Parity=USART_Parity_No;
	USART_InitStruture.USART_StopBits=USART_StopBits_1;
	USART_InitStruture.USART_WordLength=USART_WordLength_8b;
	USART_Init(USART1,&USART_InitStruture);
	
	// 使能接收非空中断
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 配置NVIC中断分组
	// 配置USART1中断
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;// 抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;// 子优先级
	NVIC_Init(&NVIC_InitStructure);
	
	USART_Cmd(USART1,ENABLE); // 使能USART1
}

/**
	串口发送一个字节
*/
void Serial_SendByte(uint8_t Byte)
{
	USART_SendData(USART1,Byte);
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)== RESET);

}
 /**
	发送一个数组
*/
void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i=0;i< Length;i++)
	{
	Serial_SendByte(Array[i]);
	}
}
/**
	发送一个字符串
*/
void Serial_SendString(char *String)
{
	uint8_t i;
	for(i=0;String[i]!='\0';i++)
	{
	Serial_SendByte(String[i]);
	}
}

/**
	次方函数,返回值等于x的y次方
*/

uint32_t Serial_Pow(uint32_t X,uint32_t Y)
{
	uint32_t Result =1;
	while(Y --)
	{
	Result *=X;
		
	}
	return Result;
}

/**
	串口发送数字
*/
void Serial_SendNumber(uint32_t Number,uint8_t length)
{
	uint8_t i;
	for(i=0; i<length;i++)
	{
	Serial_SendByte(Number/Serial_Pow(10,length-i-1)%10+'0');	
	}
}
/**
	使用printf需要重定向的底层函数
*/

int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);
	return ch;

}
/**
	自己封装的printf函数
*/
void Serial_Printf(char *format, ...)
{
	char String[100];
	va_list arg;
	va_start(arg,format);
	vsprintf(String,format,arg);
	va_end(arg);
	Serial_SendString(String);
}

void Serial_SendPacket(void)
{
	Serial_SendByte(0xFF);
	Serial_SendArray(Serial_TxPacket,4);
	Serial_SendByte(0xFE);

	}

/**
	获取串口接收标注位,串口接收标志位,范围:0~1,接收到数据后,标志位置1,读取后标志位自动清零
*/

uint8_t Serial_GetRxFlag(void)
{
	if(Serial_RxFlag ==1)
	{
		Serial_RxFlag =0;
		return 1;
	}
	return 0;
}

/**
函    数:USART1中断函数
*/
void USART1_IRQHandler(void)
{
	static uint8_t RxState=0;
	static uint8_t pRxPacket=0;
	if (USART_GetFlagStatus(USART1,USART_IT_RXNE)== SET)
	{
		uint8_t RxData = USART_ReceiveData(USART1);
		
		if(RxState==0)
		{
			if(RxData==0xFF)// 检测到帧头0xFF
				{
					RxState=1; // 进入数据接收状态
					pRxPacket = 0;  // 重置数据包指针
				}
		}
		else if (RxState==1)
		{
			Serial_RxPacket[pRxPacket] = RxData; // 存储数据
			pRxPacket++;
			if(pRxPacket>=4) // 收满4个字节数据
			{ 
			RxState = 2; // 进入帧尾检查状态
			}
		}
		else if (RxState==2)
		{
		 if (RxData == 0xFE)
		 { 
			RxState = 0;// 回到初始状态 
			 Serial_RxFlag =1; // 设置接收完成标志
		 }
		}
		USART_ClearITPendingBit(USART1,USART_IT_RXNE);
	}
}



serial.h

#ifndef __SERIAL_H
#define __SERIAL_H


#include <stdio.h>

extern uint8_t Serial_TxPacket[];
extern uint8_t Serial_RxPacket[];
void Serial_SendByte(uint8_t Byte);
void Serial_Init(void);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_Printf(char *format,...);
void Serial_SendPacket(void);
uint8_t Serial_GetRxFlag(void);
void USART1_IRQHandler(void);

#endif

image

posted @ 2025-03-17 17:47  kkrystle  阅读(167)  评论(0)    收藏  举报