[LINUX应用编程]GPIO控制TM1650键盘
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核实处理,我们将尽快回复您,谢谢合作!
若转载请注明出处: [LINUX应用编程]GPIO控制TM1650键盘
本文地址: https://pptw.com/jishu/291447.html