STM32F103C8T6-流水灯、PWM 与呼吸灯

STM32F103C8T6-流水灯、呼吸灯

一、流水灯

1. 搭建电路

注意面包板的导通情况:
在这里插入图片描述
这里我们将开发板插在面包板上,3.3V 和 GND 分别通过电阻接面包板的正极、负极;LED的正极接面包板正极,负极接 GPIO 口。

2. 代码编写

因为这里使用的引脚属于不同的端口,无法使用二进制位移操作,因此我们使用两个数组标记引脚。

  /* USER CODE BEGIN 2 */
	uint16_t pinsA[4] = {LED_1_Pin, LED_2_Pin, LED_3_Pin, LED_4_Pin};
	uint16_t pinsB[2] = {LED_5_Pin, LED_6_Pin};
  /* USER CODE END 2 */
    /* USER CODE BEGIN WHILE */
  while (1)
  {
		for (int i = 0; i < sizeof(pinsA) / sizeof(pinsA[0]); i ++){
			HAL_GPIO_WritePin(GPIOA, LED_1_Pin | LED_2_Pin | LED_3_Pin | LED_4_Pin, GPIO_PIN_SET); 
			HAL_GPIO_WritePin(GPIOA, pinsA[i], GPIO_PIN_RESET);
			HAL_Delay(1000);
		}	
		
		HAL_GPIO_WritePin(GPIOA, LED_4_Pin, GPIO_PIN_SET);
		for (int i = 0; i < sizeof(pinsB) / sizeof(pinsB[0]); i ++){
			HAL_GPIO_WritePin(GPIOB, LED_5_Pin | LED_6_Pin, GPIO_PIN_SET);
			HAL_GPIO_WritePin(GPIOB, pinsB[i], GPIO_PIN_RESET);
			HAL_Delay(1000);
		}
		
		HAL_GPIO_WritePin(GPIOB, LED_6_Pin, GPIO_PIN_SET);
    /* USER CODE END WHILE */

此外还要注意的是HAL_GPIO_WritePin(GPIOA, LED_1_Pin | LED_2_Pin | LED_3_Pin | LED_4_Pin, GPIO_PIN_SET)这种写法,用于同时操作多个 GPIO 引脚;因为每个引脚通常用一个二进制位表示,每个宏(如 LED_1_Pin)只在它对应的那一位上为1,其余位为0。这个整体数值称为位掩码(bitmask)。

3. 交替循环

上文实现的是单灯循环,进一步实现交替循环,即 右 -> 左 -> 右…

 /* USER CODE BEGIN WHILE */
  while (1)
  {
		for (int i = 0; i < sizeof(pinsA) / sizeof(pinsA[0]); i ++){
			HAL_GPIO_WritePin(GPIOA, LED_1_Pin | LED_2_Pin | LED_3_Pin | LED_4_Pin, GPIO_PIN_SET); 
			HAL_GPIO_WritePin(GPIOA, pinsA[i], GPIO_PIN_RESET);
			HAL_Delay(1000);
		}	
		
		HAL_GPIO_WritePin(GPIOA, LED_4_Pin, GPIO_PIN_SET);
		for (int i = 0; i < sizeof(pinsB) / sizeof(pinsB[0]); i ++){
			HAL_GPIO_WritePin(GPIOB, LED_5_Pin | LED_6_Pin, GPIO_PIN_SET);
			HAL_GPIO_WritePin(GPIOB, pinsB[i], GPIO_PIN_RESET);
			HAL_Delay(1000);
		}
		
		HAL_GPIO_WritePin(GPIOB, LED_6_Pin, GPIO_PIN_SET);
		
		HAL_GPIO_WritePin(GPIOB, LED_5_Pin, GPIO_PIN_RESET);
		
		HAL_Delay(1000);
		
		HAL_GPIO_WritePin(GPIOB, LED_5_Pin, GPIO_PIN_SET);
		
		for (int i = sizeof(pinsA) / sizeof(pinsA[0]) - 1; i >= 1; i --){
			HAL_GPIO_WritePin(GPIOA, LED_1_Pin | LED_2_Pin | LED_3_Pin | LED_4_Pin, GPIO_PIN_SET); 
			HAL_GPIO_WritePin(GPIOA, pinsA[i], GPIO_PIN_RESET);
			HAL_Delay(1000);
		}
		
		HAL_GPIO_WritePin(GPIOB, LED_1_Pin, GPIO_PIN_SET);
    /* USER CODE END WHILE */
  • 此处出现过的 bug 有 LED_6 亮的时间超出,简单修改。

4. 分组亮灯

通过结构体,统一管理 LED 的相关信息:

/* USER CODE BEGIN PD */
# define LED_NUM sizeof(LEDs) / sizeof(LEDTypedef)
/* USER CODE END PD */
/* USER CODE BEGIN PM */
typedef struct {
	GPIO_TypeDef *port;
	uint16_t pin;
} LEDTypedef;
/* USER CODE END PM */
/* USER CODE BEGIN PV */
LEDTypedef LEDs[] = {
	{GPIOA, LED_1_Pin},
	{GPIOA, LED_2_Pin},
	{GPIOA, LED_3_Pin},
	{GPIOA, LED_4_Pin},
	{GPIOB, LED_5_Pin},
	{GPIOB, LED_6_Pin}
};	
/* USER CODE END PV */

编写分组亮灯的函数:

/* USER CODE BEGIN 0 */
void LED_GROUP_ON(const uint16_t *groupIndex, uint16_t groupSize){
	
	for (int i = 0; i < groupSize; i ++){
		uint8_t index = groupIndex[i];
		HAL_GPIO_WritePin(LEDs[index].port, LEDs[index].pin, GPIO_PIN_RESET);
	}	
}	

void LED_GROUP_OFF(const uint16_t *groupIndex, uint16_t groupSize){
	
	for (int i = 0; i < groupSize; i ++){
		uint8_t index = groupIndex[i];
		HAL_GPIO_WritePin(LEDs[index].port, LEDs[index].pin, GPIO_PIN_SET);
	}	
}	
/* USER CODE END 0 */

分组:

  /* USER CODE BEGIN 2 */
	uint16_t group1[3] = {0, 2, 4}, group2[3] = {1, 3, 5};
  /* USER CODE END 2 */
    /* USER CODE BEGIN WHILE */
  while (1)
  {
		LED_GROUP_ON(group1, 3);
		HAL_Delay(1000);
		LED_GROUP_OFF(group1, 3);
		LED_GROUP_ON(group2, 3);
		HAL_Delay(1000);
		LED_GROUP_OFF(group2, 3);
    /* USER CODE END WHILE */

二、PWM 与呼吸灯

1. PWM 的定义

绝大多数的单片机上,处理模拟信号的资源很少。STM32F103C8T6 上只有模数转换功能(ADC),用于读取模拟信号的电压值;脉冲宽度调制(PWM, Pulse-Width Modulation)能够通过改变占空比,用数字信号尽可能地模拟模拟信号效果。只要动态地改变占空比,宏观上看就能产生 [0, 3.3V] 的电压。

2. 定时器:输出比较模式之 PWM 模式

通过调节比较寄存器值的大小,控制 PWM 的占空比

3. CubeMX 配置

  • 设置串口 debug
    在这里插入图片描述
  • 设置外部时钟晶振
    在这里插入图片描述
  • 设置时钟树
    在这里插入图片描述
  • TIM 设置
  • 在这里插入图片描述
  1. 选择 MCU 的内部时钟系统来驱动定时器。

  2. 启用TIM3的 Channel1 并配置为 PWM输出模式。Channel1 被分配到 PA6 引脚。
    Prescaler(预分频器,PSC):该参数定义了定时器的预分频系数。定时器的时钟频率通过预分频器从 MCU 的系统时钟分频得到。PSC = N 表示时钟每经过(N + 1)个周期才让定时器计数器递增 1。
    Counter Period(计数周期,ARR):该参数定义了定时器的周期,即计数器从 0 计数到的最大值。定时器每次从 0 计数到 ARR(自动重载寄存器的值)后会重置为 0,重新开始计数。此处计数器从 0 计数到 99。

  3. 在这里插入图片描述

  4. Pulse: 设置 Channel1 的初始脉冲宽度,即占空比为 50。Pulse 值 是输出比较寄存器中的值。当计数器的值小于 Pulse 时,PWM 输出为高电平。
    在这里插入图片描述

  • 注:要查看函数定义,必须先编译。光标放在函数上,右键。
  • 此处的输出为 GPIO 的复用推挽输出模式。因为外设需要进行推挽输出?

3. 代码

  /* USER CODE BEGIN 2 */
	HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
  /* USER CODE END 2 */
    /* USER CODE BEGIN WHILE */
  while (1)
  {
		for (int i = 0; i < 100; i ++){
			__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, i);
			HAL_Delay(10);
		}	
		
		for (int i = 99; i >= 0; i --){
			__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, i);
			HAL_Delay(10);
		}	
    /* USER CODE END WHILE */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值