在获取到工程模板后,学习某个CPU的第一步通常都是IO口操作。因此按钮输入和点灯,就是本次学习的第一个程序。先从简单入手。
和GPIO操作有关的函数如下:
__HAL_RCC_GPIOA_CLK_ENABLE();//使能GPIOA时钟
__HAL_RCC_GPIOB_CLK_ENABLE();//使能GPIOB时钟
__HAL_RCC_GPIOC_CLK_ENABLE();//使能GPIOC时钟
__HAL_RCC_GPIOD_CLK_ENABLE();//使能GPIOD时钟
__HAL_RCC_GPIOF_CLK_ENABLE();//使能GPIOF时钟
HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)
根据GPIO_InitTypeDef型结构变量指定的参数初始化GPIOx的外设寄存器
HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
PinState=0,GPIOx端口的GPIO_Pin引脚输出低电平;
PinState=1,GPIOx端口的GPIO_Pin引脚输出高电平
HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
GPIOx端口的GPIO_Pin的输出电平翻转
HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
读取"GPIOx端口的第GPIO_Pin引脚"输入的电平值
HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin)
将"GPIOx端口的GPIO_Pin引脚"的外设寄存器恢复到"复位时的默认值"
HAL_GPIO_LockPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
锁定GPIO引脚GPIO_Pin的配置;当GPIO_Pin执行了"锁键的写入时序"后,在下次系统复位前将不能再更改端口位的配置;
1、LED灯初始化
#include "LED.h"
void LED_Init(void);
//函数功能:配置PC13为输出,无上拉或下拉,输出速度为5MHz
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOC_CLK_ENABLE(); //GPIOC时钟使能
GPIO_InitStruct.Pin = GPIO_PIN_13; //选择引脚号码
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出模式
GPIO_InitStruct.Pull = GPIO_NOPULL; //引脚上拉和下拉都没有被激活
// GPIO_InitStruct.Pull = GPIO_PULLUP; //设置上拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; //引脚的输出速度为5MHz
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
//根据GPIO_InitStruct结构变量指定的参数初始化GPIOC的外设寄存器
LED1_Off();
}
#ifndef __LED_H__
#define __LED_H__
#include "stm32g4xx_hal.h"
#define LED1_On() HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET)
//输出低电平开灯
#define LED1_Off() HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET)
//输出高电平关灯
#define LED1_Toggle() HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13)
//LED1引脚输出电平翻转
extern void LED_Init(void);
#endif /*__ GPIO_H__ */
2、Key初始化程序
#include "Key.h"
void Key_Init(void);
//函数功能:将PA12引脚配置为输入引脚
void Key_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOB_CLK_ENABLE(); //GPIOB时钟使能
GPIO_InitStruct.Pin = GPIO_PIN_15; //选择第15脚
GPIO_InitStruct.Pull = GPIO_PULLUP; //引脚上拉被激活
// GPIO_InitStruct.Pull = GPIO_NOPULL; //引脚上拉和下拉都没有被激活
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM; //配置GPIO速度为中速
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; //设置引脚工作模式为输入模式
HAL_GPIO_Init(GPIOB,&GPIO_InitStruct);
//根据GPIO_InitStruct结构变量指定的参数初始化GPIOB的外设寄存器
}
#ifndef __Key_H__
#define __Key_H__
#include "stm32g4xx_hal.h"
#define Key_LevelValue() HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_15) //读取Key1的电平值
extern void Key_Init(void);
#endif /* __Key_H */
3、delay.c程序
#include "delay.h"static uint8_t fac_us=0; //us延时倍乘数
static uint16_t fac_ms=0; //ms延时倍乘数,在ucos下,代表每个节拍的ms数void My_delay_us(__IO uint32_t nCount);//systick中断服务函数,1ms中断一次
//在调用HAL_Init()时,SysTick定时器初始化为使能中断
void SysTick_Handler(void)
{
}//函数功能:delay函数初始化
//在调用HAL_Init()时,已经对SysTick定时器初始化了,因此这里无需初始化
void delay_init(void)
{uint32_t reload;fac_us=SystemCoreClock/1000000;//相当于将170MHz经过1000000分频用作SysTick的输入时钟,即周期为1us reload=SystemCoreClock/1000000;//相当于将170MHz经过1000000分频用作SysTick的输入时钟,即周期为1us reload*=1000;//SysTick溢出时间为1000*1us=1ms//reload为24位寄存器,最大值:16777216,在170MHz下,约合0.098s左右 fac_ms=1;
}//函数功能:延时nus微妙
//nus:要延时的us数.
//nus:0~204522252(最大值即2^32/fac_us@fac_us=168)
void delay_us(uint32_t nus)
{ uint32_t ticks;uint32_t told,tnow,tcnt=0;uint32_t reload=SysTick->LOAD;//读取SysTick的LOAD寄存器的值ticks=nus*fac_us; //计算需要的节拍数 told=SysTick->VAL;//刚进入时的计数器值while(1){tnow=SysTick->VAL;//读SYSTICK计数器值 if(tnow!=told){ if(tnow<told)tcnt+=told-tnow; //SysTick定时器是一个递减的计数器.else tcnt+=reload-tnow+told; told=tnow;if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出.} }
}//函数功能:延时nms毫秒
//nms:要延时的ms数
//nms:0~65535
void delay_ms(uint32_t nms)
{ delay_us((uint32_t)(nms*1000));//普通方式延时
}//延时nms,不会引起任务调度
//nms:要延时的ms数
void delay_xms(uint32_t nms)
{uint32_t i;for(i=0;i<nms;i++) delay_us(1000);
}
4、Clock_Config.c程序
#include "Clock_Config.h"
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()void SystemClock_Config(void);
void Print_HCLK_PCLK1_PCLK2(void);void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1_BOOST);//配置主内部调节器输出电压//修改PWR->CR1寄存器bit10:9(VOS[1:0]),VOS[1:0]=01b,电压缩放范围选择"Range 1"//Configure the main internal regulator output voltageRCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;//记录修改对象,告诉后面的函数将要对“HSE时钟”进行配置RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV2;//RCC_PLLCFGR寄存器bit7:4(PLLM[3:0]),PLLM[3:0]=0001b,PLLM的值为2RCC_OscInitStruct.PLL.PLLN = 85;//RCC_PLLCFGR寄存器bit14:8(PLLN[3:0]),PLLN[6:0]=0x55,PLLN为的值为85RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;//RCC_PLLCFGR寄存器bit17(PLLP),PLLP=1,PLLP的分频值为17RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;//RCC_PLLCFGR寄存器bit22:21(PLLQ[1:0]),PLLQ[1:0]=01,PLLQ的分频值为2RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;//RCC_PLLCFGR寄存器bit26:25(PLLR[1:0]),PLLR[1:0]=01,PLLR的分频值为2
/*
f(VCO clock) = (HSE_VALUE / PLLM) * PLLN
f(PLL_P) = f(VCO clock) / PLLP
f(PLL_Q) = f(VCO clock) / PLLQ
f(PLL_R) = f(VCO clock) / PLLR
HSE_VALUE = 8000000,外部晶振为8MHz
PLL_VCO = (HSE_VALUE / PLLM) * PLLN = (8000000/2)*85=340000000Hz=340MHz
SYSCLK = PLL_VCO / PLLR = 340000000 / 2 = 170000000Hz = 170MHz
PLL “P” clock = PLL_VCO / 2 = 340000000/2 =170000000Hz = 170MHz
PLL “Q” output clock frequency = VCO frequency / PLLQ = 340000000 / 2 = 170000000Hz = 170MHz
*/if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){//配置“HSE时钟”和“PLL时钟”Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;//记录修改对象,告诉后面的函数将要修改HCLK,SYSCLK,PCLK1,PCLK2等时钟RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;//记录修改对象,告诉后面的函数将PLLCLK用作系统时钟RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;//AHB时钟(HCLK)的分频值RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;//配置RCC_CFGR寄存器bit10:8(PPRE1[2:0]),APB1时钟(PCLK1)的分频值RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;//配置RCC_CFGR寄存器bit13:11(PPRE2[2:0]),APB2时钟(PCLK2)的分频值//APB2外设:HRTIM,TIM1,TIM8,TIM15,TIM16,TIM17,TIM20,SPI1,SPI4,USART1,SAI1,SYSCFG/COMP/OPAMP/VREFBUFif (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK){//配置HCLK,SYSCLK,PCLK1,PCLK2时钟//HCLK为170MHz,PCLK1时钟为170MHz,PCLK2时钟为170MHzError_Handler();}SystemCoreClockUpdate();//更新SystemCoreClock的值
}
5、main.c程序
#include "main.h"
//#include "cmsis_os.h"
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "sys.h"
#include "Clock_Config.h"
#include "Key.h"
#include "LED.h"
#include "delay.h"//STM32G474输入输出测试程序
int main(void)
{STACK_Init();HAL_Init();//复位所有的外设//初始化FLASH接口//将SysTick定时器配置1ms中断SystemClock_Config();//Configure the system clockdelay_init();delay_ms(1000);Key_Init();LED_Init();//配置PC13为输出,无上拉或下拉,输出速度为5MHzwhile (1){if( Key_LevelValue()==0 )//如果按钮一直被按下,则灯会闪烁{LED1_Toggle(); //LED1引脚输出电平翻转delay_ms(500);}}
}//函数功能:在发生错误时,将执行此函数。
void Error_Handler(void)
{__disable_irq();while (1){printf("Error\r\n");}
}
6、测试结果
当一直按下PB15时,绿色LED灯会不停地闪烁。
STM32G474最小系统板,BOOT0需要10K/15K电阻到GND,保证程序从FLASH启动。商家的开发问题还是有的。