在获取到工程模板后,学习某个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 voltage

  RCC_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的值为2
  RCC_OscInitStruct.PLL.PLLN = 85;
	//RCC_PLLCFGR寄存器bit14:8(PLLN[3:0]),PLLN[6:0]=0x55,PLLN为的值为85
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
	//RCC_PLLCFGR寄存器bit17(PLLP),PLLP=1,PLLP的分频值为17
  RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
	//RCC_PLLCFGR寄存器bit22:21(PLLQ[1:0]),PLLQ[1:0]=01,PLLQ的分频值为2
  RCC_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/VREFBUF
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
  {//配置HCLK,SYSCLK,PCLK1,PCLK2时钟
		//HCLK为170MHz,PCLK1时钟为170MHz,PCLK2时钟为170MHz
    Error_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 clock

	delay_init();
	delay_ms(1000);

  Key_Init();
  LED_Init();//配置PC13为输出,无上拉或下拉,输出速度为5MHz

  while (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启动。商家的开发问题还是有的。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部