当前位置: 首页 > article >正文

在Proteus软件仿真STM32F103寄存器玩俄罗斯方块之第二篇

这篇文章主要是代码分享,以及简单解析。
对代码哪里不理解的,欢迎找我讨论哈。
和上篇代码结构不一样的地方是多文件和分模块,使得工程更加有条理。
这篇文章主要是单片机输入和输出典型代表,这个在单片机初学中是很常见的。
这次的代码是直接按照文件抛出来的,不像上次解释的那么细。

目录

  • 独立按钮的处理
  • LCD12864的基础功能实现

独立按钮的处理

独立按钮在单片机初学阶段一定是绕不开的。
计算机系统的组成里边包括输入和输出两个部分。
我们的单片机也是一个计算机系统,区别是单片机是专用的,高度可裁剪的计算机系统
独立按键就是一个典型的输入部分。
在这里不给你讲按键过于细节的部分。
在PID电机调速那篇文章中也就简单的独立按钮处理。这个工程里就是更规范化了,适合在实际工程中使用。同样这个工程中也没有做消抖处理。
下边的代码是按键配置头文件,也就是h文件。

#ifndef __BUTTON_CFG_H_
#define __BUTTON_CFG_H_

#include "Reg.h"

typedef enum
{
    Btn_Up,
    Btn_Down,
    Btn_Left,
    Btn_Right,
    
    Btn_TotalNum
}Button;

typedef enum
{
    Btn_Sta_Press,
    Btn_Sta_Release,
    Btn_Sta_PressLong,
}Button_State;

typedef struct
{
    Button              BtnID;
    GPIO_TypeDef        *GPIOx;
    uint8_t             Pinx;
    uint8_t             FreeLevel;
}ButtonCfg_Def;

#endif

下边的代码是按键配置C文件。
实际上配置文件里的GPIOB这个参数没有使用,原因就是在写GPIO这个初始化文件的时候是直接针对B端口的函数,没有写成通用的。

#include "button_cfg.h"

const ButtonCfg_Def BtnCfg[Btn_TotalNum] = {
    {Btn_Up,    GPIOB,  5,      1},
    {Btn_Down,  GPIOB,  6,      1},
    {Btn_Left,  GPIOB,  7,      1},
    {Btn_Right, GPIOB,  8,      1},
};

下边的代码是按键对外开放的接口文件,也就是h文件。
一般情况下这种接口文件尽可能要简短。

#ifndef __BUTTON_H
#define __BUTTON_H

#include "button_cfg.h"

void Button_Init(void);
void Button_Step(void);
uint8_t Button_Read(Button *btn,Button_State *sta);

#endif

下边的代码是按键功能C语言实现的代码文件,里边实现了简单的环形队列,用于存放按键触发的状态。
按键功能的实现,很大程度取决你习惯用什么技术,有的人喜欢用回调去做,这种方式在图形界面中比较常见。
这种方式在之前H7 串口的那几篇文章中有实现。

#include "button.h"
#include "GPIO.h"

#define BtnQueueDeep    16ul

typedef struct{
    Button btn;
    Button_State sta;
    uint8_t reset;
}BtnQueue_Def;

static BtnQueue_Def BtnQueue[BtnQueueDeep];

struct BtnRW_CB
{
    uint8_t ReadNum;
    uint8_t WriteNum;
}BtnRW;

static uint8_t Btn_PressCount[Btn_TotalNum];
static Button_State Btn_LastSta[Btn_TotalNum];

extern ButtonCfg_Def BtnCfg[Btn_TotalNum];

static void BtnQueueWrite(Button btn,Button_State sta)
{
    Btn_LastSta[btn] = sta;
    BtnQueue[BtnRW.WriteNum].btn = btn;
    BtnQueue[BtnRW.WriteNum].sta = sta;
    BtnQueue[BtnRW.WriteNum ++].reset = 0;
    BtnRW.WriteNum &= 0x0F;
}

static BtnQueue_Def* BtnQueueRead(void)
{
    if(BtnQueue[BtnRW.ReadNum].reset == 0)
    {
        uint8_t num = BtnRW.ReadNum ++;
        BtnRW.ReadNum &= 0x0F;
        BtnQueue[num].reset = 1;
        return &BtnQueue[num];
    }
    else
        return 0;
}

static void Btn_Init(Button btn)
{
    if(BtnCfg[btn].BtnID == btn)
    {
        PB_In_Init(BtnCfg[btn].Pinx);
        PB_Out(BtnCfg[btn].Pinx,BtnCfg[btn].FreeLevel);
    }
}

