目录
认识led原理图
(图来源:CT117E-M4产品手册.pdf)
VDD多半是单极器件的正(理解为电源 可以输出高电平 高电平为3.3v)
R为电阻 (可以看到图中 电阻值为0.3k)
那个三角形带箭头的是期间led灯(发光二几管)
最右右边的数字就是GPIO的引脚了 这个可以直接用编程语言控制的
什么情况下led灯点亮? 高电平为3.3v 低电平为0v 有电压差超过0.7v就会有电流通过 有电流通过led就会被点亮
电阻的作用? I=V/R 如果没有电阻电流无限大 led会烧坏 所以最好控制在20mA以内 以上图为例3.3v/0.3k=11mA 如此电流流过led灯 就会被点亮
·什么是共阴/共阳极发光二极管? 以上图为例 所有的led灯都接在了电源一端 所以上图是共阳极发光二极管 GPIO的引脚输出低电平led点亮
思考?什么情况下是高电平点亮led灯
*得出结论*
PA8-PA15 低电平点亮led灯
分析GPIO口寄存器
硬件编程本质就是关于寄存器的编程
操作硬件转化为操作地址 地址用寄存器分布
cpu管脚有很多可以使用的功能 也可以多个复用 需要用到那个功能就开启
思考 cpu管脚为什么要这样设计?一个管脚就一个功能行吗?
想一想我们这次的目的! 把PA8-PA15当做输出功能用 输出低电平 点亮led灯
编程思路
先找相关的寄存器
打开STM32G4系列微控制器参考手册.pdf
361页 9.4 GPIO registers
*注意***table 57给出各个GPIO的寄存器的偏移地址** 加上GPIO的基地址就可以找到这个寄存器的地址了
我们先一个一个的看 判断当前的寄存器是否与本次编程有关
这个寄存器是有用的设置端口功能模式 我们应该找到PA8-PA15 将相应的位设置为01 (通用输出方式)
接下来设置输出模式下的类型
通用推挽输出 直接输出高电平或者低电平
选择输出速度 一般情况下 low speed的就可以
上拉和下拉一般给输入用 这里没有关系 设置为00 No pull-up pull-down
ODR就是那个关键的寄存器了
当8到15位的值为1 PA8-PA15就会输出高电平
当8到15位的值为0 PA8-PA15就会输出低电平
剩下的寄存器就与点灯无关了 也就说设置这几个寄存器就好了
理解时钟的作用
在STM32芯片的中 当我们要使用到外设设备时(GPIO就算一个)必须开启相应的时钟 这个设备才能正常工作
你可以这样理解时钟 时钟总线就是用电的总电闸,哪一个家用设备要用电 就只需对应的输出电就行了 ,对应的用电有电压 时钟也有相应的频率。这样理解是不是好多了
先看看芯片手册内部框图 —— STM32G431RB数据手册.pdf
可以看到GPIO挂在了APB2总线上
那么 我们使能APB2 外设时钟使能寄存器 让GPIO能够工作
如何配置相关寄存器 可以参考STM32G4系列微控制器参考手册.pdf RCC那一章
实操部分---利用hal库生成led点亮代码
初学者先跟着步骤做一遍 先练一个感觉!
1,新建项目文件
打开STM32CubeMX file--->newproject---找到STM32G431RBTx的芯片 双击选中
2,配置RCC时钟 ---clock configuration
初次调值 相关的总线统一设置80MHz 便于后面学习 80怎样得到的?计算过程 24/3=8 8*20/2=80
3,选择pc8~pc15的输出功能 另外开启PD2的输出功能(PD2为锁存器 作用是 控制pc8-pc15的改变)
4,配置生成文件 右上角点击生成代码
Application structure --basic--MDK-ARM v5--copy all used libraries ---generate peripherad initializa.....
5.进入keil5 调整配置
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_8
|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_SET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
/*Configure GPIO pins : PC13 PC14 PC15 PC8
PC9 PC10 PC11 PC12 */
GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_8
|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pin : PD2 */
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
}
这里初始化配置有点问题 :PD2并未置高电平 所以GPIO的引脚8-13没有配置成默认的高高电平
我们准备这样修改
在代码 HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);改成HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
最后一行 增加代码 HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
6.编写一个控制led的函数
PC8-PC15 正好为8位数可以构成一个char型 我们可以让函数接收一个char 根据位数点亮或熄灭led灯
void Led_Input_Disp(unsigned char input)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_8
|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
//思路先将pc8~12置为高电平熄灭 锁存器PD2 置高电平 pc引脚写入后PD2置为低电平
// * @param GPIO_Pin specifies the port bit to be written.
// * This parameter can be any combinatio n of GPIO_PIN_x where x can be (0..15).
HAL_GPIO_WritePin(GPIOC, input<<8, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
//输入数左移8位 低电平点亮 再次重复PD2操作
}
测试所写函数是否正确
while (1)
{
/* USER CODE END WHILE */
Led_Input_Disp(0x88);
HAL_Delay(500);
Led_Input_Disp(0x00);
HAL_Delay(500);
/* USER CODE BEGIN 3 */
}
led全亮、短暂延时后 led量全灭 达到持续闪灭的现象
最终形成的led.c文件参考
#include "led.h"
void MX_GPIO_led_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_8
|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_SET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
/*Configure GPIO pins : PC13 PC14 PC15 PC8
PC9 PC10 PC11 PC12 */
GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_8
|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pin : PD2 */
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}
void Led_Input_Disp(unsigned char input)
{
//思路先将pc8~12置为高电平熄灭 锁存器PD2 置高电平 pc引脚写入后PD2置为低电平
//输入数左移8位 低电平点亮 再次重复PD2操作
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_8
|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
// * @param GPIO_Pin specifies the port bit to be written.
// * This parameter can be any combinatio n of GPIO_PIN_x where x can be (0..15).
HAL_GPIO_WritePin(GPIOC, input<<8, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}
#ifndef __LED_H__
#define __LED_H__
#include "main.h"
void MX_GPIO_led_Init(void);
void Led_Input_Disp(unsigned char input);
#endif
为了实现的代码直观的可读性 单独新建一个rcc.c文件 存放 函数 SystemClock_Config 后面的外设如果修改了时钟我们进行全部的覆盖就可以了
#include "rcc.h"
void SystemClock_Config(void)
{
/*配置覆盖区*/
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
/* USER CODE END Error_Handler_Debug */
}
#ifndef __RCC_H__
#define __RCC_H__
#include "main.h"
void SystemClock_Config(void);
#endif
注意 函数 void Error_Handler(void) 默认在main.c定义过了 这可以让其他外设自动生成的配置中要用这个函数 不至于报错误