STM32与CH455g通信测试(仅键盘)
1、概述
CH455是数码管显示驱动和键盘扫描控制芯片。CH455内置时钟振荡电路,可以动态驱动4位数码管或者32只LED;同时还可以进行28键的键盘扫描;CH455通过SCL和SDA组成的2线串行接口与单片机等交换数据。

2、特点
●内置显示电流驱动级,段电流不小于25mA,字电流不小于160mA。
●动态显示扫描控制,支持8×4或者7×4,直接驱动4位数码管或者32只发光管LED。
●内部限流,通过占空比设定提供8级亮度控制。
●内置28键键盘控制器,基于7×4矩阵键盘扫描。
●内置按键状态输入的下拉电阻,内置去抖动电路。
●提供低电平有效的键盘中断,提供按键释放标志位,可供查询按键按下与释放。
●高速2线串行接口,时钟速度从0到4MHz,兼容两线I2C总线,节约引脚。
●内置上电复位,支持2.7V~5V电源电压。
●支持低功耗睡眠,节约电能,可以被按键唤醒或者被命令操作唤醒。
●内置时钟振荡电路,不需要外部提供时钟或者外接振荡元器件,更抗干扰。
3、引脚介绍

VCC:电源正电源,持续电流不小于150mA
GND:电源公共接地,持续电流不小于150mA
SEG0 ~SEG6:三态输出及输入数码管的段驱动,高电平有效,键盘扫描输入,高电平有效,内置下拉
SEG7:输出数码管的小数点段驱动输出,高电平有效,7段模式下的键盘中断输出,低电平有效
DIG0 ~DIG3:输出数码管的字驱动,低电平有效,键盘扫描输出,高电平有效
SDA:内置上拉开漏输出及输入2线串行接口的数据输入和输出,内置上拉电阻SCL:输入2线串行接口的数据时钟,内置上拉电阻
4、键盘扫描功能
键盘扫描CH455的键盘扫描功能支持7×4矩阵的28键键盘。在键盘扫描期间,DIG3~DIG0引脚用于列扫描输出,SEG6~SEG0引脚都带有内部下拉电阻,用于行扫描输入。CH455定期在显示驱动扫描过程中插入键盘扫描。在键盘扫描期间,DIG3~DIG0引脚按照DIG0至DIG3的顺序依次输出高电平,其余引脚输出低电平;SEG6~SEG0引脚的输出被禁止,当没有键被按下时,SEG6~SEG0都被下拉为低电平;当有键被按下时,例如连接DIG1与SEG4的键被按下,则当DIG1输出高电平时SEG4检测到高电平;为了防止因为按键抖动或者外界干扰而产生误码,CH455实行两次扫描,只有当两次键盘扫描的结果相同时,按键才会被确认有效。如果CH455检测到有效的按键,则记录下该按键代码,并通过INT#引脚产生低电平有效的键盘中断,此时单片机可以通过串行接口读取按键代码;在没有检测到新的有效按键之前,CH455不再产生任何键盘中断。CH455支持SEG1和SEG0针对同一DIG的组合键,组合键是最优先的,除此之外,如果多个键同时按下,那么按键代码较小的按键优先。例如连接DIG1与SEG1及连接DIG1与SEG0的两个键,可作为组合键。CH455所提供的按键代码为8位,位7始终为0,位2始终为1,位1~位0是列扫描码,位5~位3是行扫描码,位6是状态码(键按下为1,键释放为0)。例如,连接DIG1与SEG4的键被按下,则按键代码是01100101B或者65H,键被释放后,按键代码通常是00100101B或者25H(也可能是其它值,但是肯定小于40H),其中,对应DIG1的列扫描码为01B,对应SEG4的行扫描码为100B。单片机可以在任何时候读取按键代码,但一般在CH455检测到有效按键而产生键盘中断时读取按键代码,此时按键代码的位6总是1,另外,如果需要了解按键何时释放,单片机可以通过查询方式定期读取按键代码,直到按键代码的位6为0。下表是在DIG3~DIG0与SEG6~SEG0之间7×4矩阵的按键编址,也是数码管段位和发光管LED阵列的顺序编址。由于按键代码是8位,键按下时位6总是1,所以当键按下时,CH455所提供的实际按键代码是表中的按键编址加上40H,也就是说,此时的按键代码应该在44H到7FH之间。
| 编址 | DIG3 | DIG2 | DIG1 | DIG0 |
| SEG0 | 07H | 06H | 05H | 04H |
| SEG1 | 0FH | 0EH | 0DH | 0CH |
| SEG2 | 17H | 16H | 15H | 14H |
| SEG3 | 1FH | 1EH | 1DH | 1CH |
| SEG4 | 27H | 26H | 25H | 24H |
| SEG5 | 2FH | 2EH | 2DH | 2CH |
| SEG6 | 37H | 36H | 35H | 34H |
| SEG0+SEG1 | 3FH | 3EH | 3DH | 3CH |
5、串行接口
CH455具有硬件实现的2线串行接口,包含2个主要信号线:串行数据时钟输入线SCL、串行数据输入和输出线SDA;以及1个辅助信号线:中断输出线INT#。其中,SCL是带上拉的输入信号线,CH455中文手册4默认是高电平;SDA是带上拉的准双向信号线,默认是高电平;INT#是带上拉的开漏输出,在启用键盘扫描功能后作为键盘中断输出线,默认是高电平。SDA用于串行数据输入和输出,高电平表示位数据1,低电平表示位数据0,串行数据输入的顺序是高位在前,低位在后。SCL用于提供串行时钟,CH455在其上升沿从SDA输入数据,在其下降沿从SDA输出数据。在SCL为高电平期间发生的SDA下降沿定义为串行接口的启动信号,在SCL为高电平期间发生的SDA上升沿定义为串行接口的停止信号。CH455只在检测到启动信号后才接收并分析命令。所以在单片机I/O引脚资源紧张时,可以在保持SDA引脚状态不变的情况下,将SCL引脚与其它接口电路共用;如果能够确保SDA引脚的变化仅在SCL引脚为低电平期间发生,那么SCL引脚和SDA引脚都可以与其它接口电路共用。INT#用于键盘中断输出,默认是高电平。当CH455检测到有效按键时,INT#输出低电平有效的键盘中断;单片机被中断后,对CH455执行读操作,CH455将INT#恢复为高电平,并从SDA输出按键代码,单片机从SDA获得一个字节的数据,即按键代码。单片机与CH455的通讯过程总是分为6个步骤,按单片机的操作方向分成两种类型,一种是写操作,用于输出数据,一种是读操作,用于输入数据。具体过程可以参考例子程序中的说明。写操作包括以下6个步骤:输出启动信号、输出字节1、应答1、输出字节2、应答2、输出停止信号。其中,启动信号和停止信号如上所述,应答1和应答2总是固定为1,输出字节1和输出字节2各自包含8个数据位,即一个字节数据。读操作包括以下6个步骤:输出启动信号、输出字节1、应答1、输入字节2、应答2、输出停止信号。其中,启动信号和停止信号如上所述,应答1和应答2总是固定为1,输出字节1和输入字节2各自包含8个数据位,即一个字节数据。
6、操作命令
CH455的操作命令分为3组。各命令的启动信号、停止信号、应答1和应答2都相同,区别在于输出字节1和字节2的数据不同以及字节2的传输方向不同。
加载字数据命令:该命令的输出字节1为地址68H、6AH、6CH或者6EH,分别对应于DIG0~DIG3引脚驱动的4个数码管;输出字节2为[DIG_DATA]B,即00H到0FFH之间的值,是8位的字数据。加载字数据命令用于将字数据DIG_DATA写入字节1指定地址的数据寄存器中。例如,命令数据01101100B(即6CH对应DIG2)、01111001B表示将字数据79H写入第1个数据寄存器,使DIG2引脚驱动的数码管将显示E。
读取按键代码命令:该命令的输出字节1为01001111B,即4FH;输入字节2为按键代码。读取按键代码命令用于获得CH455最近检测到的有效按键的按键代码。该命令属于读操作,是唯一的具有数据返回的命令,单片机必须先释放SDA引脚(三态输出禁止或者上拉到高电平),然后CH455从SDA引脚输出按键代码,按键代码的有效数据是位7~位0,其中位6是状态码,位5~位0是扫描码和按键编址。
7、接口时序

