首页主机资讯[LINUX应用编程]GPIO控制TM1650键盘

[LINUX应用编程]GPIO控制TM1650键盘

时间2023-07-06 07:15:02发布访客分类主机资讯浏览1556
导读:LINUX外接TM1650键盘,由于TM1650的接口不是标准的I2C接口,只能通过操作GPIO方式模拟I2C通信,实现对TM1650的驱动;问题1:通过linux的文件读写GPIO方式,是否支持微秒级别拉高拉低控制,通过示波器验证没有问题...

LINUX外接TM1650键盘,由于TM1650的接口不是标准的I2C接口,只能通过操作GPIO方式模拟I2C通信,实现对TM1650的驱动;

问题1:通过linux的文件读写GPIO方式,是否支持微秒级别拉高拉低控制,通过示波器验证没有问题,完全支持微秒级别的gpio控制;

问题2:中断引脚如何控制;

当前解决方式是轮训查询中断引脚value值来判断是否有中断;另一种方式就是使用poll监听多路复用的方式监听是否有中断产生;

对于使用中断可以使用poll多路复用IO监测中断是否触发,poll事件有POLLIN、POLLOUT、POLLERR、POLLPRI 等,其中 POLLIN 和 POLLOUT 表示普通优先级数据可读、可写。中断就是一种高优先级事件,需要使用 POLLPRI ,当中断触发时表示有高优先级数据可被读取。
此外还可以使用信号方式监测GPIO是否触发中断。

TM1650的驱动代码参考链接实现,基于参考链接来操作GPIO,注意这个链接的几个方法没有close 文件句柄,需要注意修改;

key_pad.h

//key_pad.h

#ifndef KEY_PAD_
#define KEY_PAD_

#include stdio.h>
    
#include stdint.h>
    
#include stdlib.h>



/*************************************宏定义********************/
/*
J8707	键盘	1	SCK		144
		2	SDA		145
		3	INT	输入中断	194
		4	Light	背光控制,IO,无驱动能力	196

*/
#define TM1650_SCK_GPIO  144
#define TM1650_SDA_GPIO  145
#define TM1650_IRQ_GPIO  194

/***********************键盘丝印值定义*****************************/
typedef enum KEY_VALUE_{

	INPUT_KEY_INVALID=-1,
	INPUT_KEY_0 = 0,
	INPUT_KEY_1,
	INPUT_KEY_2,
	INPUT_KEY_3,
	INPUT_KEY_4,
	INPUT_KEY_5,
	INPUT_KEY_6,
	INPUT_KEY_7,
	INPUT_KEY_8,
	INPUT_KEY_9,
	INPUT_KEY_STAR = 20,
	INPUT_KEY_PROUND = 21,
	INPUT_KEY_PAGE_UP = 22,
	INPUT_KEY_PAGE_DOWN = 23,
	INPUT_KEY_BACK = 24,
	INPUT_KEY_OK = 25,
}
    KEY_VALUE;
    

/*************************************函数定义********************/		
extern void    TM1650_init(void);
    
extern uint8_t TM1650_Irq_Set(void);
    
extern void i2c_read_data(uint8_t *key_value);
    

//将按键值转换为键盘丝印值 
extern int get_input_key_value(uint8_t key_value);
    
#endif//

key_pad.c

//key_pad.c
#include stdio.h>
    
#include stdlib.h>
    
#include sys/time.h>
    
#include sys/types.h>
    
#include sys/select.h>
    
#include unistd.h>
    
#include fcntl.h>
      

#include "key_pad.h" 
#include "gpio.h"

/*************************************函数定义********************/

static inline int  gpio_input_bit_get(int gpio, uint8_t switch_in_mode);
     
static inline void gpio_bit_set_mode(int gpio, uint8_t in_mode);
    

static void TM1650_IIC_start(void);
    
static void TM1650_IIC_write_byte(uint8_t dat);
    
static uint8_t TM1650_IIC_wait_ack(void);
    
static void TM1650_IIC_stop(void);
    
static uint8_t TM1650_IIC_read_byte(void);



#define TRUE 1
#define FALSE 0

//========【配置IIC总线的信号读写和时序】=======
//主机拉高SCL
#define TM1650_IIC_SCL_HIGH     gpio_bit_set(TM1650_SCK_GPIO)

//主机拉低SCL
#define TM1650_IIC_SCL_LOW      gpio_bit_reset(TM1650_SCK_GPIO)


