准备工作,创建工程

一、系统初始化
1.1 CubeMX配置


1.2 Keil配置
新建APP文件夹





1.3 Keil底层代码
cpp
#include "system.h"
void System_Init(void)
{
GPIOC->ODR &= 0x00ff;//清空PC8-PC15位 LED低电平亮此时全亮
GPIOC->ODR |= 0xff00;//熄灭LED
GPIOD->BSRR = 0x01 << 2;//PD2置1
GPIOD->BRR = 0x01 << 2;//PD2置0
}
二、系统调度器(scheduler.c)
2.1 CubeMX配置
调度器无需配置CubeMX。
2.2 Keil底层代码
cpp
typedef struct{
void (*func)(void);
uint32_t interval;//运行时间间隔
uint32_t next_run;//下次运行时间
}task_t;
/*任务注册*/
task_t tasks[] = {
{key_proc,10,0}
,
{led_proc,10,0}
,
{lcd_proc,100,0}
,
{adc_proc,10,0}
,
{uart_proc,10,0}
};
uint8_t task_count = sizeof(tasks)/sizeof(task_t);//任务数量
void scheduler_run(void)
{//调度器运行
for(uint8_t i = 0;i<task_count;i++)
{
uint32_t current_time = HAL_GetTick();
if(current_time >= tasks[i].next_run)
{
tasks[i].next_run = current_time+tasks[i].interval;
tasks[i].func();
}
}
}
三 、LED配置(led_app.c)
3.1 CubeMX配置
在第一节中已经配置了LED灯的PC8--PC15口以及 PD2的使能口,在此无需额外配置。

3.2 底层代码
cpp
#include "led_app.h"
uint8_t ucled[8] = {0,1,0,1,1,0,0,0};
void led_show(uint8_t *ucled)
{
uint8_t temp = 0x00;
static uint8_t temp_old = 0xff;
for(uint8_t i = 0;i<8;i++)
temp |= (ucled[i] << i);
//temp |= (ucled[i] << (7-i));//temp = 0101 1000
//这里应该把7-去了,因为板子上是反的
if(temp != temp_old)
{
GPIOC->ODR &= 0x00ff;
GPIOC->ODR |= ~(temp << 8);//~0101 1000 0000 0000 == 1010 0111
GPIOD->BSRR |= (0x01 << 2);//PD2 0x04
GPIOD->BRR |= (0x01 << 2);//0x04
temp_old = temp;//
}
}
void led_proc(void)
{
led_show(ucled);
}
四、按键配置(key_app.c)
4.1 CubeMX配置


4.2 底层代码
cpp
#include "key_app.h"
uint8_t key_value;
uint8_t key_old;
uint8_t key_down;
uint8_t key_up;
uint8_t key_read(void)
{
uint8_t temp = 0;
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == GPIO_PIN_RESET)
temp = 1;
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == GPIO_PIN_RESET)
temp = 2;
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == GPIO_PIN_RESET)
temp = 3;
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET)
temp = 4;
return temp;
}
void key_proc(void)
{
key_value = key_read();
key_down = key_value & (key_old ^ key_value);
key_up = ~key_value & (key_old ^ key_value);
key_old = key_value;
}
4.3 按键长按&按键双击
cpp
uint8_t long_flag;
uint16_t key_1s;
uint8_t mul_flag;
uint16_t mul_500ms;
void key_proc(void)
{
key_value = key_read();
key_down = key_value & (key_old ^ key_value);
key_up = ~key_value & (key_old ^ key_value);
key_old = key_value;
if(key_down == 1)
long_flag = 1;
if(key_up == 1)
{
if(long_flag == 1)
{//短按
long_flag = 0;//按下标志清零
key_1s = 0;//计数清零
}
if(long_flag == 2)
{//长按
long_flag = 0;
ucled[7] ^= 1;//切换led状态
}
}
if(key_down == 2)
{
switch(mul_flag)
{
case 0://单击
ucled[6] ^= 1;
mul_flag = 1;
break;
case 1://双击
ucled[5] ^= 1;
mul_flag = 2;
break;
case 2://三击
ucled[4] ^= 1;
mul_flag = 3;
break;
}
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) //中断回调1ms进一次
{//定时器回调函数 800*100/80 000 000=0.001s
if(htim -> Instance == TIM6)
{
if(long_flag == 1)
{
if(++key_1s >= 1000)
{
long_flag = 2;
key_1s = 0;
}
}
if(mul_flag != 0)
{//非零计时 超过500清零
if(++mul_500ms >= 500)
{
mul_500ms = 0;
mul_flag = 0;//超过500状态回归
}
}
}
}
五、LCD配置(lcd_app.c)
5.1 CubeMX配置

使用LCD需要将PC0------PC15都配置为GPIO_Output,此外还有PB5,8,9,PA8也需要配置。(但不配置似乎没啥影响)

5.2 底层代码
5.2.1 初始化语句
需要先将lcd的文件移入自己的项目文件夹

使用LCD需要初始化

将下面代码添加到while循环之前
cpp
LCD_Init();
LCD_Clear(Black);//清除整个LCD屏幕
LCD_SetTextColor(White);//文本色为 白色
LCD_SetBackColor(Black);//背景色为 黑色
5.2.2 官方LCD函数
sprintf合成函数在stdio.h这个库文件中
cpp
#include "lcd_app.h"
char lcd_buffer[128];
void lcd_proc(void)
{
sprintf(lcd_buffer," LCD_APP Test");
LCD_DisplayStringLine(Line0,(u8*)lcd_buffer);
LCD_DrawCircle(100,50,30);
}
5.2.3 升级版
cpp
#include "lcd_app.h"
void LcdSprintf(uint8_t Line,char *format,...)
{
char String[21];//缓冲区用于存放格式化的字符串
va_list arg;//参数列表用于存储可变参数
va_start(arg,format);//初始化可变参数列表
vsprintf(String,format,arg);//格式化字符串并存储在缓冲区中
va_end(arg);//清理参数列表
LCD_DisplayStringLine(Line,(u8*)String);
}
void lcd_proc(void)
{
LcdSprintf(Line2," LCD APP TEST");
}