嵌入式进阶——数码管2

🎬 秋野酱:《个人主页》
🔥 个人专栏:《Java专栏》《Python专栏》

⛺️心若有所向往,何惧道阻且长

驱动封装

根据前面的内容可以将代码进行封装,封装后作为一个独立的整体出现:

#ifndef __NIXIE_H__
#define __NIXIE_H__

#include "config.h"

#define	NIXIE_DI	P44	// 数据输入
#define	NIXIE_SCK	P42	// 移位寄存器
#define	NIXIE_RCK	P43	// 锁存寄存器

#define NIXIE_PIN_INIT() {    P4M0 &= ~0x1c; P4M1 &= ~0x1c; }

void NIXIE_init();

// 		u8 a_dat = 0x12;	// 0001 0010	字母位
//		u8 b_idx = 0x1F;	// 0001 1111	数字位
void NIXIE_show(u8 a_dat, u8 b_idx);

// num对应数字在数组里的位置(索引)
// id 显示在指定位置(0 -> 7)
void NIXIE_display(u8 num, u8 id);

#endif

● 定义 NIXIE_init() 函数,负责对GPIO相关的初始化
● 定义 NIXIE_display()函数负责显示

#include "NIXIE.h"
#include "Delay.h"

#define GET_BIT_VAL(byte, pos)	(byte & (1 << pos))

//#define NOP_TIME() NOP40()	// 用于看logic分析仪
#define NOP_TIME() NOP2()

// 锁存操作 - 多行宏定义
#define RCK_ACTION() 		\
		NIXIE_RCK = 0;		\
		NOP_TIME();			\
		NIXIE_RCK = 1;		\
		NOP_TIME();

void NIXIE_init(){
	NIXIE_PIN_INIT();
}


static void NIXIE_out(u8 dat){
		char i;
		// 8bit,先发出去的会作为高位
		for(i = 7; i >= 0; i--){            
			NIXIE_DI = GET_BIT_VAL(dat, i);
			// 寄存器的移位操作
			NIXIE_SCK = 0;
			NOP_TIME(); // 休眠一会儿
			NIXIE_SCK = 1;
			NOP_TIME(); // 休眠一会儿
		}
}

// 每次可以显示多个,但是内容都是一样的a_dat
// 		u8 a_dat = 0x12;	// 0001 0010	字母位
//		u8 b_idx = 0x1F;	// 0001 1111	数字位
void NIXIE_show(u8 a_dat, u8 b_idx){
	
		// 显示 7.
		// 0111 1000
		// 先发字母位 (控制显示的内容)	// 0点亮
		// 8bit,先发出去的会作为高位
		NIXIE_out(a_dat);
	
		// 0,1,2,3....7
		// 再发数字位 (控制显示哪几个) // 只要不是0,就是高电平
		// 1111 1011
		// 7.7.空7. 7.7.7.7.  -------------------与二级制是反向
		// 8bit,先发出去的会作为高位
		NIXIE_out(b_idx);
		
		// 锁存操作
		RCK_ACTION();
		
}

以上为Nixie.h的实现,也是对之前代码的封装处理。

#include "Config.h"
#include "NIXIE.h"
#include "Delay.h"

int main() {
    u8 a_dat = 0xF8;	// 0001 0010	字母位
    u8 b_idx = 0xFB;	// 0001 1111	数字位

    NIXIE_init();

	// NIXIE_show写到循环外边即可
	NIXIE_show(a_dat, b_idx);
    while(1) {
    }
}

封装的一些疑问

封装的特点
封装是面向对象程序设计中的一个重要概念,它将数据和行为封装在一起,形成一个独立的单元。下面是封装的特点:

  1. 数据隐藏:封装可以隐藏数据,只对外界公开必要的接口,从而保证数据的安全性和可靠性。
  2. 接口统一:封装可以将数据和行为组织在一起,形成一个类或对象,通过统一的接口对外提供服务,便于使用和管理。
  3. 信息隐藏:封装可以隐藏实现细节,只对外界公开必要的信息,从而降低了程序的复杂度和耦合度,提高了程序的可维护性和可扩展性。
  4. 可重用性:封装可以将数据和行为封装成一个独立的单元,便于复用和重复利用,提高了程序的开发效率和代码的可重用性。
  5. 封装和继承相结合:封装和继承是面向对象程序设计中的两个重要概念,它们相互配合,可以构建出更加复杂、灵活和可扩展的程序。
    总之,封装是面向对象程序设计的核心思想之一,它可以提高程序的可靠性、安全性、可维护性和可扩展性,是程序设计中不可或缺的重要概念。
    当前设计问题
    接口设计:定义初始化(Nixie_init),和具体功能(Nixie_display),初始化和芯片开发板设计相关,功能的定义和业务相关。
    初始化问题:为什么不用库函数?首先是可以使用库函数的。观察使用库函数和不是库函数的区别。一个初始化写在头文件,一个写在c文件。c文件是实现,做到抛开平台相关是最好的方案,也就是换了芯片平台,实现不动,通过改变头中的配置,就可以做到移植。(当然,理想状态是这样的,还得看实现复杂度。目标明确,尽量做到这个,为移植提供最少变化方案,这个是共识)