TSSTA/THSTA/TSSTO/THSTO/TCLOW/TCHIG:最小时间100ns
TSD:最小时间30ns
THD:最小时间10ns
TAA/TDH:最小时间2ns
Rate:最大速率4Mbps
8、原理图接线

9、程序设计
由于这次只是做个简单的按键检测,所以只用到键盘扫描功能,数码管有需要再做吧。先定义一下系统参数:

// 设置系统参数命令
#define CH455_SCL_SET HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET)
#define CH455_SCL_CLR HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET)
#define CH455_SCL_D_OUT {} // 设置SCL为输出方向,对于双向I/O需切换为输出 #define CH455_SDA_SET HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET)
#define CH455_SDA_CLR HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET)
#define CH455_SDA_IN HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_9) // 读取SDA输入电平 #define CH455_BIT_ENABLE 0x01 // 开启/关闭位
#define CH455_BIT_SLEEP 0x04 // 睡眠控制位
#define CH455_BIT_7SEG 0x08 // 7段控制位
#define CH455_BIT_INTENS1 0x10 // 1级亮度
#define CH455_BIT_INTENS2 0x20 // 2级亮度
#define CH455_BIT_INTENS3 0x30 // 3级亮度
#define CH455_BIT_INTENS4 0x40 // 4级亮度
#define CH455_BIT_INTENS5 0x50 // 5级亮度
#define CH455_BIT_INTENS6 0x60 // 6级亮度
#define CH455_BIT_INTENS7 0x70 // 7级亮度
#define CH455_BIT_INTENS8 0x00 // 8级亮度 #define CH455_SYSOFF 0x0400 // 关闭显示、关闭键盘
#define CH455_SYSON ( CH455_SYSOFF | CH455_BIT_ENABLE ) // 开启显示、键盘
#define CH455_SLEEPOFF CH455_SYSOFF // 关闭睡眠
#define CH455_SLEEPON ( CH455_SYSOFF | CH455_BIT_SLEEP ) // 开启睡眠
#define CH455_7SEG_ON ( CH455_SYSON | CH455_BIT_7SEG ) // 开启七段模式
#define CH455_8SEG_ON ( CH455_SYSON | 0x00 ) // 开启八段模式
#define CH455_SYSON_4 ( CH455_SYSON | CH455_BIT_INTENS4 ) // 开启显示、键盘、4级亮度
#define CH455_SYSON_8 ( CH455_SYSON | CH455_BIT_INTENS8 ) // 开启显示、键盘、8级亮度 // CH455接口定义
#define CH455_I2C_ADDR 0x40 // CH455的地址
#define CH455_I2C_MASK 0x3E // CH455的高字节命令掩码 // 读取按键代码命令
#define CH455_GET_KEY 0x0700 // 获取按键,返回按键代码
define
另外,用的是stm32的HAL库,但是IIC通讯依然用的是IO口模拟IIC的,主要是因为CH455g的器件地址没有找到,其次是HAL库自带的IIC模块不太可靠。