static void Btn_Value_Read(Button btn)
{
    if(BtnCfg[btn].BtnID == btn)
    {
        uint8_t pinvalue = PB_In(BtnCfg[btn].Pinx);
        if(pinvalue != BtnCfg[btn].FreeLevel)
        {
            switch(Btn_LastSta[btn])
            {
                case Btn_Sta_Press:
                    {
                        Btn_PressCount[btn] ++;
                        if(Btn_PressCount[btn] > 50)
                        {
                            BtnQueueWrite(btn,Btn_Sta_PressLong);
                        }
                    }
                    break;
                case Btn_Sta_Release:
                    {
                        BtnQueueWrite(btn,Btn_Sta_Press);
                    }
                    break;
                default:break;
            }
        }
        else
        {
            if(Btn_LastSta[btn] == Btn_Sta_Release)return;
            
            Btn_PressCount[btn] = 0;
            BtnQueueWrite(btn,Btn_Sta_Release);
        }
    }
}

void Button_Init(void)
{
    for(uint32_t i = 0;i < Btn_TotalNum;i ++)
    {
        Btn_Init((Button)i);
        Btn_LastSta[i] = Btn_Sta_Release;
    }
    
    for(uint32_t i = 0;i < BtnQueueDeep;i ++)
    {
        BtnQueue[i].reset = 1;
    }
    
    BtnRW.ReadNum = 0;
    BtnRW.WriteNum = 0;
}

void Button_Step(void)//rate 10ms
{
    for(uint32_t i = 0;i < Btn_TotalNum;i ++)
    {
        Btn_Value_Read((Button)i);
    }
}

uint8_t Button_Read(Button *btn,Button_State *sta)
{
    uint8_t ret = 0;
    BtnQueue_Def *p = BtnQueueRead();
    if(p)
    {
        *btn = p->btn;
        *sta = p->sta;
        ret = 0x01;
    }
    return  ret;
}

按钮相关的代码到这里就结束了。

LCD12864的基础功能实现

这一次用的LCD屏幕显示画面。这个也算是个典型的输出系统吧。
总之在51中这个显示屏还是用的挺多的,但是需要注意的是,你一定要看清楚型号哦,每个型号他们的时序和寄存器什么的可能都不一样,在proteus这个软中中我选择的这个型号就和你在某宝买到的常见的那个就不是同一个,也就是说时序是不一样的,所以你首先要找到他的数据手册(数据手册点我),里边会包含时序和寄存功能。
阅读这个时序和寄存器你就需要一定的数电功底了,可以联系我,带你入门,高深的东西教不了你,但是简单的东西还是没有问题的,默念三遍,要谦虚不能托大,要谦虚不能托大,要谦虚不能托大。

首先还是配置文件,这次是12864的配置头文件。
嗯,还是非常的简短。实在没啥可配置的。

#ifndef __LCD12864_CFG_H_
#define __LCD12864_CFG_H_

#define LCD_Width       128ul
#define LCD_Hight       64ul

#define LCD_Pixel_Bit_Width     8ul

#endif

然后是LCD的对外接口头文件了。
这里对外的接口主要是每一个像素点的读写,屏幕方向的设置和屏幕大小的读取接口。

#ifndef __LCD12864_H_
#define __LCD12864_H_

#include "stdint.h"

void LCD_Init(void);
void LCD_Step(void);

uint8_t LCD_Width_Get(void);
uint8_t LCD_Hight_Get(void);

uint8_t LCD_Direction_Get(void);
void LCD_Direction_Set(uint8_t dir);

void LCD_Pixel_Write(uint8_t X,uint8_t Y,uint8_t value);
uint8_t LCD_Pixel_Read(uint8_t X,uint8_t Y);

#endif

最后是LCD接口的实现部分了。
这里LCD用的是串行接口,不支持读操作,所以直接在RAM中建一个对应的显示缓存数组,定时刷新到lcd就行了,所有数据都在内存里边,比较简单。

#include "GPIO.h"
#include "LCD12864.h"
#include "LCD12864_cfg.h"

static uint8_t LCD_memory[LCD_Hight / LCD_Pixel_Bit_Width][LCD_Width];
static volatile uint8_t Direction;

void LCD_Pin_Init(void)
{
    PB_Out_Init(0);
    PB_Out_Init(1);
    PB_Out_Init(2);
    PB_Out_Init(3);
    PB_Out_Init(4);
    PB_Out(0,1);
    PB_Out(1,1);
    PB_Out(2,1);
    PB_Out(3,1);
    PB_Out(4,1);
}

void LCD_Write_Byte(uint8_t byte)
{
    for(uint32_t i = 0;i < 8;i ++)
    {
        PB_Out(3,0);//SCK
        PB_Out(4,byte>>7);//SI
        PB_Out(3,1);
        byte <<= 1;
    }
}

