一、赛制提供的开发板原理图:
可以看出,原理图中有一个锁存器,只有引脚11(PD2)为高电平,2脚~9脚(1D~8D)的电平才能够赋值给12脚~19脚(1Q~8Q),否则,如果引脚11(PD2)为低电平,12脚~19脚(1Q~8Q)的电平会一直保存不表,可以理解为PD2锁住了LED1~8的电平。开发板电路这样设计一个锁存器在这里,是因为PC8~PC15有的引脚被LCD共用了。
所以,要想点亮LED,比如LED1,需要先将PD2赋值高电平开锁,再给PC8赋值低电平,再将PD2赋值低电平锁住,此时1Q引脚就为低电平了,LED1点亮。
二、配置STM32CubeMX
选择单片机型号,然后点击开始项目
配置时钟,在System·Core的RCC处选择外部高速时钟源
选择调试接口和选择时钟基准
最后去 Clock Configuration时钟配置界面配置系统时钟频率,在HCLK 时钟那里输入 MCU 允许的最高时钟频率。配置成80Mhz,输入后按回车点击确认OK
命名工程、生成路劲
设置工程
以上的CubeMX的基本设置完成了,接下来就是设置需要使用的外设了,我们回到设置外设;
根据原理图,要想使用8个LED,与要用到PC8~PC15这8个IO口,同时需要设置锁存器,所以要配置PD2。
如下依次配置输出模式
如此都配置完成了,最后右上角点击生成代码(GENERATE CODE)即可。
三、构建代码框架
打开代码编译运行,我们会发现有报错:
My_Project\My_Project.axf: Error: L6218E: Undefined symbol HAL_PWREx_DisableUCPDDeadBattery (referred from stm32g4xx_hal_msp.o).
Not enough information to list image symbols.
Not enough information to list load addresses in the image map.
Finished: 2 information, 0 warning and 1 error messages.
"My_Project\My_Project.axf" - 1 Error(s), 1 Warning(s).
这是因为用了最新的Cube生成旧版的1.20固件库
解决办法:将错误往上滑,找到
../Core/Src/stm32g4xx_hal_msp.c(76): warning: #223-D: function "HAL_PWREx_DisableUCPDDeadBattery" declared implicitly
然后双击会跳转,将76行的HAL_PWREx_DisableUCPDDeadBattery();改为HAL_PWREx_DisableUSBDeadBatteryPD();
修改后就不会了,如果固件库用的最新的就不会有这个问题。
然后我们打开我们的工程路径文件,新建一个我们自己的应用文件,这里我命名了APP
然后回到我们的工程将刚刚建立的文件添加到我们的工程中,右键My_Projectt添加新组
缓慢双击修改名称
添加文件路径到工程中,点击魔法棒按照下面步骤:
然后顺便设置烧录的驱动,驱动如果无法识别先解决这个,不然烧入不了
最后点击OK设置完成
接下来开始代码的编写,我的习惯是喜欢建立一个单独的模块驱动.c.h文件,如果是比赛时间怕不够的可以只建立一个。c文件就好了,全部塞那里去。
点击左上脚的新建文件
然后分别Ctrl+S保存命名,注意保存路径弄到我们刚才建立的那个文件里头
将.c文件添加到新组中,点击
在app.h下添加以下代码
#ifndef __APP_H
#define __APP_H
#endif /* __APP_H */
然后我们跳到main.c和app,c里,在注释有Begin和END之间添加app.h的头文件包含(主注意写在注释有Begin和END之间的那行,不然下次cubeMX重新生成会被清除)如下
将里面的头文件包含都复制到app.h里
编译无错误无警告后继续。代码框架的编写
在app.c里编写如下代码:
uint8_t dis=0; /*利用这个标志,上电之后运行完对应的初始化后*/
/*-------------------------------------------------------------------------------------
* 函数名称:Main_App
* 函数功能:放在主函数的while(1)循环里,这样以后生成就不用动其他文件了。直接在这里操作
---------------------------------------------------------------------------------------*/
void Main_App(void) /*主函数while(1)*/
{
if(dis) /*循环的业务代码放这里*/
{
}
else /*只进来一次,放外设的初始化*/
{
dis=1;
}
}
因为生成的代码很多那些注释,看的会很乱,所以就用了这种方式,尽量不动生成的代码
记得在app,h里声明函数,要放在主函数里的,如下位置
四、编写主要程序
编译无错误无警告之后,我们的代码框架就完成了,接下来开始点灯,直接附上整个app.c的代码:
#include "app.h"
uint8_t dis=0; /*利用这个标志,上电之后运行完对应的初始化后*/
/*-------------------------------------------------------------------------------------
* 函数名称:Main_App
* 函数功能:放在主函数的while(1)循环里,这样以后生成就不用动其他文件了。直接在这里操作
---------------------------------------------------------------------------------------*/
void Main_App(void) /*主函数while(1)*/
{
uint8_t i;
if(dis) /*循环的业务代码放这里*/
{
for(i=0;i<8;i++) /*依次点亮*/
{
LedCtrl(i,1);
HAL_Delay(100);
}
for(i=0;i<8;i++) /*依次熄灭*/
{
LedCtrl(i,0);
HAL_Delay(100);
}
}
else /*只进来一次,放外设的初始化*/
{
Led_PIN_SET(); /*熄灭所有LED*/
dis=1;
}
}
uint16_t GPIOC_ODR=0xFF00; //两个FF对应的高位PC8~15
/*----------------------------------------------------------------------------------------------------
*函数名称:Led_PIN_SET()
*函数功能:流水灯全部置高电平
*输 入:无
*返 回 值:无
-----------------------------------------------------------------------------------------------------*/
void Led_PIN_SET(void)
{
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,0xff<<8,GPIO_PIN_SET);
GPIOC_ODR=GPIOC->ODR; //记录本次流水灯的点亮状态
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
/*----------------------------------------------------------------------------------------------------
*函数名称:LedCtrl(uint8_t ds,uint8_t LED_State)
*函数功能:点亮和熄灭指定的LED灯
*输 入:ds:1~8,LED的位置,LED_State:0/1;0:指定的灯熄灭,1:指定的灯点亮
*返 回 值:无
*修 改 人:ZXP 2023/12/28 10:41
-----------------------------------------------------------------------------------------------------*/
void LedCtrl(uint8_t ds,uint8_t LED_State)
{
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);//开启锁存器的数据直通
if(LED_State)
{
GPIOC->ODR |=0xFF00; //熄灭所有led
GPIOC->ODR &= ~(1<<(ds+7) | ~GPIOC_ODR); //保持上一个灯的状态,加7是为了移到高8位
GPIOC_ODR=GPIOC->ODR; //记录本次流水灯的点亮状态
}
else if(!LED_State)
{
GPIOC->ODR |= (1<<(ds+7) | GPIOC_ODR); //熄灭指定的灯,或运算,1,加7是为了移到高8位
GPIOC_ODR=GPIOC->ODR; //记录本次流水灯的点亮状态
}
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);//锁存
}
app,h
#ifndef __APP_H
#define __APP_H
#include "main.h"
#include "gpio.h"
void Main_App(void);
void Led_PIN_SET(void);
void LedCtrl(uint8_t ds,uint8_t LED_State);
#endif /* __APP_H */
效果,流水灯逐个点亮然后逐个熄灭