// 索引对应表格参见:
// https://www.yuque.com/icheima/stc8h/kmz2mllvxs1uvdfy#lLhhp
u8 code LED_TABLE[] = 
{
	// 0 	1	 2	-> 9	(索引012...9)
	0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,
	// 0. 1. 2. -> 9.	(索引10,11,12....19)
    0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10,
	// . -						(索引20,21)
	0x7F, 0xBF,
	// AbCdEFHJLPqU		(索引22,23,24....33)
	0x88,0x83,0xC6,0xA1,0x86,0x8E,0x89,0xF1,0xC7,0x8C,0x98,0xC1
};

// 每次只显示一个
// 	\arg num 对应数字在数组里的位置(索引)
//  \arg id 显示在指定位置(0 -> 7)
void NIXIE_display(u8 num, u8 id){
	u8 a_dat = LED_TABLE[num];	// 0001 0010	字母位
	u8 b_idx = 1 << id;					// 0010 0000	数字位 5
	
	NIXIE_show(a_dat, b_idx);
}

● index在没有封装前,一个bit表示一个灯。封装后表示灯的下标。这样设计符合人的思考习惯,函数就是让人调得舒服
● dat在没有封装前,是自己来总结灯的开灭,封装后表示自己定义的字符,通过下标访问。这样简化操作。还是为了调用舒服。

数字走马灯实现

#include "Config.h"
#include "NIXIE.h"
#include "Delay.h"


int main() {
    u8 i;
    // u8 a_dat = 0xF8;	// 0001 0010	字母位
    // u8 b_idx = 0xFB;	// 0001 1111	数字位

    NIXIE_init();

    // NIXIE_show写到循环外边即可
    // NIXIE_show(a_dat, b_idx);

    while(1) {		
        // 1000 / (2 * 8) = 62.5帧/秒
        // 走马灯
        for(i = 0; i < 8; i++) {
            NIXIE_display(i + 1, i);
            delay_ms(100);
        }
    }
}

扩展知识

原理图中,大部分引脚我们已经知道了,但是还有些引脚功能不清楚。
● RESET: 10号引脚。
● OUTPUT_ENABLE: 13号引脚。
在这里插入图片描述

  • 9
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
sscanf函数是C语言中一个非常常用的函数,它可以将一个字符串按照指定的格转换成相应的数据类型。在嵌入开发中,sscanf函数也是非常常见的,因为很多时候需要从串口或者其他外部设备中读取数据,并将其转换成相应的数据类型行处理。下面是一些sscanf函数的使用技巧: 1. 使用sscanf函数时一定要注意格字符串的正确性。格字符串中的占位符必须与待转换的数据类型相对应,否则会发生未知错误。 2. 如果待转换的字符串中包含多个数据,可以使用多个占位符行转换。例如,如果待转换的字符串为"1,2,3",可以使用" %d,%d,%d"的格字符串行转换。 3. 可以使用sscanf函数的返回值来判断转换是否成功。如果返回值等于待转换字符串的长度,则说明转换成功,否则转换失败。 4. 如果待转换的字符串中包含浮点数,可以使用"%f"或者"%lf"的格字符串行转换。 5. 如果待转换的字符串中包含十六制数,可以使用"%x"的格字符串行转换。 6. 如果待转换的字符串中包含字符或字符串,可以使用"%c"或者"%s"的格字符串行转换。 7. 如果待转换的字符串中包含指针类型的数据,可以使用"%p"的格字符串行转换。 总之,在使用sscanf函数时一定要注意格字符串的正确性,否则很容易出现转换错误的情况。同时,还应该注意sscanf函数返回值的判断,以确保转换的正确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋野酱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值