void LCD_Write_CMD(uint8_t cmd)
{
    PB_Out(2,0);//cs
    PB_Out(0,0);//RS
    LCD_Write_Byte(cmd);
    PB_Out(2,1);//cs
}

void LCD_Write_Data(uint8_t data)
{
    PB_Out(2,0);//cs
    PB_Out(0,1);//RS
    LCD_Write_Byte(data);
    PB_Out(2,1);//cs
}

void LCD_Init(void)
{
    LCD_Pin_Init();
    //LCD_Write_CMD(0x2C);//升压1
    //LCD_Write_CMD(0x2e);//升压2
    //LCD_Write_CMD(0x2f);//升压3
    //LCD_Write_CMD(0x23);//粗调对比度
    //LCD_Write_CMD(0x81);//微调对比度
    //LCD_Write_CMD(0x1f);//微调对比度
    //LCD_Write_CMD(0xa2);//偏压比
    LCD_Write_CMD(0xC0);//行扫描顺序:从上到下  0上下  8下上
    LCD_Write_CMD(0xa1);//列扫描顺序:从坐到右  0左右  1右左
    LCD_Write_CMD(0xa6);//正反显示 7高亮显示 6暗显示
    //LCD_Write_CMD(0x60);//起始行 从第一行显示
    LCD_Write_CMD(0xaf);//开显示
}

void LCD_Write_line(uint8_t line)
{
    line &= 0x07;
    LCD_Write_CMD(0xb0 + line);
    LCD_Write_CMD(0x10);
    LCD_Write_CMD(0x00);
    for(uint32_t i = 0;i < LCD_Width;i ++)
    {
        LCD_Write_Data(LCD_memory[line][i]);
    }
}

void LCD_Step(void)
{
    for(uint32_t i = 0;i < (LCD_Hight / LCD_Pixel_Bit_Width);i ++)
    {
        LCD_Write_line(i);
    }
}

uint8_t LCD_Width_Get(void)
{
    return LCD_Width;
}

uint8_t LCD_Hight_Get(void)
{
    return LCD_Hight;
}

uint8_t LCD_Direction_Get(void)
{
    return Direction;
}

void LCD_Direction_Set(uint8_t dir)
{
    Direction = dir & 0x01;
}

void LCD_Pixel_Write(uint8_t X,uint8_t Y,uint8_t value)
{
    uint8_t x = Direction ? Y : X;
    uint8_t y = Direction ? LCD_Hight - 1 - X : Y;
    if(x >= LCD_Width) return;
    if(y >= LCD_Hight) return;
    value &= 0x01;
    
    LCD_memory[y >> 3][x] &= ~(0x01 << (y & 0x07));
    LCD_memory[y >> 3][x] |= value << (y & 0x07);
}

uint8_t LCD_Pixel_Read(uint8_t X,uint8_t Y)
{
    uint8_t x = Direction ? Y : X;
    uint8_t y = Direction ? LCD_Hight - 1 - X : Y;
    if(x >= LCD_Width) return 0xFF;
    if(y >= LCD_Hight) return 0xFF;
    
    return (LCD_memory[y >> 3][x] >> (y & 0x07)) & 0x01;
}

LCD12864基础部分到这里就结束了。足够发挥你的创意了。


http://www.kler.cn/a/501836.html

相关文章:

  • linux自动分区后devmappercentos-home删除后合并到其它分区上
  • type 属性的用途和实现方式(图标,表单,数据可视化,自定义组件)
  • 【华为OD-E卷 - 求字符串中所有整数的最小和 100分(python、java、c++、js、c)】
  • VSCode 插件
  • python 寻找数据拐点
  • Oracle重启后业务连接大量library cache lock
  • 在 Azure 100 学生订阅中新建一台 Ubuntu VPS,并通过 Docker 部署 Nginx 服务器
  • 《Java核心技术II》网络使用telnet
  • android四大组件之一——Service
  • MyBatis(一)
  • 阿里云存储图像bug修复
  • 4. scala高阶之隐式转换与泛型
  • vue3+vite+ts集成第三方js
  • 【文件锁】多进程线程安全访问文件demo
  • 【初识扫盲】逆概率加权
  • Windows 10 ARM工控主板连接I2S音频芯片
  • 32_Redis分片集群原理
  • 《零基础Go语言算法实战》【题目 2-26】goroutine 的执行效率问题
  • HDFS 的API的操作
  • 【Rust】函数
  • 【网络协议】EIGRP - 第二部分
  • 使用Deepseek搭建类Cursor编辑器
  • SQL语言的计算机基础
  • LeetCode:216.组合总和III
  • 基于单片机的书写坐姿规范提醒器的设计(论文+源码)
  • 自动化机械臂视觉跟踪和手眼校准