#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define LCD_EXT_DEBUG_INFO #ifdef LCD_EXT_DEBUG_INFO #define DBG_PRINT(...) printk(__VA_ARGS__) #else #define DBG_PRINT(...) #endif #define DEVICE_NAME "lcd_vk2c21" /* ioctrl magic set */ #define CH455_IOC_MAGIC 'h' #define IOCTL_CHAR_DISPLAY _IOWR(CH455_IOC_MAGIC, 0x10, unsigned char) #define IOCTL_DOT_DISPLAY _IOWR(CH455_IOC_MAGIC, 0x11, unsigned char) #define IOCTL_COLON_DISPLAY _IOWR(CH455_IOC_MAGIC, 0x12, unsigned char) #define IOCTL_PWR_DISPLAY _IOWR(CH455_IOC_MAGIC, 0x13, unsigned char) #define IOCTL_LAN_DISPLAY _IOWR(CH455_IOC_MAGIC, 0x14, unsigned char) #define IOCTL_LAN_OFF _IOWR(CH455_IOC_MAGIC, 0x15, unsigned char) #define IOCTL_WIFI_LOW_DISPLAY _IOWR(CH455_IOC_MAGIC, 0x16, unsigned char) #define IOCTL_WIFI_FINE_DISPLAY _IOWR(CH455_IOC_MAGIC, 0x17, unsigned char) #define IOCTL_WIFI_OFF _IOWR(CH455_IOC_MAGIC, 0x18, unsigned char) #define IOCTL_LED_ON _IOWR(CH455_IOC_MAGIC, 0x19, unsigned char) #define IOCTL_LED_OFF _IOWR(CH455_IOC_MAGIC, 0x1a, unsigned char) //以下管脚输出定义根据客户单片机做相应的修改 #define Vk2c21_SCL_H() gpio_set_value(gpio_i2c_scl,1) #define Vk2c21_SCL_L() gpio_set_value(gpio_i2c_scl,0) #define Vk2c21_SDA_H() gpio_set_value(gpio_i2c_sda,1) #define Vk2c21_SDA_L() gpio_set_value(gpio_i2c_sda,0) #define Vk2c21_GET_SDA() gpio_get_value(gpio_i2c_sda) #define Vk2c21_SET_SDA_IN() gpio_direction_input(gpio_i2c_sda) #define Vk2c21_SET_SDA_OUT() gpio_direction_output(gpio_i2c_sda, 1) static int gpio_i2c_scl,gpio_i2c_sda; /** ****************************************************************************** * @file vk2c21.c * @author kevin_guo * @version V1.2 * @date 05-17-2020 * @brief This file contains all the vk2c21 functions. * 此文件适用于 VK2c21 ****************************************************************************** * @attention ****************************************************************************** */ #include "lcd_vk2c21.h" #define VK2c21_CLK 100 //SCL信号线频率,由delay_nus实现 50->10kHz 10->50kHz 5->100kHz //驱动seg数 //4com //VK2C21A/B/C/D #define Vk2c21_SEGNUM 13 #define Vk2c21A_MAXSEGNUM 20 #define Vk2c21B_MAXSEGNUM 16 #define Vk2c21C_MAXSEGNUM 12 #define Vk2c21D_MAXSEGNUM 8 //segtab[]数组对应实际的芯片到LCD连线,连线见-VK2c21参考电路 //4com //Vk2c21A unsigned char vk2c21_segtab[Vk2c21_SEGNUM]={ 18,17,16,15,14,13,12,11,10, 9,8,7,6 }; //4com unsigned char vk2c21_dispram[Vk2c21_SEGNUM/2];//4COM时每个字节数据对应2个SEG //8com //unsigned char vk2c21_dispram[Vk2c21_SEGNUM];//8COM时每个字节数据对应1个SEG unsigned char shuzi_zimo[15]= //数字和字符字模 { //0 1 2 3 4 5 6 7 8 9 - L o H i 0xf5,0x05,0xb6,0x97,0x47,0xd3,0xf3,0x85,0xf7,0xd7,0x02,0x70,0x33,0x67,0x50 }; unsigned char vk2c21_segi,vk2c21_comi; unsigned char vk2c21_maxcom;//驱动的com数VK2C23A可以是4com或者8com unsigned char vk2c21_maxseg;//Vk2c21A=20 Vk2c21B=16 Vk2c21C=12 Vk2c21D=8 /* Private function prototypes -----------------------------------------------*/ unsigned char Vk2c21_InitSequence(void); /* Private function ----------------------------------------------------------*/ /******************************************************************************* * Function Name : delay_nus * Description : 延时1uS程序 * Input : n->延时时间nuS * Output : None * Return : None *******************************************************************************/ static void delay_nus(unsigned int n) { ndelay(n); } /******************************************************************************* * Function Name : I2CStart I2CStop I2CSlaveAck * I2CStart Description : 时钟线高时,数据线由高到低的跳变,表示I2C开始信号 * I2CStop Description : 时钟线高时,数据线由低到高的跳变,表示I2C停止信号 * I2CSlaveAck Description : I2C从机设备应答查询 *******************************************************************************/ static void Vk2c21_I2CStart( void ) { Vk2c21_SCL_H(); Vk2c21_SDA_H(); delay_nus(VK2c21_CLK); Vk2c21_SDA_L(); delay_nus(VK2c21_CLK); } static void Vk2c21_I2CStop( void ) { Vk2c21_SCL_H(); Vk2c21_SDA_L(); delay_nus(VK2c21_CLK); Vk2c21_SDA_H(); delay_nus(VK2c21_CLK); } static unsigned char Vk2c21_I2CSlaveAck( void ) { unsigned int TimeOut; unsigned char RetValue; Vk2c21_SCL_L(); //单片机SDA脚为单向IO要设为输入脚 Vk2c21_SET_SDA_IN(); delay_nus(VK2c21_CLK); Vk2c21_SCL_H();//第9个sclk上升沿 TimeOut = 10000; while( TimeOut-- > 0 ) { if( Vk2c21_GET_SDA()!=0 )//读取ack { RetValue = 1; } else { RetValue = 0; break; } } Vk2c21_SCL_L(); //单片机SDA脚为单向IO要设为输出脚 Vk2c21_SET_SDA_OUT(); //printk("---%s----ret=%d------\n",__func__,RetValue); return RetValue; } /******************************************************************************* * Function Name : I2CWriteByte * Description : I2C写一字节命令,命令先送高位 * Input : byte-要写入的数据 * Output : None * Return : None *******************************************************************************/ static void Vk2c21_I2CWriteByte( unsigned char byte ) { unsigned char i=8; while (i--) { Vk2c21_SCL_L(); if(byte&0x80) Vk2c21_SDA_H(); else Vk2c21_SDA_L(); byte<<=1; delay_nus(VK2c21_CLK); Vk2c21_SCL_H(); delay_nus(VK2c21_CLK); } } /************************************************************* *函数名称: WriteCmdVk2c21 *函数功能: 向Vk2C23写1个命令 *输入参数: addr Dmod地址 data 写入的数据 *输出参数:SET: 写入正常;RESET:写入错误 *备 注: **************************************************************/ static unsigned char WriteCmdVk2c21(unsigned char cmd, unsigned char data ) { Vk2c21_I2CStart(); Vk2c21_I2CWriteByte( Vk2c21_ADDR|0x00 ); if( 1 == Vk2c21_I2CSlaveAck() ) { Vk2c21_I2CStop(); return 0; } Vk2c21_I2CWriteByte( cmd ); if( 1 == Vk2c21_I2CSlaveAck() ) { Vk2c21_I2CStop(); return 0; } Vk2c21_I2CWriteByte( data ); if( 1 == Vk2c21_I2CSlaveAck() ) { Vk2c21_I2CStop(); return 0; } Vk2c21_I2CStop(); return 0; //返回操作成败标志 } /******************************************************************************* * Function Name : Write1Data * Description : 写1字节数据到显示RAM * Input : Addr-写入ram的地址 * : Dat->写入ram的数据 * Output : None * Return : 0-ok 1-fail *******************************************************************************/ static unsigned char Write1DataVk2c21(unsigned char Addr,unsigned char Dat) { //START 信号 Vk2c21_I2CStart(); //SLAVE地址 Vk2c21_I2CWriteByte(Vk2c21_ADDR); if( 1 == Vk2c21_I2CSlaveAck() ) { Vk2c21_I2CStop(); return 1; } //写显示RAM命令 Vk2c21_I2CWriteByte(Vk2c21_RWRAM); if( 1 == Vk2c21_I2CSlaveAck() ) { Vk2c21_I2CStop(); return 0; } //显示RAM地址 Vk2c21_I2CWriteByte(Addr); if( 1 == Vk2c21_I2CSlaveAck() ) { Vk2c21_I2CStop(); return 1; } //显示数据,1字节数据包含2个SEG Vk2c21_I2CWriteByte(Dat); if( Vk2c21_I2CSlaveAck()==1 ) { Vk2c21_I2CStop(); return 1; } //STOP信号 Vk2c21_I2CStop(); return 0; } /******************************************************************************* * Function Name : WritenData * Description : 写多个数据到显示RAM * Input : Addr-写入ram的起始地址 * : Databuf->写入ram的数据buffer指针 * : Cnt->写入ram的数据个数 * Output : None * Return : 0-ok 1-fail *******************************************************************************/ static unsigned char WritenDataVk2c21(unsigned char Addr,unsigned char *Databuf,unsigned char Cnt) { unsigned char n; //START信号 Vk2c21_I2CStart(); //SLAVE地址 Vk2c21_I2CWriteByte(Vk2c21_ADDR); if( 1 == Vk2c21_I2CSlaveAck() ) { Vk2c21_I2CStop(); return 0; } //写显示RAM命令 Vk2c21_I2CWriteByte(Vk2c21_RWRAM); if( 1 == Vk2c21_I2CSlaveAck() ) { Vk2c21_I2CStop(); return 0; } //显示RAM起始地址 Vk2c21_I2CWriteByte(Addr); if( 1 == Vk2c21_I2CSlaveAck() ) { Vk2c21_I2CStop(); return 0; } //发送Cnt个数据到显示RAM for(n=0;n写入ram的数据(1个字节数据对应2个SEG) * Output : None * Return : None *******************************************************************************/ static void Vk2c21_DisAll(unsigned char dat) { unsigned char segi; unsigned char dispram[16]; if(vk2c21_maxcom==4) { for(segi=0;segi<10;segi++) { dispram[segi]=dat; } WritenDataVk2c21(0,dispram,10);//这里送8bit数据对应2个SEG,每8bit数据地址加1,每8位数据1个ACK } else { for(segi=0;segi<16;segi++) { dispram[segi]=dat; } WritenDataVk2c21(0,dispram,16);//这里送8bit数据对应1个SEG,每8bit数据地址加1,每8位数据1个ACK } } /******************************************************************************* * Function Name : DisSegComOn * Description : 点亮1个点(1个seg和1个com交叉对应的显示点) * Input :seg->点对应的seg脚 * :com->点对应com脚 * Output : None * Return : None *******************************************************************************/ static void Vk2c21_DisSegComOn(unsigned char seg,unsigned char com) { if(vk2c21_maxcom==4) { if(seg%2==0) Write1DataVk2c21(seg/2,(1<<(com)));//这里送8位数据低4bit有效,每8bit数据地址加1,每8位数据1个ACK) else Write1DataVk2c21(seg/2,(1<<(4+com)));//这里送8位数据高4bit有效,每8bit数据地址加1,每8位数据1个ACK } else { Write1DataVk2c21(seg,(1<<(com)));//这里送8位数据低4bit有效,每8bit数据地址加1,每8位数据1个ACK } } /******************************************************************************* * Function Name : DisSegComOff * Description : 关闭1个点(1个seg和1个com交叉对应的显示点) * Input :seg->点对应的seg脚 * :com->点对应com脚 * Output : None * Return : None *******************************************************************************/ static void Vk2c21_DisSegComOff(unsigned char seg,unsigned char com) { if(vk2c21_maxcom==4) { if(seg%2==0) Write1DataVk2c21(seg/2,~(1<0偏置电压=VLCD WriteCmdVk2c21(Vk2c21_IVASET,VLCDSEL_IVAOFF_R1); //SEG/VLCD共用脚设为SEG,内部偏置电压调整:1/3bias=0.652VDD 1/4bias=0.714VDD //WriteCmdVk2c21(Vk2c21_IVASET,SEGSEL_IVA02H); //test Vk2c21_DisAll(0x00); disp_3num(456); //Vk2c21_DisAll(0xff); //LCD全显 //disp_3num(1234); return ret; } static int Vk2c21_open(struct inode *inode, struct file *file) { int ret; ret = nonseekable_open(inode, file); if(ret < 0) return ret; return 0; } static int Vk2c21_release(struct inode *inode, struct file *file) { return 0; } static long Vk2c21_ioctl(struct file *file, unsigned int cmd, unsigned long args) { int ret = 0 , i=0,pos_temp=0; void __user *argp = (void __user *)args; unsigned char display_arg[6]={0}; switch (cmd){ case IOCTL_CHAR_DISPLAY: if (args) { ret = copy_from_user(display_arg, argp,sizeof(display_arg)/sizeof(display_arg[0])); } for(i=0;i<6;i++) { if(display_arg[i] <= '9' && display_arg[i] >= '0') { pos_temp = display_arg[i] - '0'; vk2c21_dispram[i]&=0xf0; vk2c21_dispram[i]|=shuzi_zimo[pos_temp]&0x0f; vk2c21_dispram[i]&=0x8f; vk2c21_dispram[i]|=shuzi_zimo[pos_temp]&0xf0; Write1DataVk2c21(vk2c21_segtab[2*i+2]/2,vk2c21_dispram[i]); }else if(display_arg[i] <= 'z' && display_arg[i] >= 'a'){ }else if(display_arg[i] <= 'Z' && display_arg[i] >= 'A'){ } } break; case IOCTL_DOT_DISPLAY: break; case IOCTL_COLON_DISPLAY: break; case IOCTL_PWR_DISPLAY: break; case IOCTL_LAN_DISPLAY: Vk2c21_DisSegComOn(vk2c21_segtab[6],0x1); break; case IOCTL_LAN_OFF: Vk2c21_DisSegComOff(vk2c21_segtab[6],0x1); break; case IOCTL_WIFI_LOW_DISPLAY: break; case IOCTL_WIFI_FINE_DISPLAY: break; case IOCTL_WIFI_OFF: break; case IOCTL_LED_ON: Vk2c21_DisAll(0xff); //LCD全显 break; case IOCTL_LED_OFF: Vk2c21_DisAll(0x00); //LCD全关 break; default: printk("ERROR: IOCTL CMD NOT FOUND!!!\n"); break; } return 0; } static struct file_operations Vk2c21_fops ={ .owner =THIS_MODULE, .open =Vk2c21_open, .release =Vk2c21_release, .unlocked_ioctl =Vk2c21_ioctl, }; static int Vk2c21_probe(struct platform_device *pdev) { static struct class * scull_class; struct device_node *node = pdev->dev.of_node; enum of_gpio_flags flags; int ret; ret =register_chrdev(0,DEVICE_NAME,&Vk2c21_fops); if(ret<0){ printk("can't register device lcd_vk2c21.\n"); return ret; } printk("register device lcd_vk2c21 success.\n"); scull_class = class_create(THIS_MODULE,DEVICE_NAME); if(IS_ERR(scull_class)) { printk(KERN_ALERT "Err:faile in scull_class!\n"); return -1; } device_create(scull_class, NULL, MKDEV(ret,0), NULL, DEVICE_NAME); //--------------------------- gpio_i2c_scl = of_get_named_gpio_flags(node, "i2c_scl", 0, &flags); if (gpio_is_valid(gpio_i2c_scl)){ if (gpio_request(gpio_i2c_scl, "i2c_scl_gpio")<0) { printk("%s: failed to get gpio_i2c_scl.\n", __func__); return -1; } gpio_direction_output(gpio_i2c_scl, 1); printk("%s: get property: gpio,i2c_scl = %d\n", __func__, gpio_i2c_scl); }else{ printk("get property gpio,i2c vk2c21 failed \n"); return -1; } gpio_i2c_sda = of_get_named_gpio_flags(node, "i2c_sda", 0, &flags); if (gpio_is_valid(gpio_i2c_sda)){ if (gpio_request(gpio_i2c_sda, "i2c_sda_gpio")<0) { printk("%s: failed to get gpio_i2c_sda.\n", __func__); return -1; } gpio_direction_output(gpio_i2c_sda, 1); printk("%s: get property: gpio,i2c_sda = %d\n", __func__, gpio_i2c_sda); }else{ printk("get property gpio,i2c vk2c21 failed \n"); return -1; } printk("==========%s probe ok========\n", DEVICE_NAME); ret = Vk2c21_Init(); if(ret < 0) return -1; return 0; } static int Vk2c21_remove(struct platform_device *pdev) { unregister_chrdev(0,DEVICE_NAME); return 0; } static void Vk2c21_shutdown (struct platform_device *pdev) { WriteCmdVk2c21(Vk2c21_SYSSET,SYSOFF_LCDOFF); } #ifdef CONFIG_OF static const struct of_device_id Vk2c21_dt_match[]={ { .compatible = "lcd_vk2c21",}, {} }; MODULE_DEVICE_TABLE(of, Vk2c21_dt_match); #endif static struct platform_driver Vk2c21_driver = { .probe = Vk2c21_probe, .remove = Vk2c21_remove, .shutdown = Vk2c21_shutdown, .driver = { .name = DEVICE_NAME, .owner = THIS_MODULE, #ifdef CONFIG_OF .of_match_table = of_match_ptr(Vk2c21_dt_match), #endif }, }; static int __init led_vk2c21_init(void) { int ret; DBG_PRINT("%s\n=============================================\n", __FUNCTION__); ret = platform_driver_register(&Vk2c21_driver); if (ret) { printk("[error] %s failed to register vk2c21 driver module\n", __FUNCTION__); return -ENODEV; } return ret; } static void __exit led_vk2c21_exit(void) { platform_driver_unregister(&Vk2c21_driver); } module_init(led_vk2c21_init); module_exit(led_vk2c21_exit); MODULE_AUTHOR("Hugsun"); MODULE_DESCRIPTION("LCD Extern driver for lcd_vk2c21"); MODULE_LICENSE("GPL");