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



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

呱牛笔记


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

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

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

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


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

key_pad.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
//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;i<8;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;i<8;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;
}


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

1
2
3
4
5
6
7
8
9
10
11
    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; 


-------------------广告线---------------
项目、合作,欢迎勾搭,邮箱:promall@qq.com


本文为呱牛笔记原创文章,转载无需和我联系,但请注明来自呱牛笔记 ,it3q.com

请先登录后发表评论
  • 最新评论
  • 总共0条评论