//输入
#define TM1650_IIC_SDA_SET_IN     gpio_bit_set_mode(TM1650_SDA_GPIO, TRUE)
#define TM1650_IIC_SDA_SET_OUT     gpio_bit_set_mode(TM1650_SDA_GPIO, FALSE)

//主机拉高SDA
#define TM1650_IIC_SDA_HIGH     gpio_bit_set(TM1650_SDA_GPIO)

//主机拉低SDA
#define TM1650_IIC_SDA_LOW      gpio_bit_reset(TM1650_SDA_GPIO)

//参数b为0时主机拉低SDA,非0则拉高SDA
#define TM1650_IIC_SDA_WR(b)    do{
                                           \
                               if(b) gpio_bit_set(TM1650_SDA_GPIO);
       \
                               else  gpio_bit_reset(TM1650_SDA_GPIO);
 \
                              }
while(0)


//主机读取SDA线电平状态,返回值为0为低电平,非0则为高电平
#define TM1650_IIC_SDA_RD()    gpio_input_bit_get(TM1650_SDA_GPIO, FALSE) 

//软件延时2us
#define TM1650_IIC_DELAY_2US   do{
    for(int ii_=0;
    ii_22;
    ii_++);
}
while(0)

//软件延时4us
#define TM1650_IIC_DELAY_4US   do{
    sleep_inner(0, 4);
}
while(0)
#define TM1650_IIC_DELAY_5US   do{
    sleep_inner(0, 5);
}
while(0)

/*************************************函数实现********************/

 
 
void sleep_inner (long sec, long usec) {

 
  struct timeval timeout = {
sec, usec}
    ;
    
  int ret = 0;

 
  if ((0 == timeout.tv_sec) || (timeout.tv_usec  20))
  {
    
    //printf("local sleep_inner error! input sleep_inner time must greater than 20ms !\n");
    
    //timeout.tv_usec = 20;

  }
    
 
  ret = select(0, NULL, NULL, NULL, &
    timeout);

 
  if ((-1 == ret) || (ret))
  {
    
    printf("local sleep_inner error!\n");

  }

 
}


void i2c_read_data(uint8_t *key_value){
       
	TM1650_IIC_start();
    
	TM1650_IIC_write_byte(0x49);
      //起始地址 0x49
	TM1650_IIC_wait_ack();
     
	*key_value = TM1650_IIC_read_byte();
       
	TM1650_IIC_stop();
    
    printf("%s exit test \r\n",__FUNCTION__);
 
}


void i2c_write_data(uint8_t address, uint8_t data){
     
	TM1650_IIC_start();
    
	TM1650_IIC_write_byte(address);
     TM1650_IIC_wait_ack();
      //显存起始地址为0x68
	TM1650_IIC_write_byte(data);
     TM1650_IIC_wait_ack();
        //发送段码
	TM1650_IIC_stop();
 
}