//--------------------------------------------i2c-------------------------------------------
// 设置SDA为输出方向,对于双向I/O需切换为输出
void CH455_SDA_D_OUT()
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } // 设置SDA为输入方向,对于双向I/O需切换为输入
void CH455_SDA_D_IN()
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } void CH455_I2c_Start( void ) // 操作起始
{
//DISABLE_KEY_INTERRUPT; //禁止键盘中断,防止开始时被CH455中断而进入中断服务程序中的START CH455_SDA_SET; /*发送起始条件的数据信号*/
CH455_SDA_D_OUT(); /* 设置SDA为输出方向 */
CH455_SCL_SET;
CH455_SCL_D_OUT; /* 设置SCL为输出方向 */
HAL_Delay(10);
CH455_SDA_CLR; /*发送起始信号*/
HAL_Delay(10);
CH455_SCL_CLR; /*钳住I2C总线,准备发送或接收数据 */
} void CH455_I2c_Stop( void ) // 操作结束
{
CH455_SDA_CLR;
CH455_SDA_D_OUT(); /* 设置SDA为输出方向 */
HAL_Delay(10);
CH455_SCL_SET;
HAL_Delay(10);
CH455_SDA_SET; /*发送I2C总线结束信号*/
HAL_Delay(10);
CH455_SDA_D_IN(); /* 设置SDA为输入方向 */
//ENABLE_KEY_INTERRUPT;
} void CH455_I2c_WrByte( uint8_t dat ) //写一个字节数据
{
uint8_t i;
CH455_SDA_D_OUT(); /* 设置SDA为输出方向 */
for( i = 0; i != 8; i++ ) // 输出8位数据
{
if( dat & 0x80 )
{
CH455_SDA_SET;
}
else
{
CH455_SDA_CLR;
}
HAL_Delay(10);
CH455_SCL_SET;
dat <<= 1;
HAL_Delay(10); // 可选延时
CH455_SCL_CLR;
}
CH455_SDA_D_IN(); /* 设置SDA为输入方向 */
CH455_SDA_SET;
HAL_Delay(10);
CH455_SCL_SET; // 接收应答
HAL_Delay(10);
CH455_SCL_CLR;
} uint8_t CH455_I2c_RdByte( void ) //读一个字节数据
{
uint8_t dat,i;
CH455_SDA_SET;
CH455_SDA_D_IN(); /* 设置SDA为输入方向 */
dat = 0;
for( i = 0; i != 8; i++ ) // 输入8位数据
{
HAL_Delay(10); // 可选延时
CH455_SCL_SET;
HAL_Delay(10); // 可选延时
dat <<= 1;
if( CH455_SDA_IN ) dat++; // 输入1位
CH455_SCL_CLR;
}
CH455_SDA_SET;
HAL_Delay(10);
CH455_SCL_SET; // 发出无效应答
HAL_Delay(10);
CH455_SCL_CLR;
return dat;
} void CH455_Write( uint16_t cmd ) //写命令
{
CH455_I2c_Start(); //启动总线
CH455_I2c_WrByte(((uint8_t)(cmd>>7)&CH455_I2C_MASK)|CH455_I2C_ADDR);
CH455_I2c_WrByte((uint8_t)cmd); //发送数据
CH455_I2c_Stop(); //结束总线
} uint8_t CH455_Read( void ) //读取按键
{
uint8_t keycode;
CH455_I2c_Start(); //启动总线
CH455_I2c_WrByte((uint8_t)(CH455_GET_KEY>>7)&CH455_I2C_MASK|0x01|CH455_I2C_ADDR);
keycode=CH455_I2c_RdByte(); //读取数据
CH455_I2c_Stop(); //结束总线
return keycode;
}
IO口模拟IIC
主函数开启中断和键盘:
HAL_ADC_Start_IT(&hadc1);
CH455_Write( CH455_7SEG_ON );// 开启显示和键盘,8段显示方式
写完回调函数,即可打印输出键码:
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle)
{
RX_AD = HAL_ADC_GetValue(&hadc1);
if(RX_AD == 0)
{
RX_CH455 = CH455_Read();
printf("键码:%x\r\n",RX_CH455);
}
}
完了给大伙看看按键打印结果:

