一、介绍
蓝桥杯嵌入式使用的单片机是STM32G431RBT6,内核ARM Cortex - M4,MCU+FPU,170MHz/213DMIPS,高达128KB Flash,32KB SRAM,其余的外设就不多介绍了,参照数据芯片数据手册
CT117E-M4开发板资源:微控制器STM32G431RBT6、一路USB转串口、2.4寸TFT-LCD、4个功能按键、1个复位按键、8个LED、一个E2PROM(AT24C02)、一个可编程电阻(100K)、2路信号发生器、2个分压电位器、2个扩展接口、一个CMSIS DAP Link调试器
二、初始化Cubemx
1.新建工程
2.下载程序引脚配置
3.启用外部晶振,时钟配置
4.生成工程
如果工程路径不是纯英文则会缺少启动文件
三、下载程序配置
1.LED
1.1 原理图
1.2 引脚配置
其中PD2高电平使能锁存器,PC8-15默认给高电平,放置上电初始化LED亮
1.3 添加库文件夹路径,并新建LED库文件
1.4 相关代码
LED.c
cs
#include "led.h"
void LED_Disp(uchar dsLED)
{
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET); // 所有PC引脚置高
HAL_GPIO_WritePin(GPIOC,dsLED<<8,GPIO_PIN_RESET); // 将传入的dsLED位数置低,为了使对应的LED亮
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET); // 锁存器置高使能,记住PC的引脚电平
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET); // 锁存器置地,锁住PC的引脚电平
}
LED.h
cs
#ifndef _LED_H_
#define _LED_H_
#include "main.h"
void LED_Disp(uchar dsLED);
#endif
1.5 头文件添加
1.6 工程调试
2.LCD
2.1 原理图
2.2 将比赛提供的液晶显示工程复制到自己工程文件中
2.3 引脚配置
2.4 工程配置
2.5 工程调试
3.按键
3.1 原理图
3.2 引脚配置
3.3 定时器配置
这里按键选择按下触发定时器进入中断 所以需要配置定时器
使用定时器4(通用定时器,使用外部晶振),分频系数为80(从0开始则为80-1),则每1s
1M次,定时评率为为10000,对应1s 1M/10000次,频率为10ms每次,一定记得开启NVIC使能中断
3.4 相关代码
interrupt.c
cs
#include "interrupt.h"
#include "stdio.h"
struct keys key[4] = {0,0,0,0,0};
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//中断回调函数
{
if(htim->Instance == TIM4)//判断是否来自于定时器4
{
key[0].key_status = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
key[1].key_status = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
key[2].key_status = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
key[3].key_status = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
for(int i=0;i<4;i++)
{
switch(key[i].judge_status)
{
case 0:
if(key[i].key_status == 0)
{
key[i].judge_status = 1;
key[i].key_time = 0;
}
break;
case 1:
if(key[i].key_status == 0)
{
key[i].judge_status = 2;
}
else
{
key[i].judge_status = 0;
}
break;
case 2:
if(key[i].key_status == 1)
{
key[i].judge_status = 0;
if(key[i].key_time < 70)
{
key[i].key_flag = 1;
key[i].key_longflag = 0;
}
else
{
key[i].key_time++;
if(key[i].key_time > 70)
{
key[i].key_longflag = 1;
key[i].key_flag = 0;
}
}
}
break;
}
}
}
}
interrupt.h
cs
#ifndef __INTERRUPT_H_
#define __INTERRUPT_H_
#include "stdbool.h"
#include "main.h"
struct keys
{
uint8_t judge_status;
bool key_flag;
bool key_status;
bool key_longflag;
uint8_t key_time;
};
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
#endif
3.5 工程调试
在interrupt.h定义的结构体须在main.c中用extern再次声明
4.pwm
4.1 pwm输出引脚配置
配置PA6、PA7为输出引脚
频率fre=80 000 000 / (PSC+1)/ (ARR+1)
占空比duty=CRR / ARR
4.2 输入捕获引脚配置
原理图
根据题目需求设置捕获引脚即可 第一个一般是模拟输出频率 第二个则是模拟输出电压 R37~R40分别对应硬件板上的四个旋钮
相关配置
选用PB4, PA15为定时器通道CH1
配置预分频,重装载值(设为最大,防止溢出),启用中断
4.3 相关代码
interrupt.c
cs
#include "interrupt.h"
unsigned int Cnta1 = 0,Cnta2 = 0;
unsigned int fre1 = 0,fre2 = 0;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2)//获取频率
{
Cnta1 = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);//得到计时值
__HAL_TIM_SetCounter(htim,0);//计时值清0
fre1 = (80000000/80)/Cnta1;//获得频率
HAL_TIM_IC_Start(htim,TIM_CHANNEL_1);//打开定时器
}
if(htim->Instance == TIM3)
{
Cnta2 = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);
__HAL_TIM_SetCounter(htim,0);
fre2 = (80000000/80)/Cnta2;
HAL_TIM_IC_Start(htim,TIM_CHANNEL_2);
}
}
interrupt.h
cs
#ifndef __INTERRUPT_H_
#define __INTERRUPT_H_
#include "main.h"
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);
#endif
4.4 工程调试
通过选择R39、R40即可调动频率值
5.ADC
5.1 原理图
5.2 引脚配置
5.3 相关代码
adc1.c
cs
#include "adc1.h"
double getADC(ADC_HandleTypeDef *pin)
{
unsigned int adc;
HAL_ADC_Start(pin);
adc = HAL_ADC_GetValue(pin);
return adc*3.3/4096;
}
adc1.h
cs
#ifndef __ADC1_H_
#define __ADC1_H_
#include "main.h"
double getADC(ADC_HandleTypeDef *pin);
#endif
5.4 工程调试
通过旋转R37~R38即可调动模拟输出电压
6.I2C
6.1 原理图及芯片资料
芯片资料中前面的nK就是芯片大小 我们只需要看第一行即可 第一行中前面四位固定是1010 后面的A0、A1、A2分别对应原理图中的E1、E2、E3 原理图中E1、E2、E3分别接地 所以在设置地址时设置0即可 最后的R/W位即用来配置是读或者写 0则为写入 1为读取 这对后续配置I2C相关工程有很大关系
6.2 引脚i配置
简单的使能PB6、PB7的引脚为输出模式即可
6.3 将比赛提供库函数移植到自己工程文件中
6.4 相关代码
在复制过来的工程I2C-hal.c底部编写以下函数
cs
unsigned char I2C_Read(unsigned char addr)
{
unsigned char data;
I2CStart();
I2CSendByte(0xa0);//0是写 1是读
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CStop();
I2CStart();
I2CSendByte(0xa1);
I2CWaitAck();
data = I2CReceiveByte();
I2CSendAck();
I2CStop();
return data;
}
void I2C_Write(unsigned char addr,unsigned char data)
{
I2CStart();
I2CSendByte(0xa0);//0是写 1是读
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CSendByte(data);
I2CWaitAck();
I2CStop();
}
头文件中定义
cs
unsigned char I2C_Read(unsigned char addr);
void I2C_Write(unsigned char addr,unsigned char data);
6.5 工程调试
将上面的频率值分别的高八位和低八位分别写入I2C的指定地址 再将其读取
7.UART
7.1 引脚配置
波特率根据题目设定 一般为9600 同时需要开启中断
7.2 相关代码
cs
//发送字符测试
char temp[20];串口调试
sprintf(temp,"fre1 = %d\r\n",fre1);
HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50);
//接收字符函数
char rx_Alldata[30];
uint8_t rx_data;
unsigned char rx_pointer;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
rx_Alldata[rx_pointer++] = rx_data;
HAL_UART_Receive_IT(&huart1,&rx_data,1);
}
7.3 工程调试
cs
void uart_rx_process()
{
if(rx_pointer>0)
{
if(rx_pointer == 22)
{
sscanf(rx_Alldata,"%4s:%4s:%12s",car_type,car_data,car_time);
}
else
{
char temp[20];
sprintf(temp,"Error");
HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50);
}
rx_pointer = 0;
memset(rx_Alldata,0,30);
}
}
须在头文件中包含string,h
8.RTC
8.1 引脚配置
8.2 相关代码
cs
RTC_TimeTypeDef rtc_time;
RTC_DateTypeDef rtc_date;
void RTC_Rorcess()
{
HAL_RTC_GetTime(&hrtc,&rtc_time,RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc,&rtc_date,RTC_FORMAT_BIN);
}
8.3 工程调试
cs
char text[20];
sprintf(text, "%02d-%02d-%02d",rtc_time.Hours,rtc_time.Minutes,rtc_time.Seconds);
LCD_DisplayStringLine(Line7, (uint8_t *)text);
sprintf(text, "%04d-%02d-%02d-%d",rtc_date.Year ,rtc_date.Month,rtc_date.Date,rtc_date.WeekDay );
LCD_DisplayStringLine(Line8, (uint8_t *)text