因为项目原因需要用到TM1637,实现驱动数码管和按键扫描,参考了网络上搜索到的一些例程,基本实现了功能要求,能够实现数码管点亮和按键扫描。

  调试过程中也出现一些问题,现在描述一下问题和解决方法。

  问题1:函数必须带参数,无参数无法正确读取键值

  问题2:获取到的键值与LUA版本(运行在AIR724UG上的TM1637驱动程序)获取的键值有出入

  关于第一个问题,如下图所示的tm1637_process函数:

    如果函数定义为tm1637_process(void),即无参数函数,则无法读取到正确按键值,一共四个按键,总是有两个按键的值是相同的。

    如果函数定义为tm1637_process(uint16 num),即有参数函数,则可以读取到按键值。但是也存在一个问题,系统运行一段时间后,仍然变成上述的四个按键其中两个按键键值相同的故障。

    笔者尝试从TM1637的驱动时序上入手、更改驱动速率波特率等方法解决,没有效果。最后发现问题出在按键读取函数上,按键读取函数定义变量没有赋初始值

    

    结合程序来说,函数内使用了变量rekey,如果不赋初值,则初值会出现随机数,则读取按键值会出现错误,无法读取到正确的按键值。修复这个错误之后,tm1637_process函数是否带参数,都不影响按键读取了。

   第二个问题,STM32驱动TM1637得到的四个按键值分别是0xF7 0xF6 0xF5 0xF4,但是同样的电路板使用AIR724UG读取到的按键值却分别是 0xEF 0x6F 0xAF 0x2F。

   经过分析,笔者找到了其中的原因,总结起来就是,两个单片机程序都是按照TM1637的读取时序逐个bit读取按键值,但是对按键读取到的二进制数值处理方向不同。STM32处理是  左低右高  AIR724UG处理是 左高右低,具体如下表所示:

  

下图是AIR724UG读取到的键值

最后 附上驱动代码

TM1637.H
 #ifndef _TM1637_H_
#define _TM1637_H_ //包含头文件
#include "delay.h"
#include "stm32f0xx.h" //定义端口操作
#define SET 1 //置高
#define CLR 0 //置低 #define TM_SCL_PORT GPIOB
#define TM_SCL_PIN GPIO_Pin_8
#define TM_SDA_PORT GPIOB
#define TM_SDA_PIN GPIO_Pin_9 #define SDA_IN() GPIO_ReadInputDataBit(TM_SCL_PORT, TM_SCL_PIN)
#define SDA_H() GPIO_SetBits(TM_SCL_PORT, TM_SCL_PIN) //端口置高
#define SDA_L() GPIO_ResetBits(TM_SCL_PORT, TM_SCL_PIN) //端口置低 #define SCL_H() GPIO_SetBits(TM_SDA_PORT, TM_SDA_PIN)
#define SCL_L() GPIO_ResetBits(TM_SDA_PORT, TM_SDA_PIN) //命令定义
#define DisCtr 0x8D //显示控制,显示开,亮度4/16
#define DisMode 0x44 //固定地址方式写显存
#define ReadKey 0x42 //读取按键寄存器 #define tm_addr_0 0xc5 //显示地址0
#define tm_addr_1 0xc4 //显示地址1
#define tm_addr_2 0xc3 //显示地址2
#define tm_addr_3 0xc2 //显示地址3
#define tm_addr_4 0xc1 //显示地址4
#define tm_addr_5 0xc0 //显示地址5 #define key_up 0x01
#define key_down 0x02
#define key_left 0x03
#define key_right 0x04
#define key_ok 0x05
#define key_ng 0x06 //函数声明
void tm1637_init(void); void tm1637_process(void); void tm1637_key_process(void);
uint8_t tm1637_read_key(void);
void tm1637_menu_process(uint8_t key_value); void tm1637_show_dec(uint16_t dec_num);
void tm1637_cmd_send(uint8_t addr, uint8_t cmd, uint8_t datH, uint8_t datL);
#endif
TM1637.C