需要程序的记得留言打赏哦
STM32与CH455g通信测试(仅键盘)的更多相关文章
- STM32 实现 4*4 矩阵键盘扫描(HAL库、标准库 都适用)
本文实现的代码是基于STM32HAL库的基础上的,不过标准库也可以用,只是调用的库函数不同,逻辑跟配置是一样的,按我这里的逻辑来配置即可. 1.键盘原理图: 原理举例:先把 F0-F7 内部拉高,这样 ...
- Tkinter 鼠标键盘事件(二)
一个Tkinter主要跑在mainloop进程里.Events可能来自多个地方,比如按键,鼠标,或是系统事件. Tkinter提供了丰富的方法来处理这些事件.对于每一个控件Widget,你都可以为其绑 ...
- python tkinter教程-事件绑定
一个Tkinter主要跑在mainloop进程里.Events可能来自多个地方,比如按键,鼠标,或是系统事件. Tkinter提供了丰富的方法来处理这些事件.对于每一个控件Widget,你都可以为其绑 ...
- [FreeRTOS入门] 1.CubeMX中FreeRTOS配置参数及理解
1.有关优先级 1.1 Configuration --> FreeRTOS MAX_PRIORITIES 设置任务优先级的数量:配置应用程序有效的优先级数目.任何数量的任务都可以共享一个优先级 ...
- python tkinter中的事件绑定
一个Tkinter主要跑在mainloop进程里.Events可能来自多个地方,比如按键,鼠标,或是系统事件. Tkinter提供了丰富的方法来处理这些事件.对于每一个控件Widget,你都可以为其绑 ...
- 【STM32学习笔记】STM32f407 使用4*4矩阵键盘
作者:李剀 出处:https://www.cnblogs.com/kevin-nancy/ 欢迎转载,但也请保留上面这段声明.谢谢! 写在前面: 这是本人第一次开始写博客,可能写的不是很好,也请大家谅 ...
- ARM开发(3)基于STM32的矩阵键盘控制蜂鸣器
一 矩阵键盘控制蜂鸣器原理: 1.1 本实验实现8*7矩阵键盘上按键控制蜂鸣器响. 1.2 实验思路:根据电路图原理,找出矩阵键盘行列所对应的引脚,赋予对应的按键值,然后控制蜂鸣器响. 1.3 ...
- stm32 hid 键盘描述
/* USB Standard Device Descriptor */ const uint8_t Joystick_DeviceDescriptor[JOYSTICK_SIZ_DEVICE_DES ...
- stm32矩阵键盘扫描数据通过USB发送
Keyboard.c #include "keyboard.h"#include "my_usb.h"#include " ...
随机推荐
- 华为云服务器安装hadoop2.7.5
1. 安装环境 1.1硬件环境 1.1.1 NameNode 配置项 详细参数 主机 k8s-master CPU Intel(R) Xeon(R) Gold 6278C CPU @ 2.60GHz ...
- 【原创】探索云计算容器底层之Cgroup
一.什么是Cgroup,使用场景? 容器本质上是进程,既然是进程就会消耗掉系统资源,比如:CPU.内存.磁盘.网络带宽等,如果不加以限制,容器在某些情况下就会无限制地吃掉宿主机的系统资源,显然这不是我 ...
- 力扣Leetcode 21. 合并两个有序链表
合并两个有序链表 将两个升序链表合并为一个新的升序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例: 输入:1->2->4, 1->3->4 输出:1-> ...
- TextBox控件保存上次的输入
本片文章是参考C# 怎么让winform程序中的输入文本框保留上次的输入再此表示感谢重新在这里写一遍,是为了保存一下,方便自己下次使用可以很快的找到1.设置txtBox控件的配置文件2.选择Text ...
- 【Gin-API系列】Gin中间件之异常处理(六)
本文我们介绍生产环境上如何通过捕捉异常recovery来完善程序设计和提高用户体验. Golang异常处理 golang 的异常处理比较简单,通常都是在程序遇到异常崩溃panic之后通过defer调用 ...
- Queries for Number of Palindromes(区间dp)
You've got a string s = s1s2... s|s| of length |s|, consisting of lowercase English letters. There a ...
- 剑指 Offer 53 - I. 在排序数组中查找数字 I
题目描述 统计一个数字在排序数组中出现的次数. 示例1: 输入: nums = [5,7,7,8,8,10], target = 8 输出: 2 示例2: 输入: nums = [5,7,7,8,8, ...
- Oracle的dbms_random.value(min,max)函数包括边界值吗?数据是如何分布的?
事先申明下,我的DB环境是Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production,不保证在其它版本下也 ...
- Java反射库介绍
本文主要罗列了Java反射中使用比较多的一些方法,主要都是来自 java.lang.reflect包下的Field.Method 和 Constructor 等三个类,希望对大家有帮助!! 介绍 ja ...
- postman -- 环境变量、全局变量使用
背景: [登录接口]中会返回sign值,[学生金币充值接口]会则需要用到该sign值,因此把sign设置为环境或全局变量,便于其他接口调用. 1.请求登录接口,获取sign值: 2.把sign值添加至 ...