目录
一、前言
使用STM32F407VET6单片机,沁恒CH455H数码管控制及键盘控制芯片,通过外部中断的方式上报键值信息,并通过串口打印。
二、硬件设计
(1)CH455H
CH455 是数码管显示驱动和键盘扫描控制芯片。CH455 内置时钟振荡电路,可以动态驱动 4 位数码管或者 32 只 LED;同时还可以进行 28 键的键盘扫描;CH455 通过 SCL 和 SDA 组成的 2 线串行接口与单片机等交换数据。
芯片特点:
- 内置显示电流驱动级,段电流不小于 25mA,字电流不小于 160mA。
- 动态显示扫描控制,支持 8×4 或者 7×4,直接驱动 4 位数码管或者 32 只发光管 LED。
- 内置 28 键键盘控制器,基于 7×4 矩阵键盘扫描。
- 高速 2 线串行接口,时钟速度从 0 到 4MHz,兼容两线 I2C 总线,节约引脚。
- 内置上电复位,支持 2.7V~5V 电源电压。
- 提供低电平有效的键盘中断,提供按键释放标志位,可供查询按键按下与释放。
- 内置时钟振荡电路,不需要外部提供时钟或者外接振荡元器件,更抗干扰。
也就是说,CH455H芯片可以同时进行4位8段数码管和7*4矩阵键盘的扫描。这里,仅以4*4矩阵键盘为例,设计电路。DIG0~DIG3为列扫描,SEG0~SEG1为行扫描,INT引脚连接单片机的E0引脚。SCL和SDA通过4.7k电阻上拉。
(2)矩阵键盘
在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式,采用逐行或逐列的“扫描”,就可以读出任何位置按键的状态。这样的行列式键盘结构能够有效地提高单片机系统中I/O口的利用率。节约单片机的资源,其本质和独立按键类似,就是进行逐行扫描和逐列扫描,然后判断是第几行的第几列个按键,进而进行整体按键值得确定。但在使用时,单片机需要定时的逐行逐列扫描按键状态,不仅使用了定时器,而且在程序量较大或对时序要求较高时,定时扫描的方法就比较耗费资源。
图示为4*4矩阵键盘电路图。SEG和DIG分别连接CH455H芯片的SEG和DIG引脚,DIG0~DIG3、H1~H4引脚单独引出。不使用CH455芯片时,可通过单片机扫描DIG和H1~H4引脚进行按键扫描。
(3)STM32F407VET6单片机
单片机部分仅说明引脚连接:SCL连接PB8,SDA连接PB9,INT连接PE0。
(4)原理图和PCB
原理图
PCB
三、程序设计
CH455 的键盘扫描功能支持 7×4 矩阵的 28 键键盘。在键盘扫描期间,DIG3~DIG0 引脚用于列扫描输出,SEG6~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。
写操作包括以下 6 个步骤:输出启动信号、输出字节 1、应答 1、输出字节 2、应答 2、输出停止信号。其中,启动信号和停止信号如上所述,应答 1 和应答 2 总是固定为 1,输出字节 1 和输出字节 2 各自包含 8 个数据位,即一个字节数据。
读操作包括以下 6 个步骤:输出启动信号、输出字节 1、应答 1、输入字节 2、应答 2、输出停止信号。其中,启动信号和停止信号如上所述,应答 1 和应答 2 总是固定为 1,输出字节 1 和输入字节 2 各自包含 8 个数据位,即一个字节数据。
下面,仅给出CH455与main.c程序内容。若有需要整个工程,可直接私信。
(1)CH455.h
#ifndef __CH455_H
#define __CH455_H
#include "stm32f4xx.h" // Device header
extern unsigned char key;
void CH455_Init(void);
void CH455_EXIT_Init(void);
unsigned char CH455H_Config(void);
unsigned char CH455H_Read_Key(void);
void CH455_Wait_Ack(void);
#endif
(2)CH455.c
#include "ch455.h"
#include "mpuiic.h"
#include "board.h"
unsigned char key = 0;
void CH455_Init(void)
{
IIC_Init();
CH455_EXIT_Init();
CH455H_Config();
}
unsigned char CH455H_Config(void)
{
//设置系统参数
IIC_Start();
IIC_Send_Byte(0x48);
CH455_Wait_Ack();//应答
IIC_Send_Byte(0x11); /*[KOFF][INTENS][7SEG][SLEEP]0[ENA]
ENA: 1 使能显示输出和按键扫描
SLEEP: 0 不进入低功耗睡眠状态
7SEG: 0 8段模式
INTENS: 001 显示驱动占空比为1/8
KOFF: 0 键盘扫描和显示驱动交替进行*/
CH455_Wait_Ack();//应答
IIC_Stop();
return 0;
}
void CH455_EXIT_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct ;
EXTI_InitTypeDef EXTI_InitStruct ;
//使能外设时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
//初始化GPIOA引脚1
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN ;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP ;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 ;
GPIO_Init( GPIOE, & GPIO_InitStruct);
/* 将引脚和中断线进行连接*/
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE , EXTI_PinSource0 );
EXTI_InitStruct.EXTI_Line = EXTI_Line0 ;//PA1 对应EXTI_Line1
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt ;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling ;
EXTI_InitStruct.EXTI_LineCmd = ENABLE ;
EXTI_Init(& EXTI_InitStruct) ;
/*配置对应外设中断*/
NVIC_InitTypeDef NVIC_InitStruct ;
NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn ; //外部中断1 通道 IRQn 位于stm32f4xx.h 中196行处
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority =2 ; //抢占优先级 2
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1 ; //响应优先级 2
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ; //使能中断
NVIC_Init(& NVIC_InitStruct);
}
/* 读取键值 */
unsigned char CH455H_Read_Key(void)
{
unsigned char h=0, l=0;
unsigned char res;
IIC_Start();
IIC_Send_Byte(0x4F);
CH455_Wait_Ack(); //应答
res = IIC_Read_Byte(0); //停止应答
IIC_Stop(); //产生一个停止条件
h = (res>>3)&0x7;
l = res&0x3;
res = h*4+(4-l);
return res;
}
/* 等待应答 */
void CH455_Wait_Ack(void)
{
MPU_SDA_IN(); //SDA设置为输入
IIC_SDA=1;
delay_us(1);
IIC_SCL=1;
delay_us(1);
IIC_SCL=0;//时钟输出0
}
/* 按键中断 */
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0) == SET) //检查EXTI_Line1中断标志位
{
key = CH455H_Read_Key();
EXTI_ClearITPendingBit(EXTI_Line0) ; //及时清除 EXTI_Line1 的标志位
}
}
(3) main.c
#include "stm32f4xx.h" // Device header
#include "board.h"
#include "led.h"
#include "key.h"
#include "exit.h"
#include "ch455.h"
#include "usart.h"
int main(void)
{
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );
board_init();
LED_Init();
KEY_Init();
Usart_Config();
CH455_Init();
while(1)
{
printf("%d\r\n", key);
LED1_ON;
delay_ms(500);
LED1_OFF;
delay_ms(500);
}
}