//包含intrins.h头文件,可以使用_nop_()函数
#include "tm1637.h" #include "looplist.h"
#include "usart.h" #define TM1637 1 #define SPC_NONE 0x00 uint16_t key_press_cnt = 0;
uint8_t key_value_new = 0;
uint8_t key_value_old = 0; uint8_t com_tx_buf[] = { 0xFF, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xE4 };
uint8_t num_code7[] = { 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71 }; // 0-F
uint8_t num_code8[] = { 0xBF, 0x86, 0xDB, 0xCF, 0xE6, 0xED, 0xFD, 0x87, 0xFF, 0xEF, 0xF7, 0xFC, 0xB9, 0xDE, 0xF9, 0xF1 }; // 0-F //显示缓冲区 默认为0
uint8_t DispBuf[6] = { 0 };
#ifdef TM1637
void TM1637_Delay(uint16_t us)
{
delay_us(us);
} void TM1637_Start(void)
{
SDA_H(); //拉高数据线
SCL_H(); //拉高时钟线
TM1637_Delay(5); //延时
SDA_L(); //产生下降沿
TM1637_Delay(5); //延时
} void TM1637_Stop(void)
{
SDA_L(); //拉低数据线
SCL_H(); //拉高时钟线
TM1637_Delay(5); //延时
SDA_H(); //拉高数据线
TM1637_Delay(5); //延时
} char TM1637_RecvACK(void)
{
char ack;
SCL_L(); //拉低时钟线
TM1637_Delay(5);
SDA_H(); //端口读之前先置高
SCL_H(); //拉高时钟线
TM1637_Delay(5); ack = SDA_IN(); //读应答信号
TM1637_Delay(5); //延时
SCL_L(); //拉低时钟线
TM1637_Delay(5);
return ack;
} void TM1637_SendByte(uint8_t dat)
{
uint8_t i;
for (i = 0; i < 8; i++) //发送一个字节数据
{
SCL_L(); //拉低时钟线
TM1637_Delay(5); //延时
if ((dat & 0x01) == 0x01) //判断数据最低位
{
SDA_H(); //置高数据线
} else {
SDA_L(); //置低数据线
} dat >>= 1; //移出数据的最低位
TM1637_Delay(5); //延时
SCL_H(); //拉高时钟线
TM1637_Delay(5); //延时
}
SCL_L(); //拉低时钟线
TM1637_Delay(5); //延时 TM1637_RecvACK(); //读取应答
} void TM1637_WriteCMD(uint8_t cmd)
{
TM1637_Start(); //开始
TM1637_SendByte(cmd); //写入命令
TM1637_Stop(); //停止
} void TM1637_WriteReg(uint8_t add, uint8_t dat)
{
TM1637_Start(); //开始
TM1637_SendByte(add); //写入地址
TM1637_SendByte(dat); //写入数据
TM1637_Stop(); //停止
} void TM1637_Clear(void)
{
//写入数据0x00则全部熄灭不显示
TM1637_WriteCMD(DisMode);
TM1637_WriteReg(tm_addr_0, 0x00);
TM1637_WriteReg(tm_addr_1, 0x00);
TM1637_WriteReg(tm_addr_2, 0x00);
TM1637_WriteReg(tm_addr_3, 0x00);
TM1637_WriteReg(tm_addr_4, 0x00);
TM1637_WriteReg(tm_addr_5, 0x00);
} uint8_t tm1637_read_key()
{
uint8_t rekey = 0;
uint8_t i = 0;
TM1637_Start();
TM1637_SendByte(ReadKey);
SDA_H();
for (i = 0; i < 8; i++) {
SCL_L();
TM1637_Delay(10);
rekey = rekey >> 1;
SCL_H();
if (SDA_IN() == 1)
rekey = rekey | 0x80;
else
rekey = rekey | 0x00; TM1637_Delay(10);
}
SCL_L();
TM1637_Delay(2);
while (SDA_IN() == 1)
;
SCL_H();
return rekey;
} #endif
void tm1637_init(void)
{ GPIO_InitTypeDef GPIO_InitStructure;
//延时等待上电稳定
TM1637_Delay(1000); //使能GPIOC的外设时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); //使能GPIO的外设时钟 GPIO_InitStructure.GPIO_Pin = TM_SCL_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(TM_SCL_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = TM_SDA_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(TM_SDA_PORT, &GPIO_InitStructure);
TM1637_WriteCMD(DisMode); //固定地址方式写显存 TM1637_Clear(); //清屏 TM1637_WriteCMD(DisCtr); //显示开,亮度4/16
} void tm1637_key_process(void)
{
if ((key_value_new != 0xFF) && (key_value_old == 0xFF)) { // 按键按下
test_rtt_printf(term_log, "key press down ,key value is %02X \r\n", key_value_new);
} else if ((key_value_new != 0xFF) && (key_value_old == key_value_new)) { // 按键长按 if (key_press_cnt < 9) {
key_press_cnt++; if (0x09 == key_press_cnt) {
test_rtt_printf(term_log, "key long press ,key value is %02X \r\n", key_value_new);
} } else {
if (key_ok == key_value_old) { // ok 键长按
} else if (key_ng == key_value_old) { // ok 键长按
} else if (key_up == key_value_old) { // ok 键长按
} else if (key_down == key_value_old) { // ok 键长按
}
}
} else if ((key_value_new == 0xFF) && (key_value_old != 0xFF)) { // 按键释放
key_press_cnt = 0;
test_rtt_printf(term_log, "key press release\r\n");
tm1637_menu_process(key_value_new);
}
key_value_old = key_value_new;
} void tm1637_menu_process(uint8_t key_value)
{
} void tm1637_process(void)
{
static uint8_t twinkle = 0;
twinkle = (twinkle > 19 ? 0 : (++twinkle));
//写入三位数码管显示数据
if (twinkle < 10) {
TM1637_WriteCMD(DisMode);
TM1637_WriteReg(tm_addr_0, num_code7[8]);
TM1637_WriteReg(tm_addr_1, num_code7[8]);
TM1637_WriteReg(tm_addr_2, num_code7[8]);
TM1637_WriteReg(tm_addr_3, num_code7[8]);
TM1637_WriteReg(tm_addr_4, num_code7[8]);
TM1637_WriteReg(tm_addr_5, num_code7[8]);
} else {
TM1637_Clear(); //清屏
}
key_value_new = tm1637_read_key();
tm1637_key_process();
} void tm1637_show_dec(uint16_t dec)
{
uint8_t n1 = 0;
uint8_t n2 = 0;
uint8_t n3 = 0;
uint8_t n4 = 0;
if (dec < 10) {
n4 = num_code7[dec / 0x001 % 0x0A];
} else if (dec < 100) {
n3 = num_code7[dec / 0x00A % 0x0A];
n4 = num_code7[dec / 0x001 % 0x0A];
} else if (dec < 1000) {
n2 = num_code7[dec / 0x064 % 0x0A];
n3 = num_code7[dec / 0x00A % 0x0A];
n4 = num_code7[dec / 0x001 % 0x0A];
} else if (dec < 10000) {
n1 = num_code7[dec / 0x3E8 % 0x0A];
n2 = num_code7[dec / 0x064 % 0x0A];
n3 = num_code7[dec / 0x00A % 0x0A];
n4 = num_code7[dec / 0x001 % 0x0A];
} else {
n1 = num_code7[0x0F];
n2 = num_code7[0x0F];
n3 = num_code7[0x0F];
n4 = num_code7[0x0F];
}
//写入三位数码管显示数据
TM1637_WriteCMD(DisMode);
TM1637_WriteReg(tm_addr_5, n1);
TM1637_WriteReg(tm_addr_4, n2);
TM1637_WriteReg(tm_addr_3, n3);
TM1637_WriteReg(tm_addr_2, n4);
} void tm1637_cmd_send(uint8_t addr, uint8_t cmd, uint8_t datH, uint8_t datL)
{
com_tx_buf[0] = 0xFF;
com_tx_buf[1] = addr;
com_tx_buf[2] = cmd;
com_tx_buf[3] = datH;
com_tx_buf[4] = datL;
com_tx_buf[5] = 0x00;
com_tx_buf[6] = 0x00;
com_tx_buf[7] = 0x00;
com_tx_buf[8] = get_check_sum(com_tx_buf, sizeof(com_tx_buf));
rbPutData(&rb_usart_tx, com_tx_buf, sizeof(com_tx_buf));
test_rtt_printf(term_log, "a cmd create by tm1637 key,trans to slave now!\r\n");
}

