目录
题目
配置
注意事项
代码 - 默写大师
EEPROM读写函数
LED驱动函数
ADC采集
上电初始化
LCD
按键
PWM互补输出
全部代码
hardware.c
hardware.h
control.c
control.h
main.c
题目
配置
注意事项
复制LCD的工程,先配置资源 --- 勾选完选项一定要再看一眼,可能选择错误
ADC:配置ADC2_IN15,对应PB15引脚
EEROM,配置PB6和PB7
按键 输入模式PB0、PB1、PB2、PA0
LED 一定要使能PD2
PWM互补输出,用TIM15
TIM6 - 10ms基准定时器
代码 - 默写大师
先默写几个函数
EEPROM读写函数
随机地址读函数 - 参考手册的时序
uint8_t EEPROM_ReadByte(uint8_t address)
{
uint8_t data;
I2CStart();
I2CSendByte(0xA0); // address + write
I2CWaitAck();
I2CSendByte(address);
I2CWaitAck();
I2CStop();
I2CStart();
I2CSendByte(0xA1); // address + read
I2CWaitAck();
data = I2CReceiveByte();
I2CSendNotAck();
I2CStop();
return data;
}
void EEPROM_WriteByte(uint8_t address, uint8_t data)
{
I2CStart();
I2CSendByte(0xA0);
I2CWaitAck();
I2CSendByte(address);
I2CWaitAck();
I2CSendByte(data);
I2CWaitAck();
I2CStop();
}
定义几个结构体
//-----------------------
void power_init(void);
void LCD_Disp(void);
void Key_Proc(void);
void ADC_Proc(void);
void PWM_Proc(void);
//-----------------------
struct Tick{
uint32_t lcd;
uint32_t key;
uint32_t adc;
uint32_t pwm;
};
extern struct Tick tick;
struct Flag{
bool PWM_Mode;
uint8_t LCD_View;
};
extern struct Flag flag;
struct Param{
double ADC;
uint16_t PWM_Frq; //频率
uint8_t PWM_Duty_PA9;
uint8_t PWM_Duty_PA14;
};
extern struct Param param;
//定义变量
struct Tick tick;
struct Flag flag;
struct Param param;
struct Keys key;
LED驱动函数
void LED_Disp(uint8_t state)
{
HAL_GPIO_WritePin(GPIOC, 0xFF00, GPIO_PIN_SET); //0ff
HAL_GPIO_WritePin(GPIOC, state << 8, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}
ADC采集
double Get_ADC(ADC_HandleTypeDef *hadc)
{
uint32_t adc;
HAL_ADC_Start(hadc);
adc = HAL_ADC_GetValue(hadc);
return adc*3.3/4040; //这里应该是adc*3.3/4096
}
double Get_ADC(ADC_HandleTypeDef *hadc)
{
uint32_t adc;
HAL_ADC_Start(hadc);
adc = Hal_ADC_GetValue(hadc);
return adc*3.3/4040;
}
返回值应该是adc*3.3/4096,但是这里我改成了return adc*3.3/4040; 补偿电压采集的数据,范围是0~3.30V
void ADC_Proc(void)
{
if (uwTick - tick.adc < 250)
return;
tick.adc = uwTick;
param.ADC = Get_ADC(&hadc2);
}
上电初始化
void power_init(void)
{
LCD_Clear(Black);
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
LED_Disp(0x00); //0ff
I2CInit();
if (EEPROM_ReadByte(0x55) != 0x11) //第一次上电
{
param.PWM_Frq = 1000; //初始化参数
EEPROM_WriteByte(0x55, 0x11);
HAL_Delay(10);
EEPROM_WriteByte(0x01, 1);
HAL_Delay(10);
}
else
{
param.PWM_Frq = EEPROM_ReadByte(0x01)*1000;
}
}
LCD
封装LCD显示函数,使用可变参数列表
#include "stdio.h"
#include "string.h"
#include "stdarg.h"
void LCD_Printf(uint8_t linex, char *format, ...)
{
char lcd_show_text[30];
memset(lcd_show_text, '\0', sizeof(lcd_show_text));
va_list arg;
va_start(arg, format);
vsprintf(lcd_show_text, format, arg);
LCD_DisplayStringLine(linex, (uint8_t *)lcd_show_text);
va_end(arg);
}
LCD界面
void LCD_Disp(void)
{
if (uwTick - tick.lcd < 200)
return;
tick.lcd = uwTick;
if (flag.LCD_View == 0)
{
LCD_Printf(Line0, " Para");
LCD_Printf(Line2, " ADC_V:%.2fV ", param.ADC);
LCD_Printf(Line4, " State:%s ", (flag.PWM_Mode) ? "start" : "stop" ); //三目运算符
LCD_Printf(Line6, " Siginal:PA9: %2d%% ", (flag.PWM_Mode) ? (param.PWM_Duty_PA9) : 0);
LCD_Printf(Line7, " PB14:%2d%% ", (flag.PWM_Mode) ? (param.PWM_Duty_PA14) :0);
LCD_Printf(Line8, " %dKHz ", param.PWM_Frq/1000);
LCD_Printf(Line9, " 1");
}
else if (flag.LCD_View == 1)
{
LCD_Printf(Line1, " Setting");
LCD_Printf(Line5, " Signal_Frq:%dKHz ", param.PWM_Frq/1000);
LCD_Printf(Line9, " 2");
}
}
按键
按键扫描
void Key_Read(void)
{
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == 0)
key.value = 1;
else if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1) == 0)
key.value = 2;
else if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2) == 0)
key.value = 3;
else if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 0)
key.value = 4;
else
key.value = 0;
key.down = key.value & (key.value ^ key.old);
key.up = ~key.value & (key.value ^ key.old);
key.old = key.value;
}
按键处理函数
void Key_Proc(void)
{
if (uwTick - tick.key < 10) //10ms
return;
tick.key = uwTick;
Key_Read();
if (key.down == 1) //“B1”按键设定为“启动/停止”按键
{
flag.PWM_Mode = !flag.PWM_Mode;
}
if (key.down == 2)
{
//先判断是否存入E2PROM
if (flag.LCD_View == 1)
{
//eeprom
EEPROM_WriteByte(0x01, param.PWM_Frq/1000); //存整数部分
HAL_Delay(10);
}
// 再刷新界面
flag.LCD_View ++;
if (flag.LCD_View == 2) flag.LCD_View = 0;
LCD_Clear(Black);
}
if (key.down == 3)
{
if (flag.LCD_View == 1)
{
param.PWM_Frq += 1000;
if (param.PWM_Frq > 10000) param.PWM_Frq = 1000;
}
}
}
PWM互补输出
PWM配置的定时器,给的80-1预分频,那计数值就是80M/80 = 1M
频率设置:TIMx->ARR = 1e6/频率
占空比设置:TIMx->CCRx = (1e6/频率)*占空比
我代码里的占空比只保留整数部分,所以我的CCR赋值时候最后除以100。
void PWM_Proc(void)
{
if (uwTick - tick.pwm < 100)
return;
tick.pwm = uwTick;
//计算占空比
param.PWM_Duty_PA9 = (uint8_t)(param.ADC*100.0/3.3);
param.PWM_Duty_PA14 = 100-param.PWM_Duty_PA9;
TIM15->ARR = 1e6 / param.PWM_Frq; //计算频率
if (flag.PWM_Mode == 0) //stop
{ //关闭PWM输出
LED_Disp(0);
HAL_TIM_PWM_Stop(&htim15, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Stop(&htim15, TIM_CHANNEL_1);
}
else
{
LED_Disp(0x01);
TIM15 -> CCR1 = (TIM15->ARR + 1) * param.PWM_Duty_PA9 / 100;
HAL_TIM_PWM_Start(&htim15, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Start(&htim15, TIM_CHANNEL_1);
}
}
互补PWM的函数
//PWM开启
HAL_TIM_PWM_Start(&htim15, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Start(&htim15, TIM_CHANNEL_1);
//PWM关闭
HAL_TIM_PWM_Stop(&htim15, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Stop(&htim15, TIM_CHANNEL_1);
全部代码
hardware.c
#include "hardware.h"
struct Keys key;
void LED_Disp(uint8_t state)
{
HAL_GPIO_WritePin(GPIOC, 0xFF00, GPIO_PIN_SET); //0ff
HAL_GPIO_WritePin(GPIOC, state << 8, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}
void Key_Read(void)
{
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == 0)
key.value = 1;
else if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1) == 0)
key.value = 2;
else if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2) == 0)
key.value = 3;
else if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 0)
key.value = 4;
else
key.value = 0;
key.down = key.value & (key.value ^ key.old);
key.up = ~key.value & (key.value ^ key.old);
key.old = key.value;
}
double Get_ADC(ADC_HandleTypeDef *hadc)
{
uint32_t adc;
HAL_ADC_Start(hadc);
adc = HAL_ADC_GetValue(hadc);
return adc*3.3/4040;
}
hardware.h
#ifndef __HARDWARE_H
#define __HARDWARE_H
#include "stm32g4xx_hal.h"
void LED_Disp(uint8_t state);
void Key_Read(void);
double Get_ADC(ADC_HandleTypeDef *hadc);
struct Keys{
uint8_t value;
uint8_t old;
uint8_t down;
uint8_t up;
};
extern struct Keys key;
#endif
control.c
#include "control.h"
//定义变量
struct Tick tick;
struct Flag flag;
struct Param param;
void power_init(void)
{
LCD_Clear(Black);
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
LED_Disp(0x00); //0ff
I2CInit();
if (EEPROM_ReadByte(0x55) != 0x11) //第一次上电
{
param.PWM_Frq = 1000; //初始化参数
EEPROM_WriteByte(0x55, 0x11);
HAL_Delay(10);
EEPROM_WriteByte(0x01, 1);
HAL_Delay(10);
}
else
{
param.PWM_Frq = EEPROM_ReadByte(0x01)*1000;
}
}
void LCD_Disp(void)
{
if (uwTick - tick.lcd < 200)
return;
tick.lcd = uwTick;
if (flag.LCD_View == 0)
{
LCD_Printf(Line0, " Para");
LCD_Printf(Line2, " ADC_V:%.2fV ", param.ADC);
LCD_Printf(Line4, " State:%s ", (flag.PWM_Mode) ? "start" : "stop" ); //三目运算符
LCD_Printf(Line6, " Siginal:PA9: %2d%% ", (flag.PWM_Mode) ? (param.PWM_Duty_PA9) : 0);
LCD_Printf(Line7, " PB14:%2d%% ", (flag.PWM_Mode) ? (param.PWM_Duty_PA14) :0);
LCD_Printf(Line8, " %dKHz ", param.PWM_Frq/1000);
LCD_Printf(Line9, " 1");
}
else if (flag.LCD_View == 1)
{
LCD_Printf(Line1, " Setting");
LCD_Printf(Line5, " Signal_Frq:%dKHz ", param.PWM_Frq/1000);
LCD_Printf(Line9, " 2");
}
}
void Key_Proc(void)
{
if (uwTick - tick.key < 10) //10ms
return;
tick.key = uwTick;
Key_Read();
if (key.down == 1) //“B1”按键设定为“启动/停止”按键
{
flag.PWM_Mode = !flag.PWM_Mode;
}
if (key.down == 2)
{
//先判断是否存入E2PROM
if (flag.LCD_View == 1)
{
//eeprom
EEPROM_WriteByte(0x01, param.PWM_Frq/1000); //存整数部分
HAL_Delay(10);
}
// 再刷新界面
flag.LCD_View ++;
if (flag.LCD_View == 2) flag.LCD_View = 0;
LCD_Clear(Black);
}
if (key.down == 3)
{
if (flag.LCD_View == 1)
{
param.PWM_Frq += 1000;
if (param.PWM_Frq > 10000) param.PWM_Frq = 1000;
}
}
}
void ADC_Proc(void)
{
if (uwTick - tick.adc < 250)
return;
tick.adc = uwTick;
param.ADC = Get_ADC(&hadc2);
}
void PWM_Proc(void)
{
if (uwTick - tick.pwm < 100)
return;
tick.pwm = uwTick;
//计算占空比
param.PWM_Duty_PA9 = (uint8_t)(param.ADC*100.0/3.3);
param.PWM_Duty_PA14 = 100-param.PWM_Duty_PA9;
TIM15->ARR = 1e6 / param.PWM_Frq; //计算频率
if (flag.PWM_Mode == 0) //stop
{ //关闭PWM输出
LED_Disp(0);
HAL_TIM_PWM_Stop(&htim15, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Stop(&htim15, TIM_CHANNEL_1);
}
else
{
LED_Disp(0x01);
TIM15 -> CCR1 = (TIM15->ARR + 1) * param.PWM_Duty_PA9 / 100;
HAL_TIM_PWM_Start(&htim15, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Start(&htim15, TIM_CHANNEL_1);
}
}
control.h
#ifndef __CONTROL_H
#define __CONTROL_H
#include "stm32g4xx_hal.h"
#include "string.h"
#include "stdio.h"
#include "stdbool.h"
#include "hardware.h"
#include "i2c_hal.h"
#include "tim.h"
#include "adc.h"
#include "lcd.h"
//-----------------------
void power_init(void);
void LCD_Disp(void);
void Key_Proc(void);
void ADC_Proc(void);
void PWM_Proc(void);
//-----------------------
struct Tick{
uint32_t lcd;
uint32_t key;
uint32_t adc;
uint32_t pwm;
};
extern struct Tick tick;
struct Flag{
bool PWM_Mode;
uint8_t LCD_View;
};
extern struct Flag flag;
struct Param{
double ADC;
uint16_t PWM_Frq; //频率
uint8_t PWM_Duty_PA9;
uint8_t PWM_Duty_PA14;
};
extern struct Param param;
//-----------------------
#endif
main.c
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "tim.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "control.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM6_Init();
MX_ADC2_Init();
MX_TIM15_Init();
/* USER CODE BEGIN 2 */
LCD_Init();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
power_init();
while (1)
{
Key_Proc();
LCD_Disp();
ADC_Proc();
PWM_Proc();
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
完结,第六届国赛比较简单,只有PWM互补输出是没有写过的
后续会更新其他届的赛题,同步源码到我的Gitee~
波形展示:具体见B站演示视频