static inline void key_gpio_init(int gpio){
    
	gpio_unexport(gpio);
    
	#if 1
	char cmd[255] = "";
    
	sprintf(cmd, "echo %d >
     /sys/class/gpio/export", gpio);
    
	system(cmd);
    
	#else
	gpio_export(gpio);

	#endif
}

 
static int tm1650_init_inner()
{
    
   TM1650_IIC_start();
    
   TM1650_IIC_write_byte(0x48);
    
   TM1650_IIC_wait_ack();
    
   TM1650_IIC_write_byte(0x08|0x00|0x01);
        //0x08:7段模式;0x00:正常工作模式;0x01:开屏(开启键扫描)
   TM1650_IIC_wait_ack();
    
   TM1650_IIC_stop();
    
   return 0;

}


//TM1650初始化
void TM1650_init(void)
{
     
	key_gpio_init(TM1650_SCK_GPIO);
    
	key_gpio_init(TM1650_SDA_GPIO);
    
	key_gpio_init(TM1650_IRQ_GPIO);
    


	gpio_direction_input(TM1650_IRQ_GPIO);
    
	//gpio_edge_falling(TM1650_IRQ_GPIO);
    

	gpio_direction_output(TM1650_SCK_GPIO);
    
	gpio_direction_output(TM1650_SDA_GPIO);
    

    sleep_inner(0,5000);
    // 

	tm1650_init_inner();
    
    sleep_inner(0,5000);
    // 
	printf("TM1650_init end\r\n");

}


uint8_t TM1650_Irq_Set(void){
    
	int ret = gpio_get_value(TM1650_IRQ_GPIO);

	if (ret == 0){
    
		return TRUE;

	}
    
	return FALSE;

}


static inline void gpio_bit_set_inner(int gpio, uint8_t high){
    
	#if 0
	char cmd[255] = "";
    
	sprintf(cmd, "echo out >
     /sys/class/gpio/gpio%d/direction", gpio);
    
	//printf("cmd1:%s\r\n", cmd);
    
	system(cmd);
     
	sprintf(cmd, "echo %d >
     /sys/class/gpio/gpio%d/value", high?1:0, gpio);
    
	//printf("cmd2:%s\r\n", cmd);
    
	system(cmd);
      
	#else
	gpio_set_value(gpio, high);

	#endif
}


void gpio_bit_set(int gpio){
    
	gpio_bit_set_inner(gpio, 1);

}


void gpio_bit_reset(int gpio){
    
	gpio_bit_set_inner(gpio, 0);

}


static inline void gpio_bit_set_mode(int gpio, uint8_t in_mode){
    
	#if 0
    char cmd[255] = "";
 
	if (in_mode){
     
		sprintf(cmd, "echo in >
     /sys/class/gpio/gpio%d/direction", gpio);
    
		system(cmd);
  
	}
else{
     
		sprintf(cmd, "echo out >
     /sys/class/gpio/gpio%d/direction", gpio);
    
		system(cmd);
  
	}
    
	//printf("gpio_bit_set_mode, cmd:%s\r\n", cmd);

	#else
	if (in_mode){
    
		gpio_direction_input(gpio);

	}
else{
    
		gpio_direction_output(gpio);

	}

	#endif
}


static inline int gpio_input_bit_get(int gpio, uint8_t switch_in_mode){

#if 1
	if (switch_in_mode){
    
		gpio_bit_set_mode(gpio, 1);

	}
    
	return gpio_get_value(gpio);
    
#else
	FILE *fp = NULL;
     
    int rc = 0;
     // 用于接收命令返回值 
	int value  =0;
    
	char cmd[255] = "";
    
    char result_buf[255] = "";
    
	sprintf(cmd, "echo in >
     /sys/class/gpio/gpio%d/direction", gpio);

	if (switch_in_mode){
    
		//printf("gpio_input_bit_get:%s\r\n", cmd);
    
		system(cmd);
   
	}
    
	sprintf(cmd, "cat /sys/class/gpio/gpio%d/value", gpio);
    
    fp = popen(cmd, "r");
 
    if(NULL == fp) 
    {
     
    	printf("popen执行失败!");
     
		return (0);
 
    }
  
    while(fgets(result_buf, sizeof(result_buf), fp) != NULL) 
    {
     
		value = atoi(result_buf);

	}
    
	rc = pclose(fp);

    if(-1 == rc) 
    {
     
        printf("关闭文件指针失败\r\n");
     
        return (0);
 
    }
     
	return value;

#endif
}


//产生IIC总线起始信号
static void TM1650_IIC_start(void)
{
    
	TM1650_IIC_SDA_SET_OUT;
    
	TM1650_IIC_SCL_HIGH;
         //SCL=1
	TM1650_IIC_SDA_HIGH;
        //SDA=1
	TM1650_IIC_DELAY_5US;
    
	TM1650_IIC_SDA_LOW;
         //SDA=0
	TM1650_IIC_DELAY_5US;
    
	TM1650_IIC_SCL_LOW;
      //SCL=0
}


 
//通过IIC总线发送一个字节
static void TM1650_IIC_write_byte(uint8_t dat)
{
    
	uint8_t i;
    
	TM1650_IIC_SDA_SET_OUT;
    
	
	TM1650_IIC_SCL_LOW;
    
	for(i=0;
    i8;
i++)
	{
    
		TM1650_IIC_SDA_WR(dat&
    0x80);
    
		dat=1;
    	
		
		TM1650_IIC_DELAY_5US;
    
		TM1650_IIC_SCL_HIGH;
    
		TM1650_IIC_DELAY_5US;
    
		TM1650_IIC_SCL_LOW;
    
		TM1650_IIC_DELAY_5US;

	}

}

 
//通过IIC总线读一个字节
static uint8_t TM1650_IIC_read_byte(void)
{
    
	uint8_t i;
     
	uint8_t read_key = 0;
    

    //printf("%s enter test \r\n",__FUNCTION__);
    
	TM1650_IIC_SDA_SET_IN;
    

	for(i=0;
    i8;
i++)
	{
    
		TM1650_IIC_SCL_LOW;
    
		TM1650_IIC_DELAY_5US;
    
		TM1650_IIC_SCL_HIGH;
    
		read_key =1;
    	
		int value = gpio_input_bit_get(TM1650_SDA_GPIO, FALSE);

		if (value == 1){
    
			read_key++;

		}
    
		TM1650_IIC_DELAY_5US;
 
	}
    
	TM1650_IIC_SDA_SET_OUT;
    
    //printf("%s exit test \r\n",__FUNCTION__);
    
	return read_key;

}


//通过IIC总线接收从机响应的ACK信号
static uint8_t TM1650_IIC_wait_ack(void)
{
    
	uint8_t ack_signal = 0;
    
	int times_wait = 0;
    
	
	TM1650_IIC_SDA_SET_OUT;
    
	TM1650_IIC_SDA_HIGH;
        //SDA=1
	TM1650_IIC_DELAY_5US;
    
	TM1650_IIC_SCL_HIGH;
    
	TM1650_IIC_DELAY_5US;
    
	TM1650_IIC_SDA_SET_IN;

	do{

		if(TM1650_IIC_SDA_RD()) {
    
			ack_signal = 1;
       //如果读取到的是NACK信号
			break;

		}
 
	}
    while(times_wait++  300);
    
	TM1650_IIC_SCL_LOW;
    
	TM1650_IIC_DELAY_2US;
    
	return ack_signal;

}

 
 
//产生IIC总线结束信号
static void TM1650_IIC_stop(void)
{
    
	TM1650_IIC_SDA_SET_OUT;
    
	TM1650_IIC_SCL_LOW;
          //SCL=0
	TM1650_IIC_SDA_LOW;
          //SDA=0
	TM1650_IIC_DELAY_5US;
    
	TM1650_IIC_SCL_HIGH;
         //SCL=1
	TM1650_IIC_DELAY_5US;
    
	TM1650_IIC_SDA_HIGH;
    //SDA=1
}

 

//将按键值转换为键盘丝印值 
int get_input_key_value(uint8_t key_value){

	switch(key_value){
    
		case 0x64:
		return INPUT_KEY_OK;
    
		case 0x65:
		return INPUT_KEY_PROUND;
    
		case 0x66:
		return INPUT_KEY_0;
    
		case 0x67:
		return INPUT_KEY_STAR;
    


		case 0x4C:
		return INPUT_KEY_BACK;
    
		case 0x4D:
		return INPUT_KEY_9;
    
		case 0x4E:
		return INPUT_KEY_8;
    
		case 0x4F:
		return INPUT_KEY_7;
    

		case 0x44:
		return INPUT_KEY_INVALID;
    
		case 0x45:
		return INPUT_KEY_INVALID;
    
		case 0x46:
		return INPUT_KEY_INVALID;
    
		case 0x47:
		return INPUT_KEY_INVALID;
    


		default:
		return INPUT_KEY_INVALID;

	}
    
	return INPUT_KEY_INVALID;

}
    

有中断信号时,读键盘的按键值,主函数实现:

    TM1650_init();
    
 
    sleep_ms(20);

    while(1){
  
	    if (TM1650_Irq_Set())
		{
    
		    i2c_read_data(&
    input_key);
     
		    printf("input_key:%02x:%d\r\n", input_key, get_input_key_value(input_key));

		}
    
	    sleep_ms(10);
 
    }
    

几个报错处理:

1、开始通过文件方式操作export文件,发现总是失败;

[failed]gpio_export path:/sys/class/gpio/gpio145

open error: Permission denied

[failed]gpio_export path:/sys/class/gpio/gpio194

open error: Permission denied

[failed]gpio_cfg_attr path:/sys/class/gpio/gpio145/value

open error: No such file or directory

解决:使用如下命令执行后,再操作gpio就没有问题:

system("echo 199 > /sys/class/gpio/export");

2、sh: write error: Device or resource busy

未做处理,应该是权限控制问题;

3、write error: Operation not permited.

问题原因:direction是in时,写value失败

root@fsu:/home/fsu# echo 199 > /sys/class/gpio/export

root@fsu:/home/fsu# echo in > /sys/class/gpio/gpio199/direction

root@fsu:/home/fsu# echo 1 > /sys/class/gpio/gpio199/value

ash: write error: Operation not permitted

root@fsu:/home/fsu#

4、too many open file

文件打开没有close,网上找的gpio文件操作的代码(参考链接)有问题,操作完文件没有close; 

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!

linuxgpiokey编程主机

若转载请注明出处: [LINUX应用编程]GPIO控制TM1650键盘
本文地址: https://pptw.com/jishu/291447.html
美国云服务器带宽经常被占满如何解决? 静默内网存活探测

游客 回复需填写必要信息