Add files via upload
This commit is contained in:
1 parent
6b89e01b0c
commit
1fa683922f
3 files changed
+1008
No files matched your search
@@ -0,0 +1,9 @@
|
||||
obj-m += driver.o
|
||||
|
||||
KDIR := /home/ubuntu/linux
|
||||
|
||||
all:
|
||||
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -C $(KDIR) M=$(PWD) modules
|
||||
|
||||
clean:
|
||||
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -C $(KDIR) M=$(PWD) clean
|
||||
+404
@@ -0,0 +1,404 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <time.h>
|
||||
#include <linux/i2c-dev.h>
|
||||
|
||||
#define CLOCK_DEV "/dev/clock_drv"
|
||||
|
||||
|
||||
#define I2C_DEV "/dev/i2c-1"
|
||||
#define OLED_I2C_ADDR 0x3C
|
||||
|
||||
#define OLED_W 128
|
||||
#define OLED_H 64
|
||||
static uint8_t fb[OLED_W * OLED_H / 8];
|
||||
|
||||
static int i2c_fd = -1;
|
||||
|
||||
static long long now_ms(void) {
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (long long)ts.tv_sec * 1000LL + ts.tv_nsec / 1000000LL;
|
||||
}
|
||||
|
||||
static int iround_div(long long sum, int cnt) {
|
||||
if (cnt <= 0) return -1;
|
||||
if (sum >= 0) return (int)((sum + cnt/2) / cnt);
|
||||
return (int)((sum - cnt/2) / cnt);
|
||||
}
|
||||
|
||||
|
||||
static int i2c_open_oled(void) {
|
||||
i2c_fd = open(I2C_DEV, O_RDWR);
|
||||
if (i2c_fd < 0) { perror("open i2c"); return -1; }
|
||||
if (ioctl(i2c_fd, I2C_SLAVE, OLED_I2C_ADDR) < 0) {
|
||||
perror("ioctl I2C_SLAVE");
|
||||
close(i2c_fd);
|
||||
i2c_fd = -1;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void oled_cmd(uint8_t c) {
|
||||
uint8_t buf[2] = {0x00, c};
|
||||
(void)write(i2c_fd, buf, 2);
|
||||
}
|
||||
|
||||
static void oled_data_chunk(const uint8_t *d, size_t n) {
|
||||
uint8_t buf[1 + 16];
|
||||
while (n > 0) {
|
||||
size_t m = (n > 16) ? 16 : n;
|
||||
buf[0] = 0x40;
|
||||
memcpy(&buf[1], d, m);
|
||||
(void)write(i2c_fd, buf, 1 + m);
|
||||
d += m;
|
||||
n -= m;
|
||||
}
|
||||
}
|
||||
|
||||
static void oled_init(void) {
|
||||
oled_cmd(0xAE);
|
||||
oled_cmd(0xD5); oled_cmd(0x80);
|
||||
oled_cmd(0xA8); oled_cmd(0x3F);
|
||||
oled_cmd(0xD3); oled_cmd(0x00);
|
||||
oled_cmd(0x40);
|
||||
oled_cmd(0x8D); oled_cmd(0x14);
|
||||
oled_cmd(0x20); oled_cmd(0x00);
|
||||
oled_cmd(0xA1);
|
||||
oled_cmd(0xC8);
|
||||
oled_cmd(0xDA); oled_cmd(0x12);
|
||||
oled_cmd(0x81); oled_cmd(0xCF);
|
||||
oled_cmd(0xD9); oled_cmd(0xF1);
|
||||
oled_cmd(0xDB); oled_cmd(0x40);
|
||||
oled_cmd(0xA4);
|
||||
oled_cmd(0xA6);
|
||||
oled_cmd(0xAF);
|
||||
}
|
||||
|
||||
static void oled_flush(void) {
|
||||
oled_cmd(0x21); oled_cmd(0); oled_cmd(127);
|
||||
oled_cmd(0x22); oled_cmd(0); oled_cmd(7);
|
||||
oled_data_chunk(fb, sizeof(fb));
|
||||
}
|
||||
|
||||
|
||||
static void fb_clear(void) { memset(fb, 0, sizeof(fb)); }
|
||||
|
||||
static void fb_set_px(int x, int y, int on) {
|
||||
if (x<0||x>=OLED_W||y<0||y>=OLED_H) return;
|
||||
int idx = (y/8)*OLED_W + x;
|
||||
uint8_t mask = (1u << (y%8));
|
||||
if (on) fb[idx] |= mask;
|
||||
else fb[idx] &= ~mask;
|
||||
}
|
||||
|
||||
static const uint8_t font5x7_digits[][5] = {
|
||||
{0x3E,0x51,0x49,0x45,0x3E},{0x00,0x42,0x7F,0x40,0x00},
|
||||
{0x42,0x61,0x51,0x49,0x46},{0x21,0x41,0x45,0x4B,0x31},
|
||||
{0x18,0x14,0x12,0x7F,0x10},{0x27,0x45,0x45,0x45,0x39},
|
||||
{0x3C,0x4A,0x49,0x49,0x30},{0x01,0x71,0x09,0x05,0x03},
|
||||
{0x36,0x49,0x49,0x49,0x36},{0x06,0x49,0x49,0x29,0x1E}
|
||||
};
|
||||
static const uint8_t font_colon[5] = {0x00,0x36,0x36,0x00,0x00};
|
||||
|
||||
static const uint8_t font_E[5]={0x7F,0x49,0x49,0x49,0x41};
|
||||
static const uint8_t font_D[5]={0x7F,0x41,0x41,0x22,0x1C};
|
||||
static const uint8_t font_I[5]={0x00,0x41,0x7F,0x41,0x00};
|
||||
static const uint8_t font_T[5]={0x01,0x01,0x7F,0x01,0x01};
|
||||
static const uint8_t font_R[5]={0x7F,0x09,0x19,0x29,0x46};
|
||||
static const uint8_t font_U[5]={0x3F,0x40,0x40,0x40,0x3F};
|
||||
static const uint8_t font_N[5]={0x7F,0x06,0x18,0x60,0x7F};
|
||||
static const uint8_t font_W[5]={0x7F,0x20,0x18,0x20,0x7F};
|
||||
static const uint8_t font_H[5]={0x7F,0x08,0x08,0x08,0x7F};
|
||||
static const uint8_t font_M[5]={0x7F,0x02,0x04,0x02,0x7F};
|
||||
static const uint8_t font_S[5]={0x46,0x49,0x49,0x49,0x31};
|
||||
static const uint8_t font_G[5]={0x3E,0x41,0x41,0x51,0x32};
|
||||
static const uint8_t font_P[5]={0x7F,0x09,0x09,0x09,0x06};
|
||||
static const uint8_t font_A[5]={0x7E,0x09,0x09,0x09,0x7E};
|
||||
static const uint8_t font_B[5]={0x7F,0x49,0x49,0x49,0x36};
|
||||
|
||||
|
||||
|
||||
|
||||
static const uint8_t font_a[5]={0x20,0x54,0x54,0x54,0x78};
|
||||
static const uint8_t font_c[5]={0x38,0x44,0x44,0x44,0x20};
|
||||
static const uint8_t font_d[5]={0x38,0x44,0x44,0x48,0x7F};
|
||||
static const uint8_t font_e[5]={0x38,0x54,0x54,0x54,0x18};
|
||||
static const uint8_t font_g[5]={0x18,0xA4,0xA4,0xA4,0x7C};
|
||||
static const uint8_t font_h[5]={0x7F,0x08,0x04,0x04,0x78};
|
||||
static const uint8_t font_i[5]={0x00,0x44,0x7D,0x40,0x00};
|
||||
static const uint8_t font_l[5]={0x00,0x41,0x7F,0x40,0x00};
|
||||
static const uint8_t font_m[5]={0x7C,0x04,0x18,0x04,0x78};
|
||||
static const uint8_t font_n[5]={0x7C,0x08,0x04,0x04,0x78};
|
||||
static const uint8_t font_o[5]={0x38,0x44,0x44,0x44,0x38};
|
||||
static const uint8_t font_p[5]={0x7C,0x14,0x14,0x14,0x08};
|
||||
static const uint8_t font_r[5]={0x7C,0x08,0x04,0x04,0x08};
|
||||
static const uint8_t font_s[5]={0x48,0x54,0x54,0x54,0x20};
|
||||
static const uint8_t font_t[5]={0x04,0x3F,0x44,0x40,0x20};
|
||||
static const uint8_t font_u[5]={0x3C,0x40,0x40,0x20,0x7C};
|
||||
static const uint8_t font_w[5]={0x3C,0x40,0x30,0x40,0x3C};
|
||||
static const uint8_t font_y[5]={0x0C,0x50,0x50,0x50,0x3C};
|
||||
static const uint8_t font_space[5] = {0,0,0,0,0};
|
||||
|
||||
static const uint8_t font_percent[5] = {0x23, 0x13, 0x08, 0x64, 0x62};
|
||||
static const uint8_t font_deg[5] = {0x06, 0x09, 0x09, 0x06, 0x00};
|
||||
static const uint8_t font_dot[5] = {0x00,0x60,0x60,0x00,0x00};
|
||||
|
||||
|
||||
static const uint8_t icon_good[8] = { 0x3C,0x42,0xA5,0x81,0xA5,0x99,0x42,0x3C };
|
||||
static const uint8_t icon_Mild[8] = { 0x3C,0x42,0xA5,0x81,0xBD,0x81,0x42,0x3C };
|
||||
static const uint8_t icon_bad[8] = { 0x3C,0x42,0xA5,0x81,0x99,0xA5,0x42,0x3C };
|
||||
static const uint8_t icon_hot[8] = { 0x3C,0x42,0xA5,0x81,0x9D,0xA1,0x42,0x3C };
|
||||
|
||||
static const uint8_t* glyph_for_char(char c) {
|
||||
if (c>='0' && c<='9') return font5x7_digits[c-'0'];
|
||||
if (c==':') return font_colon;
|
||||
|
||||
if (c=='E') return font_E; if (c=='D') return font_D; if (c=='I') return font_I;
|
||||
if (c=='T') return font_T; if (c=='R') return font_R; if (c=='U') return font_U;
|
||||
if (c=='N') return font_N; if (c=='W') return font_W; if (c=='H') return font_H;
|
||||
if (c=='M') return font_M; if (c=='S') return font_S; if (c=='G') return font_G;
|
||||
if (c=='P') return font_P; if (c=='A') return font_A; if (c=='B') return font_B;
|
||||
|
||||
|
||||
if (c=='a') return font_a; if (c=='c') return font_c; if (c=='d') return font_d;
|
||||
if (c=='e') return font_e; if (c=='g') return font_g; if (c=='h') return font_h;
|
||||
if (c=='i') return font_i; if (c=='l') return font_l; if (c=='m') return font_m;
|
||||
if (c=='n') return font_n; if (c=='o') return font_o; if (c=='p') return font_p;
|
||||
if (c=='r') return font_r; if (c=='s') return font_s; if (c=='t') return font_t;
|
||||
if (c=='u') return font_u; if (c=='w') return font_w; if (c=='y') return font_y;
|
||||
if (c=='%') return font_percent; if (c==0xB0) return font_deg; if (c=='.') return font_dot;
|
||||
if (c==' ') return font_space;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void fb_draw_char5x7(int x, int y, char c, int scale) {
|
||||
const uint8_t *g = glyph_for_char(c);
|
||||
if (!g) return;
|
||||
for (int i=0;i<5;i++) {
|
||||
for (int j=0;j<7;j++) {
|
||||
if ((g[i] >> j) & 1) {
|
||||
for (int sx=0;sx<scale;sx++)
|
||||
for (int sy=0;sy<scale;sy++)
|
||||
fb_set_px(x+i*scale+sx, y+j*scale+sy, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fb_draw_text(int x, int y, const char *s, int scale, int spacing) {
|
||||
while (*s) {
|
||||
fb_draw_char5x7(x, y, *s++, scale);
|
||||
x += 5*scale + spacing;
|
||||
}
|
||||
}
|
||||
|
||||
static int di_to_led(int di)
|
||||
{
|
||||
if (di < 65) return 0;
|
||||
if (di > 80) return 8;
|
||||
|
||||
return 1 + (di - 65) * 7 / 15;
|
||||
}
|
||||
|
||||
static void set_led_level(int level)
|
||||
{
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "LED %d", level);
|
||||
|
||||
int fd = open(CLOCK_DEV, O_WRONLY);
|
||||
if (fd >= 0) {
|
||||
write(fd, buf, strlen(buf));
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
static void fb_draw_icon8(int x, int y, const uint8_t icon[8], int scale) {
|
||||
for (int r = 0; r < 8; r++) {
|
||||
for (int c = 0; c < 8; c++) {
|
||||
if (icon[r] & (1 << c)) {
|
||||
for (int sx = 0; sx < scale; sx++) {
|
||||
for (int sy = 0; sy < scale; sy++) {
|
||||
fb_set_px(x + c*scale + sx, y + r*scale + sy, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fb_draw_circle(int cx,int cy,int r,int filled){
|
||||
for(int y=-r;y<=r;y++){
|
||||
for(int x=-r;x<=r;x++){
|
||||
int d=x*x+y*y;
|
||||
int rr=r*r;
|
||||
if(filled){ if(d<=rr) fb_set_px(cx+x,cy+y,1); }
|
||||
else{ if(d>=rr-r && d<=rr+r) fb_set_px(cx+x,cy+y,1); }
|
||||
}
|
||||
}
|
||||
}
|
||||
static void draw_page_dots(int page){
|
||||
int cy=60,r=3;
|
||||
int cx1=OLED_W/2-15;
|
||||
int cx2=OLED_W/2;
|
||||
int cx3=OLED_W/2+15;
|
||||
fb_draw_circle(cx1,cy,r,0);
|
||||
fb_draw_circle(cx2,cy,r,0);
|
||||
fb_draw_circle(cx3,cy,r,0);
|
||||
if(page==0) fb_draw_circle(cx1,cy,r-1,1);
|
||||
else if(page==1) fb_draw_circle(cx2,cy,r-1,1);
|
||||
else fb_draw_circle(cx3,cy,r-1,1);
|
||||
}
|
||||
|
||||
static int read_clock_line(char *out, size_t outsz) {
|
||||
int fd = open(CLOCK_DEV, O_RDONLY);
|
||||
if (fd < 0) return -1;
|
||||
int n = read(fd, out, outsz-1);
|
||||
close(fd);
|
||||
if (n <= 0) return -1;
|
||||
out[n] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
if (i2c_open_oled() != 0) return 1;
|
||||
oled_init();
|
||||
|
||||
int blink = 0;
|
||||
int prev_page = 0;
|
||||
|
||||
long long last_dht_ms = 0;
|
||||
int cur_temp = -1;
|
||||
int cur_hum = -1;
|
||||
|
||||
while (1) {
|
||||
char line[200];
|
||||
char mode[8]="RUN";
|
||||
char field[8]="SEC";
|
||||
int hh=0,mm=0,ss=0,page=0;
|
||||
int temp=-1,hum=-1;
|
||||
|
||||
if (read_clock_line(line,sizeof(line))==0) {
|
||||
sscanf(line,"%d:%d:%d MODE=%7s FIELD=%7s PAGE=%d TEMP=%d HUM=%d",
|
||||
&hh,&mm,&ss,mode,field,&page,&temp,&hum);
|
||||
}
|
||||
|
||||
long long now = now_ms();
|
||||
|
||||
|
||||
if ((page == 1 || page == 2) && (now - last_dht_ms >= 2000)) {
|
||||
if (temp >= 0 && hum >= 0) {
|
||||
cur_temp = temp;
|
||||
cur_hum = hum;
|
||||
}
|
||||
last_dht_ms = now;
|
||||
}
|
||||
|
||||
fb_clear();
|
||||
draw_page_dots(page);
|
||||
|
||||
|
||||
if (page==0) {
|
||||
if (strcmp(mode,"EDIT")==0)
|
||||
fb_draw_text(0,0,"EDIT",1,1);
|
||||
else
|
||||
fb_draw_text(0,0,"RUN",1,1);
|
||||
|
||||
if (strcmp(mode,"EDIT")==0) {
|
||||
char hs[3], ms[3], ss_s[3];
|
||||
sprintf(hs,"%02d",hh);
|
||||
sprintf(ms,"%02d",mm);
|
||||
sprintf(ss_s,"%02d",ss);
|
||||
|
||||
if (!(strcmp(field,"HOUR")==0 && blink)) fb_draw_text(10,18,hs,2,2);
|
||||
fb_draw_text(34,18,":",2,2);
|
||||
if (!(strcmp(field,"MIN")==0 && blink)) fb_draw_text(46,18,ms,2,2);
|
||||
fb_draw_text(70,18,":",2,2);
|
||||
if (!(strcmp(field,"SEC")==0 && blink)) fb_draw_text(82,18,ss_s,2,2);
|
||||
} else {
|
||||
char ts[16];
|
||||
sprintf(ts,"%02d:%02d:%02d",hh,mm,ss);
|
||||
fb_draw_text(10,18,ts,2,2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
else if (page==1) {
|
||||
const int num_x=92;
|
||||
|
||||
fb_draw_text(0,0,"Today Weather",1,1);
|
||||
fb_draw_text(0,20,"Temp:",1,1);
|
||||
fb_draw_text(0,38,"Humidity:",1,1);
|
||||
|
||||
char tstr[8], hstr[8];
|
||||
|
||||
if (cur_temp >= 0) sprintf(tstr,"%02d",cur_temp);
|
||||
else strcpy(tstr,"--");
|
||||
|
||||
if (cur_hum >= 0) sprintf(hstr,"%02d",cur_hum);
|
||||
else strcpy(hstr,"--");
|
||||
|
||||
fb_draw_text(num_x,16,tstr,2,2);
|
||||
fb_draw_text(num_x+22,16,"\xB0",1,1);
|
||||
fb_draw_text(num_x+30,16,"C",1,1);
|
||||
|
||||
fb_draw_text(num_x,34,hstr,2,2);
|
||||
fb_draw_text(num_x+24,34,"%",1,1);
|
||||
}
|
||||
|
||||
|
||||
else {
|
||||
int di=-1;
|
||||
if (cur_temp >= 0 && cur_hum >= 0)
|
||||
di = (int)(0.81*cur_temp + 0.01*cur_hum*(0.99*cur_temp-14.3) + 46.3);
|
||||
|
||||
fb_draw_text(0,0,"DI PAGE",1,1);
|
||||
|
||||
|
||||
char di_str[8];
|
||||
if (di>=0) sprintf(di_str,"%02d",di);
|
||||
else strcpy(di_str,"--");
|
||||
|
||||
fb_draw_text(0,20,"DI:",2,2);
|
||||
fb_draw_text(40,20,di_str,2,2);
|
||||
|
||||
if (di >= 0) {
|
||||
int led = di_to_led(di);
|
||||
set_led_level(led);
|
||||
}
|
||||
|
||||
if (di < 0) {
|
||||
fb_draw_text(40,14,"--",2,2);
|
||||
fb_draw_text(0,36,"No Data",2,2);
|
||||
}
|
||||
else if (di < 68) {
|
||||
fb_draw_icon8(90,20,icon_good,2);
|
||||
fb_draw_text(0,36,"Good",2,2);
|
||||
}
|
||||
else if (di < 75) {
|
||||
fb_draw_icon8(90,20,icon_Mild,2);
|
||||
fb_draw_text(0,36,"Mild",2,2);
|
||||
}
|
||||
else if (di < 80) {
|
||||
fb_draw_icon8(90,20,icon_bad,2);
|
||||
fb_draw_text(0,36,"Bad",2,2);
|
||||
}
|
||||
else {
|
||||
fb_draw_icon8(90,20,icon_hot,2);
|
||||
fb_draw_text(0,36,"Hot",2,2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
oled_flush();
|
||||
blink = !blink;
|
||||
prev_page = page;
|
||||
usleep(200000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,595 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/time64.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define DRIVER_NAME "clock_drv"
|
||||
#define CLASS_NAME "clock_drv_class"
|
||||
|
||||
|
||||
#define DS_RST 17
|
||||
#define DS_DAT 4
|
||||
#define DS_SCLK 22
|
||||
|
||||
#define ENC_S1 24
|
||||
#define ENC_S2 25
|
||||
#define ENC_SW 26
|
||||
|
||||
#define DHT_GPIO 23
|
||||
|
||||
|
||||
#define LED0 5
|
||||
#define LED1 6
|
||||
#define LED2 12
|
||||
#define LED3 13
|
||||
#define LED4 16
|
||||
#define LED5 19
|
||||
#define LED6 20
|
||||
#define LED7 21
|
||||
|
||||
static int leds[8] = {
|
||||
LED0, LED1, LED2, LED3,
|
||||
LED4, LED5, LED6, LED7
|
||||
};
|
||||
|
||||
#define DEBOUNCE_MS 6
|
||||
#define LONGPRESS_MS 1000
|
||||
#define PAGE_DEBOUNCE_MS 200
|
||||
|
||||
#define DHT_CACHE_MS 2000
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("kkk");
|
||||
MODULE_DESCRIPTION("DS1302 + rotary/button + DHT11 via /dev/clock_drv");
|
||||
|
||||
struct rtc_simple {
|
||||
int hh, mm, ss;
|
||||
int ch;
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(lock0);
|
||||
|
||||
static struct rtc_simple cur;
|
||||
static struct rtc_simple edit;
|
||||
static bool edit_mode = false;
|
||||
static int edit_field = 2;
|
||||
|
||||
static int ui_page = 0;
|
||||
static unsigned long last_page_switch_j = 0;
|
||||
|
||||
static int irq_s1, irq_sw;
|
||||
static unsigned long last_irq_s1, last_irq_sw;
|
||||
static unsigned long sw_pressed_jiffies;
|
||||
|
||||
static dev_t devno;
|
||||
static struct cdev cdev0;
|
||||
static struct class *cls;
|
||||
|
||||
static int dht_temp = -1;
|
||||
static int dht_hum = -1;
|
||||
static unsigned long last_dht_j = 0;
|
||||
|
||||
|
||||
static inline void ds_clk_pulse(void)
|
||||
{
|
||||
gpio_set_value(DS_SCLK, 1); udelay(2);
|
||||
gpio_set_value(DS_SCLK, 0); udelay(2);
|
||||
}
|
||||
|
||||
static void ds_write_byte(u8 b)
|
||||
{
|
||||
int i;
|
||||
gpio_direction_output(DS_DAT, 0);
|
||||
for (i = 0; i < 8; i++) {
|
||||
gpio_set_value(DS_DAT, (b >> i) & 1);
|
||||
ds_clk_pulse();
|
||||
}
|
||||
}
|
||||
|
||||
static u8 ds_read_byte(void)
|
||||
{
|
||||
int i;
|
||||
u8 v = 0;
|
||||
|
||||
gpio_direction_input(DS_DAT);
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (gpio_get_value(DS_DAT)) v |= (1 << i);
|
||||
ds_clk_pulse();
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline int bcd2int(u8 b) { return (b & 0x0F) + ((b >> 4) & 0x0F) * 10; }
|
||||
static inline u8 int2bcd(int v){ return (u8)(((v/10) << 4) | (v%10)); }
|
||||
|
||||
static void ds1302_read_time(struct rtc_simple *t)
|
||||
{
|
||||
u8 sec, min, hour;
|
||||
|
||||
gpio_set_value(DS_SCLK, 0);
|
||||
gpio_set_value(DS_RST, 1);
|
||||
udelay(4);
|
||||
|
||||
ds_write_byte(0xBF);
|
||||
sec = ds_read_byte();
|
||||
min = ds_read_byte();
|
||||
hour = ds_read_byte();
|
||||
|
||||
ds_read_byte(); ds_read_byte(); ds_read_byte(); ds_read_byte(); ds_read_byte();
|
||||
|
||||
gpio_set_value(DS_RST, 0);
|
||||
|
||||
t->ch = (sec >> 7) & 1;
|
||||
t->ss = bcd2int(sec & 0x7F);
|
||||
t->mm = bcd2int(min);
|
||||
t->hh = bcd2int(hour & 0x3F);
|
||||
}
|
||||
|
||||
static void ds1302_write_reg(u8 cmd, u8 data)
|
||||
{
|
||||
gpio_set_value(DS_SCLK, 0);
|
||||
gpio_set_value(DS_RST, 1);
|
||||
udelay(4);
|
||||
|
||||
ds_write_byte(cmd);
|
||||
ds_write_byte(data);
|
||||
|
||||
gpio_set_value(DS_RST, 0);
|
||||
}
|
||||
|
||||
static void ds1302_set_time(const struct rtc_simple *t)
|
||||
{
|
||||
ds1302_write_reg(0x8E, 0x00);
|
||||
ds1302_write_reg(0x80, int2bcd(t->ss) & 0x7F);
|
||||
ds1302_write_reg(0x82, int2bcd(t->mm));
|
||||
ds1302_write_reg(0x84, int2bcd(t->hh));
|
||||
ds1302_write_reg(0x8E, 0x80);
|
||||
}
|
||||
|
||||
static void clamp_time(struct rtc_simple *t)
|
||||
{
|
||||
if (t->ss < 0) t->ss = 59;
|
||||
if (t->ss > 59) t->ss = 0;
|
||||
|
||||
if (t->mm < 0) t->mm = 59;
|
||||
if (t->mm > 59) t->mm = 0;
|
||||
|
||||
if (t->hh < 0) t->hh = 23;
|
||||
if (t->hh > 23) t->hh = 0;
|
||||
}
|
||||
|
||||
static const char *field_name(int f)
|
||||
{
|
||||
if (f == 2) return "HOUR";
|
||||
if (f == 1) return "MIN";
|
||||
return "SEC";
|
||||
}
|
||||
|
||||
|
||||
static int dht_wait_level(int level, int timeout_us)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < timeout_us; i++) {
|
||||
if (gpio_get_value(DHT_GPIO) == level)
|
||||
return i;
|
||||
udelay(1);
|
||||
}
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int dht_measure_high_us(int timeout_us)
|
||||
{
|
||||
int i = 0;
|
||||
while (gpio_get_value(DHT_GPIO) == 1) {
|
||||
if (i++ >= timeout_us) return timeout_us;
|
||||
udelay(1);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static int dht11_read_once(int *out_temp, int *out_hum)
|
||||
{
|
||||
u8 data[5] = {0};
|
||||
int bit, byte;
|
||||
unsigned long flags;
|
||||
|
||||
gpio_direction_output(DHT_GPIO, 0);
|
||||
msleep(20);
|
||||
gpio_set_value(DHT_GPIO, 1);
|
||||
udelay(40);
|
||||
gpio_direction_input(DHT_GPIO);
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
if (dht_wait_level(0, 100) < 0) goto err;
|
||||
if (dht_wait_level(1, 100) < 0) goto err;
|
||||
if (dht_wait_level(0, 100) < 0) goto err;
|
||||
|
||||
for (bit = 0; bit < 40; bit++) {
|
||||
int high_us;
|
||||
|
||||
if (dht_wait_level(1, 70) < 0) goto err;
|
||||
|
||||
high_us = dht_measure_high_us(100);
|
||||
|
||||
byte = bit / 8;
|
||||
data[byte] <<= 1;
|
||||
data[byte] |= (high_us > 40) ? 1 : 0;
|
||||
|
||||
(void)dht_wait_level(0, 70);
|
||||
}
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
||||
if (((data[0] + data[1] + data[2] + data[3]) & 0xFF) != data[4])
|
||||
return -EIO;
|
||||
|
||||
*out_hum = data[0];
|
||||
*out_temp = data[2];
|
||||
return 0;
|
||||
|
||||
err:
|
||||
local_irq_restore(flags);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static void dht11_get_cached(int *out_temp, int *out_hum)
|
||||
{
|
||||
unsigned long now = jiffies;
|
||||
|
||||
if (time_after(now, last_dht_j + msecs_to_jiffies(DHT_CACHE_MS)) || last_dht_j == 0) {
|
||||
int t, h;
|
||||
int ret = dht11_read_once(&t, &h);
|
||||
if (ret == 0) {
|
||||
dht_temp = t;
|
||||
dht_hum = h;
|
||||
}
|
||||
last_dht_j = now;
|
||||
}
|
||||
|
||||
*out_temp = dht_temp;
|
||||
*out_hum = dht_hum;
|
||||
}
|
||||
|
||||
static void apply_delta_locked(int delta)
|
||||
{
|
||||
if (!edit_mode) return;
|
||||
if (ui_page != 0) return;
|
||||
|
||||
if (edit_field == 0) edit.ss += delta;
|
||||
else if (edit_field == 1) edit.mm += delta;
|
||||
else edit.hh += delta;
|
||||
|
||||
clamp_time(&edit);
|
||||
}
|
||||
|
||||
static void short_press_locked(void)
|
||||
{
|
||||
if (!edit_mode) return;
|
||||
if (ui_page != 0) return;
|
||||
|
||||
edit_field = (edit_field == 0) ? 2 : (edit_field - 1);
|
||||
}
|
||||
|
||||
static void long_press_action(void)
|
||||
{
|
||||
struct rtc_simple t;
|
||||
|
||||
mutex_lock(&lock0);
|
||||
|
||||
if (ui_page != 0) {
|
||||
mutex_unlock(&lock0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!edit_mode) {
|
||||
ds1302_read_time(&cur);
|
||||
edit = cur;
|
||||
edit_mode = true;
|
||||
edit_field = 2;
|
||||
mutex_unlock(&lock0);
|
||||
return;
|
||||
}
|
||||
|
||||
t = edit;
|
||||
edit_mode = false;
|
||||
mutex_unlock(&lock0);
|
||||
|
||||
ds1302_set_time(&t);
|
||||
}
|
||||
|
||||
static void page_toggle_if_cw_locked(void)
|
||||
{
|
||||
unsigned long now = jiffies;
|
||||
|
||||
if (edit_mode) return;
|
||||
|
||||
if (time_before(now, last_page_switch_j + msecs_to_jiffies(PAGE_DEBOUNCE_MS)))
|
||||
return;
|
||||
|
||||
if (gpio_get_value(ENC_S2)) {
|
||||
|
||||
ui_page = (ui_page + 1) % 3;
|
||||
} else {
|
||||
ui_page = (ui_page + 3 - 1) % 3;
|
||||
}
|
||||
|
||||
last_page_switch_j = now;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static irqreturn_t s1_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
unsigned long now = jiffies;
|
||||
|
||||
if (time_before(now, last_irq_s1 + msecs_to_jiffies(DEBOUNCE_MS)))
|
||||
return IRQ_HANDLED;
|
||||
last_irq_s1 = now;
|
||||
|
||||
mutex_lock(&lock0);
|
||||
|
||||
if (!edit_mode) {
|
||||
page_toggle_if_cw_locked();
|
||||
} else {
|
||||
if (gpio_get_value(ENC_S2))
|
||||
apply_delta_locked(+1);
|
||||
else
|
||||
apply_delta_locked(-1);
|
||||
}
|
||||
|
||||
mutex_unlock(&lock0);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t sw_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
unsigned long now = jiffies;
|
||||
|
||||
if (time_before(now, last_irq_sw + msecs_to_jiffies(DEBOUNCE_MS)))
|
||||
return IRQ_HANDLED;
|
||||
last_irq_sw = now;
|
||||
|
||||
if (gpio_get_value(ENC_SW) == 0) {
|
||||
sw_pressed_jiffies = now;
|
||||
} else {
|
||||
unsigned long held_ms = jiffies_to_msecs(now - sw_pressed_jiffies);
|
||||
if (held_ms >= LONGPRESS_MS) {
|
||||
long_press_action();
|
||||
} else {
|
||||
mutex_lock(&lock0);
|
||||
short_press_locked();
|
||||
mutex_unlock(&lock0);
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void set_led_level(int level)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (level < 0) level = 0;
|
||||
if (level > 8) level = 8;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
gpio_set_value(leds[i], (i < level) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ssize_t dev_read(struct file *f, char __user *ubuf, size_t cnt, loff_t *ppos)
|
||||
{
|
||||
char kbuf[160];
|
||||
int len;
|
||||
struct rtc_simple t;
|
||||
bool mode;
|
||||
int field, page;
|
||||
int temp, hum;
|
||||
|
||||
if (*ppos > 0) return 0;
|
||||
|
||||
ds1302_read_time(&cur);
|
||||
|
||||
mutex_lock(&lock0);
|
||||
mode = edit_mode;
|
||||
field = edit_field;
|
||||
page = ui_page;
|
||||
t = (mode && page==0) ? edit : cur;
|
||||
mutex_unlock(&lock0);
|
||||
|
||||
dht11_get_cached(&temp, &hum);
|
||||
|
||||
len = snprintf(kbuf, sizeof(kbuf),
|
||||
"%02d:%02d:%02d MODE=%s FIELD=%s PAGE=%d TEMP=%d HUM=%d\n",
|
||||
t.hh, t.mm, t.ss,
|
||||
(mode && page==0) ? "EDIT" : "RUN",
|
||||
field_name(field),
|
||||
page,
|
||||
temp, hum);
|
||||
|
||||
if (len > cnt) len = cnt;
|
||||
if (copy_to_user(ubuf, kbuf, len)) return -EFAULT;
|
||||
|
||||
*ppos += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t dev_write(struct file *f, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
char kbuf[64];
|
||||
int hh, mm, ss;
|
||||
int level;
|
||||
|
||||
if (cnt >= sizeof(kbuf))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(kbuf, ubuf, cnt))
|
||||
return -EFAULT;
|
||||
|
||||
kbuf[cnt] = '\0';
|
||||
|
||||
|
||||
if (sscanf(kbuf, "LED %d", &level) == 1) {
|
||||
set_led_level(level);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
|
||||
if (sscanf(kbuf, "SET %d:%d:%d", &hh, &mm, &ss) == 3) {
|
||||
struct rtc_simple t = {
|
||||
.hh = hh,
|
||||
.mm = mm,
|
||||
.ss = ss,
|
||||
.ch = 0
|
||||
};
|
||||
|
||||
clamp_time(&t);
|
||||
ds1302_set_time(&t);
|
||||
|
||||
mutex_lock(&lock0);
|
||||
edit_mode = false;
|
||||
mutex_unlock(&lock0);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct file_operations fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = dev_read,
|
||||
.write = dev_write,
|
||||
};
|
||||
|
||||
static int __init mod_init(void)
|
||||
{
|
||||
int ret;
|
||||
struct timespec64 ts;
|
||||
struct rtc_time tm_val;
|
||||
|
||||
printk(KERN_INFO "==== %s init ====\n", DRIVER_NAME);
|
||||
|
||||
ret = alloc_chrdev_region(&devno, 0, 1, DRIVER_NAME);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
gpio_request(leds[i], "led");
|
||||
gpio_direction_output(leds[i], 0);
|
||||
}
|
||||
|
||||
cdev_init(&cdev0, &fops);
|
||||
ret = cdev_add(&cdev0, devno, 1);
|
||||
if (ret < 0) goto err_chr;
|
||||
|
||||
cls = class_create(THIS_MODULE, CLASS_NAME);
|
||||
if (IS_ERR(cls)) { ret = PTR_ERR(cls); goto err_cdev; }
|
||||
|
||||
device_create(cls, NULL, devno, NULL, DRIVER_NAME);
|
||||
|
||||
if (gpio_request(DS_RST,"ds_rst")||
|
||||
gpio_request(DS_DAT,"ds_dat")||
|
||||
gpio_request(DS_SCLK,"ds_sclk")||
|
||||
gpio_request(ENC_S1,"enc_s1")||
|
||||
gpio_request(ENC_S2,"enc_s2")||
|
||||
gpio_request(ENC_SW,"enc_sw")||
|
||||
gpio_request(DHT_GPIO,"dht11")) {
|
||||
ret = -EBUSY;
|
||||
goto err_dev;
|
||||
}
|
||||
|
||||
gpio_direction_output(DS_RST,0);
|
||||
gpio_direction_output(DS_SCLK,0);
|
||||
gpio_direction_output(DS_DAT,0);
|
||||
|
||||
gpio_direction_input(ENC_S1);
|
||||
gpio_direction_input(ENC_S2);
|
||||
gpio_direction_input(ENC_SW);
|
||||
gpio_direction_input(DHT_GPIO);
|
||||
|
||||
irq_s1 = gpio_to_irq(ENC_S1);
|
||||
irq_sw = gpio_to_irq(ENC_SW);
|
||||
|
||||
ret = request_irq(irq_s1,s1_irq_handler,IRQF_TRIGGER_FALLING,"enc_s1_irq",NULL);
|
||||
if (ret) goto err_gpio;
|
||||
|
||||
ret = request_irq(irq_sw,sw_irq_handler,
|
||||
IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
|
||||
"enc_sw_irq",NULL);
|
||||
if (ret) { free_irq(irq_s1,NULL); goto err_gpio; }
|
||||
|
||||
ds1302_read_time(&cur);
|
||||
if (cur.ch==1) {
|
||||
ds1302_set_time(&cur);
|
||||
ds1302_read_time(&cur);
|
||||
}
|
||||
|
||||
ktime_get_real_ts64(&ts);
|
||||
rtc_time64_to_tm(ts.tv_sec,&tm_val);
|
||||
|
||||
mutex_lock(&lock0);
|
||||
cur.hh = (tm_val.tm_hour+24-3)%24;
|
||||
cur.mm = tm_val.tm_min;
|
||||
cur.ss = tm_val.tm_sec;
|
||||
cur.ch = 0;
|
||||
|
||||
ds1302_set_time(&cur);
|
||||
edit = cur;
|
||||
edit_mode = false;
|
||||
edit_field = 2;
|
||||
ui_page = 0;
|
||||
mutex_unlock(&lock0);
|
||||
|
||||
printk(KERN_INFO "OK: DHT cached every %d ms\n", DHT_CACHE_MS);
|
||||
return 0;
|
||||
|
||||
err_gpio:
|
||||
gpio_free(DHT_GPIO);
|
||||
gpio_free(ENC_SW); gpio_free(ENC_S2); gpio_free(ENC_S1);
|
||||
gpio_free(DS_SCLK); gpio_free(DS_DAT); gpio_free(DS_RST);
|
||||
err_dev:
|
||||
device_destroy(cls,devno);
|
||||
class_destroy(cls);
|
||||
err_cdev:
|
||||
cdev_del(&cdev0);
|
||||
err_chr:
|
||||
unregister_chrdev_region(devno,1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit mod_exit(void)
|
||||
{
|
||||
free_irq(irq_s1,NULL);
|
||||
free_irq(irq_sw,NULL);
|
||||
|
||||
gpio_free(DHT_GPIO);
|
||||
gpio_free(ENC_SW); gpio_free(ENC_S2); gpio_free(ENC_S1);
|
||||
gpio_free(DS_SCLK); gpio_free(DS_DAT); gpio_free(DS_RST);
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
gpio_free(leds[i]);
|
||||
|
||||
device_destroy(cls,devno);
|
||||
class_destroy(cls);
|
||||
cdev_del(&cdev0);
|
||||
unregister_chrdev_region(devno,1);
|
||||
|
||||
printk(KERN_INFO "==== %s exit ====\n", DRIVER_NAME);
|
||||
}
|
||||
|
||||
module_init(mod_init);
|
||||
module_exit(mod_exit);
|
||||
Reference in new issue
Block a user