TM1637读取键值调试笔记的更多相关文章

  1. Dictionary读取键值的快捷方法

    对泛型集合Dictionary<T,T> 进行读取键值是经常的操作,一般情况下,都是通过keys 和values进行键值的读取操作: eg: foreach (var item in di ...

  2. 在C#中用Linq从属性文件中读取键值对Key-Value Pair

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:在C#中用Linq从属性文件中读取键值对Key-Value Pair.

  3. 如何读取Linux键值,输入子系统,key,dev/input/event,dev/event,C语言键盘【转】

    转自:https://blog.csdn.net/lanmanck/article/details/8423669 相信各位使用嵌入式的都希望直接读取键值,特别是芯片厂家已经提供input驱动的情况下 ...

  4. 【原】Learning Spark (Python版) 学习笔记(二)----键值对、数据读取与保存、共享特性

    本来应该上周更新的,结果碰上五一,懒癌发作,就推迟了 = =.以后还是要按时完成任务.废话不多说,第四章-第六章主要讲了三个内容:键值对.数据读取与保存与Spark的两个共享特性(累加器和广播变量). ...

  5. JDBC学习笔记(6)——获取自动生成的主键值&处理Blob&数据库事务处理

    获取数据库自动生成的主键 [孤立的技术是没有价值的],我们这里只是为了了解具体的实现步骤:我们在插入数据的时候,经常会需要获取我们插入的这一行数据对应的主键值. 具体的代码实现: /** * 获取数据 ...

  6. 读取当前键值,并赋值给LED

    /********************************* 代码功能:读取当前键值,并赋值给LED 使用函数: digitalRead(数字输入端口号); 创作时间:2016*10*07 作 ...

  7. 【转】JDBC学习笔记(6)——获取自动生成的主键值&处理Blob&数据库事务处理

    转自:http://www.cnblogs.com/ysw-go/ 获取数据库自动生成的主键 我们这里只是为了了解具体的实现步骤:我们在插入数据的时候,经常会需要获取我们插入的这一行数据对应的主键值. ...

  8. Spark学习笔记3:键值对操作

    键值对RDD通常用来进行聚合计算,Spark为包含键值对类型的RDD提供了一些专有的操作.这些RDD被称为pair RDD.pair RDD提供了并行操作各个键或跨节点重新进行数据分组的操作接口. S ...

  9. CockroachDB学习笔记——[译]CockroachDB中的SQL:映射表中数据到键值存储

    CockroachDB学习笔记--[译]CockroachDB中的SQL:映射表中数据到键值存储 原文标题:SQL in CockroachDB: Mapping Table Data to Key- ...

  10. 读取Properties键值对

    public class CommonFunc { /** * 取properties文件中的键值对 */ public static String getProperties(String para ...

随机推荐

  1. Svelte 最新中文文档翻译(1)—— 概述与入门指南

    前言 Svelte,一个非常"有趣".用起来"很爽"的前端框架.从 Svelte 诞生之初,就备受开发者的喜爱,根据统计,从 2019 年到 2024 年,连续 ...

  2. dart中Map类型详解

    Map是什么 map类型的数据都是由key和value两个值组成, key是唯一的,value不必唯一,读写数据都是通过key进行. key和value可以是任意类型数据. Map的基本使用 01== ...

  3. Luogu P2468 SDOI2010 粟粟的书架 题解 [ 紫 ] [ 可持久化线段树 ] [ 二分 ] [ 前缀和 ]

    粟粟的书架:二合一的缝合题. 前一半测试点 此时是 \(200\times 200\) 的二维问题,首先考虑暴力怎么写,显然是每次询问把查的全扔进大根堆里,然后一直取堆顶直到满足要求或者取空. 那么这 ...

  4. CSP 2024 游记

    初赛 Day -1 唐,rp--了. 上午语文正卷满分,然后作文挂完了靠.我没想到我作文能挂到 40pts. 吃饭的时候 gcy 说了什么奇怪的东西,然后喷饭爆金币了,社死现场.吃饭的时候还 tm 咬 ...

  5. AI探索:通过宏脚本给小众编辑器EverEdit插上AI的翅膀!

    1 AI探索:通过宏脚本给小众编辑器EverEdit插上AI的翅膀! 1.1 背景   在AI编程大行其道的背景下,各种AI编程工具:Cursor.VSCode的各种插件.Trae等等搞得不亦乐乎!您 ...

  6. c/c++ 2019公司面试题目录

    原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/12131500.html c++面试题中经常被面试官面试的小问题总结(一)(本篇偏向基础知识) ...

  7. springboot2.1.6整合activiti6.0(二)--网页流程编辑器bpmnjs

    网页流程编辑器bpmnjs 官网:https://bpmn.io/ github:https://github.com/bpmn-io/bpmn-js-examples 因为还需要做一些改造,才能使其 ...

  8. 分布式锁—4.Redisson的联锁和红锁

    大纲 1.Redisson联锁MultiLock概述 2.Redisson联锁MultiLock的加锁与释放锁 3.Redisson红锁RedLock的算法原理 4.Redisson红锁RedLock ...

  9. AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现

    引言 在当今这个数据爆炸的时代,信息的快速存储与高效检索已经成为技术领域的核心挑战.随着人工智能(AI)和机器学习(ML)的迅猛发展,向量存储和相似性搜索技术逐渐崭露头角,成为处理海量数据的利器.对于 ...

  10. 关于我这周学习SQL注入的一些笔记:

    sql注入的原理: 通过恶意的SQL语句插入到应用的输入参数中,再在后台数据库服务器上解析执行的攻击.   Web程序的三层结构: 界面层( User Interface layer ) 业务逻辑层( ...