求图中IO口扩展的程序!
最近在做一个项目,发现单片机的IO口不够用,然后得知如上图可以在节省IO口的情况下进行IO口扩展完成矩阵键盘。
在此求大神关于此扩展程序,不甚感激啊!
HELP!
不知道74LS164好不好用·?
云里雾里
代码
解析图片链接
禁用链接识别
禁用表情
禁用编辑器代码
使用个人签名
发送动态
看看看
zhichixia!
看看
谢谢你的点拨,顿时豁然开朗起来!希望有机会能再次获得你的帮助!谢谢
k看看
这个就是先选中第一个74L 在开始扫描 再选中第二个74L 再扫描
这个算是在做4*4键盘中最省的一种方法了,如果你有更好的方法,希望你能共享下哈!
你可以做矩阵键盘啊,以前看到过一个很节约io口的矩阵键盘。
这个程序的目标是每发送一个时钟(P1.0)后,立即检查一次P1.2的状态;
这里要注意两点:
1、由于74LS164不能清零,所以扫描检查之前,必须先发送16个零脉冲,即P1.1保持为0时连发16个时钟信号;
2、扫描过程中,第一个时钟使P1.1为1,然后的15个时钟P1.1应为零,同时CPU内部应该有时钟计数器,该计数值是与按键号相对应的,这样才能知道扫描到的是哪一个键的状态。
/*****************************************************
* main.c 中如此这般:
*****************************************************/
kd_init();
// ......
while (1)
{
if( should_update_kd )
{
kd_update();
}
// Other code
// ......
}
再看显示、键扫源代码:
/******************************************************
* key_disp-config.h
******************************************************/
#ifndef _KEY_DISP_CFG_H_
#define _KEY_DISP_CFG_H_
#define DIGIT1 B, 0
#define DIGIT2 B, 1
#define DIGIT3 B, 2
#define DIGIT4 B, 3
#define KEY_FB D, 6
#define KD_CLR D, 7
#define KD_CLK B, 5
#define KD_DAT B, 4
#define KEY_NONE (uint8_t)(0xFF)
#define KEY_S1 (uint8_t)(0x01<<0)
#define KEY_S2 (uint8_t)(0x01<<1)
#define KEY_S3 (uint8_t)(0x01<<2)
#define KEY_S4 (uint8_t)(0x01<<3)
#define KEY_S5 (uint8_t)(0x01<<4)
#define KEY_S6 (uint8_t)(0x01<<5)
#define KEY_S7 (uint8_t)(0x01<<6)
#define KEY_S8 (uint8_t)(0x01<<7)
#endif /*_KEY_DISP_CFG_H_*/
/******************************************************
* key_disp.h
******************************************************/
#ifndef _KEY_DISP_H_
#define _KEY_DISP_H_
#include <inttypes.h>
#include "key_disp-config.h"
#define KD_CODE_NONE 10
#define KD_CODE_PAUSED 11
#define KD_CODE_CW 12
#define KD_CODE_CCW 13
#define KD_CODE_SET_RUN 14
#define KD_CODE_SET_SLEEP 15
#define KD_CODE_TIMER_RUN 16
#define KD_CODE_TIMER_SLEEP 17
#define KD_CODE_EXTERN_TRIG 18
#define KD_CODE_EXTERN_CTRL 19
#define KD_CODE_H 20
#define KD_CODE_M 21
#define KD_CODE_S 22
// Initialize key & display
void kd_init();
// Update key & display, MUST be called periodically, eg., in timer
void kd_update();
// Get key code
uint8_t kd_get_key();
// Set mode to display
void kd_display_code(uint8_t digit_id, uint8_t code_id);
// Set display digits, dp_pos=-1 means no dp displayed
void kd_display(uint16_t value, uint8_t max_digits, const int8_t dp_pos);
#endif /*_KEY_DISP_H_*/
/******************************************************
* key_disp.c
******************************************************/
#include "avr/io.h"
#include "key_disp.h"
#include "config.h"
#include "util.h"
#define NOP() asm volatile ("nop")
static const uint8_t seg_code[] =
{
0x3F/*0*/, 0x06/*1*/, 0x5B/*2*/, 0x4F/*3*/, 0x66/*4*/,
0x6D/*5*/, 0x7D/*6*/, 0x07/*7*/, 0x7F/*8*/, 0x6F/*9*/,
0x00/*KD_CODE_NONE*/,
0x73/*KD_CODE_PAUSED*/,
0x21/*KD_CODE_CW*/,
0x03/*KD_CODE_CCW*/,
0x50/*KD_CODE_SET_RUN*/,
0x6D/*KD_CODE_SET_SLEEP*/,
0x09/*KD_CODE_TIMER_RUN*/,
0x36/*KD_CODE_TIMER_SLEEP*/,
0x79/*KD_CODE_EXTERN_TRIG*/,
0x39/*KD_CODE_EXTERN_CTRL*/,
0x76/*KD_CODE_H*/,
0x20/*KD_CODE_M*/,
0x22/*KD_CODE_S*/,
};
#define SEG_DP 0x80
static volatile uint8_t _key_code = 0xFF;
static volatile uint8_t _digits[4];
void kd_init()
{
PORT_DDR_SET(DIGIT1);
PORT_DDR_SET(DIGIT2);
PORT_DDR_SET(DIGIT3);
PORT_DDR_SET(DIGIT4);
PORT_DDR_CLR(KEY_FB); // Input
PORT_DDR_SET(KD_CLR);
PORT_PIN_CLR(DIGIT1);
PORT_PIN_CLR(DIGIT2);
PORT_PIN_CLR(DIGIT3);
PORT_PIN_CLR(DIGIT4);
PORT_PIN_SET(KEY_FB); // Internal pull-up
PORT_PIN_SET(KD_CLR);
_digits[0] = _digits[1] = _digits[2] = _digits[3] = 0;
}
/* Takes about 50 us @ 8MHz */
void kd_update()
{
static uint8_t turn = 0;
uint8_t i;
if( turn++ & 0x01 )
return;
// Disable all digits first
PORT_PIN_CLR(DIGIT1);
PORT_PIN_CLR(DIGIT2);
PORT_PIN_CLR(DIGIT3);
PORT_PIN_CLR(DIGIT4);
if( turn++ & 0x02 )
{
//
// trun for key scan
//
uint8_t shift_data;
static uint8_t last_scan_code = 0;
static uint8_t last_code_count = 0;
//
// Scan key
PORT_PIN_CLR(KD_CLK);
PORT_PIN_CLR(KD_CLR);
PORT_PIN_SET(KD_CLR);
//
// All output 1
shift_data = 0xFF;
PORT_PIN_SET(KD_DAT);
while( shift_data )
{
// Pulse out
PORT_PIN_SET(KD_CLK);
PORT_PIN_CLR(KD_CLK);
shift_data >>= 1;
}
shift_data = 0x01;
while( shift_data )
{
if( (~shift_data) & 0x01 )
PORT_PIN_SET(KD_DAT);
else
PORT_PIN_CLR(KD_DAT);
// Pulse out
PORT_PIN_SET(KD_CLK);
PORT_PIN_CLR(KD_CLK);
// Delay
for( i=0; i<16; i++ )
NOP();
// Check feedback
if( PORT_PIN_VALUE(KEY_FB) == 0 )
{
if( last_scan_code == shift_data )
{
// Same as last scan result, that's the key!
if( last_code_count > 4 )
_key_code = shift_data;
if( last_code_count < 255 )
last_code_count++;
}
else
{
last_scan_code = shift_data;
last_code_count = 1;
_key_code = KEY_NONE;
}
break;
}
shift_data <<= 1;
}
if( shift_data == 0 )
{
_key_code = KEY_NONE;
last_scan_code = KEY_NONE;
last_code_count = 1;
}
}
else
{
//
// Turn for display
//
static uint8_t curr_digit = 0;
uint8_t curr_code = 0;
//
// Display digits
PORT_PIN_CLR(KD_CLK);
PORT_PIN_CLR(KD_CLR);
PORT_PIN_SET(KD_CLR);
curr_code = _digits[curr_digit];
for( i=0; i<8; i++ )
{
// MSB first
if( curr_code & 0x80 )
PORT_PIN_SET(KD_DAT);
else
PORT_PIN_CLR(KD_DAT);
curr_code <<= 1;
// Pulse out
PORT_PIN_SET(KD_CLK);
PORT_PIN_CLR(KD_CLK);
}
switch( curr_digit ) // 位控制pin可能不连续,所以不能够用移位之类的
{
case 0:
PORT_PIN_SET(DIGIT4);
break;
case 1:
PORT_PIN_SET(DIGIT3);
break;
case 2:
PORT_PIN_SET(DIGIT2);
break;
case 3:
PORT_PIN_SET(DIGIT1);
break;
}
// For next trun
curr_digit++;
curr_digit %= 4;
}
}
uint8_t kd_get_key()
{
return _key_code;
}
void kd_display_code(uint8_t digit_id, uint8_t code_id)
{
_digits[digit_id] = seg_code[code_id];
}
void kd_display(uint16_t value, uint8_t max_digits, const int8_t dp_pos/*=-1*/)
{
//
// Prepare seg code for LED
_digits[0] = seg_code[value % 10];
value /= 10;
_digits[1] = seg_code[value % 10];
if(max_digits > 2)
{
value /= 10;
_digits[2] = seg_code[value % 10];
if(max_digits > 3)
{
value /= 10;
_digits[3] = seg_code[value % 10];
}
}
if( dp_pos >=0 && dp_pos<3 )
_digits[dp_pos] |= SEG_DP;
}
坐等大神,万分期盼,不甚感激!