1. 程式人生 > >S5PV210 Linux 模擬SPI方式控制ADS7846觸控式螢幕驅動

S5PV210 Linux 模擬SPI方式控制ADS7846觸控式螢幕驅動

/**************************************************************************************
*	ads7846 touch screen	ads.c
*	Light	< [email protected] >
*	20011-11-28
*	
***************************************************************************************/

#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>

#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio.h>	

#define ADS7846_CNV_NBR  8  //ADC連續轉換的次數

struct ads7846_ts_info {

    struct input_dev *dev;

    unsigned int xp; //x方向位置
    unsigned int yp; //y方向位置
    unsigned int count; //adc轉換次數
    unsigned int cnv_nbr;
    unsigned int x_buf[ADS7846_CNV_NBR];  //ad轉換buf 
    unsigned int y_buf[ADS7846_CNV_NBR];
    
};

static struct ads7846_ts_info	*ts;
static struct input_dev *OFN_dev;
int absX = 0;
int absY = 0;

#define ADS7846_GPIO_MISO	6		//gpb6
#define ADS7846_GPIO_MOSI	7		//gpb7
#define ADS7846_GPIO_CLK	4		//gpb4
#define ADS7846_GPIO_CS		5		//gpb5

// ADS7846 Control Byte bit defines
#define ADS7846_CMD_START	0x0080
#define ADS7846_ADDR_BIT	4
#define ADS7846_ADDR_MASK 	(0x7<<ADS7846_ADDR_BIT)
#define ADS7846_MEASURE_X  	(0x5<<ADS7846_ADDR_BIT)
#define ADS7846_MEASURE_Y  	(0x1<<ADS7846_ADDR_BIT)
#define ADS7846_MEASURE_Z1 	(0x3<<ADS7846_ADDR_BIT)
#define ADS7846_MEASURE_Z2 	(0x4<<ADS7846_ADDR_BIT)
#define ADS7846_8BITS     	(1<<3)
#define ADS7846_12BITS    	0
#define ADS7846_SER       	(1<<2)
#define ADS7846_DFR       	0
#define ADS7846_PWR_BIT   	0
#define ADS7846_PD      	0
#define ADS7846_ADC_ON  	(0x1<<ADS7846_PWR_BIT)
#define ADS7846_REF_ON  	(0x2<<ADS7846_PWR_BIT)
#define ADS7846_REF_ADC_ON 	(0x3<<ADS7846_PWR_BIT)

#define MEASURE_8BIT_X\
    (unsigned short)(ADS7846_CMD_START | ADS7846_MEASURE_X | ADS7846_8BITS | ADS7846_DFR | ADS7846_PD)
#define MEASURE_8BIT_Y\
    (unsigned short)(ADS7846_CMD_START | ADS7846_MEASURE_Y | ADS7846_8BITS | ADS7846_DFR | ADS7846_PD)

#define MEASURE_12BIT_X \
    (unsigned short)(ADS7846_CMD_START | ADS7846_MEASURE_X | ADS7846_12BITS | ADS7846_DFR | ADS7846_PD)
#define MEASURE_12BIT_Y \
    (unsigned short)(ADS7846_CMD_START | ADS7846_MEASURE_Y | ADS7846_12BITS | ADS7846_DFR | ADS7846_PD)
#define MEASURE_12BIT_Z1 \
    (unsigned char)(ADS7846_MEASURE_Z1 | ADS7846_12BITS | ADS7846_DFR | ADS7846_PD)
#define MEASURE_12BIT_Z2 \
    (unsigned char)(ADS7846_MEASURE_Z2 | ADS7846_12BITS | ADS7846_DFR | ADS7846_PD)


static inline void set_miso_as_up(void)//gpc0
{
	s3c_gpio_setpull(S5PV210_GPB(6), S3C_GPIO_PULL_UP);
}

static inline void set_miso_as_input(void)//gpc0
{
	gpio_direction_input(S5PV210_GPB(6));	
}

static inline void set_cs_mosi_clk_as_output(void)//gpc1 2 3
{
	gpio_direction_output(S5PV210_GPB(7),1);	//MOSI
	gpio_direction_output(S5PV210_GPB(4),1);	//clk
	gpio_direction_output(S5PV210_GPB(5),1);	//cs
}

static inline void set_cs_mosi_clk_as_up(void)//gpc1 2 3
{	
	s3c_gpio_setpull(S5PV210_GPB(4), S3C_GPIO_PULL_UP);
	s3c_gpio_setpull(S5PV210_GPB(5), S3C_GPIO_PULL_UP);
	s3c_gpio_setpull(S5PV210_GPB(7), S3C_GPIO_PULL_UP);
}

static inline void set_gpcx_value(int pinx ,int v)
{

	gpio_set_value(S5PV210_GPB(pinx),v);
}

static inline int get_gpcx_value(int pinx)
{
	return( gpio_get_value(S5PV210_GPB(pinx)) );
}

//讀12bit
static unsigned int ads7846_Read_Data(void)
{
 unsigned int i,temp=0x00;
   for(i=0;i<12;i++)
   {
       temp <<=1;
       set_gpcx_value(ADS7846_GPIO_CLK, 1); udelay(10);
       set_gpcx_value(ADS7846_GPIO_CLK, 0); udelay(10);
       if(get_gpcx_value(ADS7846_GPIO_MISO) != 0)temp++;
   }
   return (temp);
}
//寫8bit
static void ads7846_Write_Data(unsigned int n)
{
   unsigned char i;
  set_gpcx_value(ADS7846_GPIO_CLK, 0);
  for(i=0;i<8;i++)
   {
    if((n&0x80)==0x80)
      set_gpcx_value(ADS7846_GPIO_MOSI, 1);
     else
      set_gpcx_value(ADS7846_GPIO_MOSI, 0);
    
    n <<= 1;
    set_gpcx_value(ADS7846_GPIO_CLK, 1);  udelay(10);
    set_gpcx_value(ADS7846_GPIO_CLK, 0); udelay(10);
    }
}

//ADS7846轉換==//儲存在ts->buf 當中//ADS7846_CNV_NBR是值為8的巨集,含義是AD轉換次數。
static void ads7846_conver_start(void)
{
	int i;
	unsigned int cmd[2];
	unsigned int data[2];
        
	set_gpcx_value(ADS7846_GPIO_CS, 0);
        //開讀
	cmd[0] = MEASURE_12BIT_X;//巨集,本程式內定義
	cmd[1] = MEASURE_12BIT_Y;//巨集,本程式內定義

	/* CS# Low */
	set_gpcx_value(ADS7846_GPIO_CS, 0);
	
	//連續轉換
	for(ts->count=0; ts->count<ts->cnv_nbr;) 
	{
	    //分別讀出x y座標==
	    for(i=0; i<2; i++){
	      ads7846_Write_Data(cmd[i]);
	      udelay(40);
	      data[i] = ads7846_Read_Data();
	    }

	    //儲存轉換結果
	    ts->x_buf[ts->count]= data[0];
	    ts->y_buf[ts->count]= data[1];
	    ts->count++;
	}
	/* CS# High */
	set_gpcx_value(ADS7846_GPIO_CS, 1);

}
//-----------------------------------------------------------------------------
//觸控式螢幕資料濾波演算法
//觸控式螢幕AD連續轉換N次後,按升序排列,再取中間幾位值求平均
#define  TS_AD_NBR_PJ		4      	 //取中間4位求平均值 
#define  TS_AD_NBR_MAX_CZ	10       //最大與最小的差值
static inline bool touch_ad_data_filter(unsigned int *buf0, unsigned int *buf1)
{
	   unsigned int i,j,k,temp,temp1,nbr=(ADS7846_CNV_NBR);
	   //將轉換結果升序排列
	   //buf0
	    for (j= 0; j< nbr; j++)
	    {
		for (i = 0; i < nbr; i++) 
	        {           
			if(buf0[i]>buf0[i+1])//升序排列
			{
				temp=buf0[i+1];
				buf0[i+1]=buf0[i];
				buf0[i]=temp;
			}  
	        }
	    }
	    
	  
	    //buf1
	    for (j= 0; j< nbr; j++)
	    {
		for (i = 0; i < nbr; i++) 
	        {           
			if(buf1[i]>buf1[i+1])//升序排列
			{
				temp=buf1[i+1];
				buf1[i+1]=buf1[i];
				buf1[i]=temp;
			}  
	        } 
	    }	 
	   //取中間值求平均==
	   k=((nbr-TS_AD_NBR_PJ)>>1);
	   temp = 0;temp1 = 0;
	   //檢查值是否有效==
	   if((buf0[k+TS_AD_NBR_PJ-1]-buf0[k]>TS_AD_NBR_MAX_CZ)||(buf1[k+TS_AD_NBR_PJ-1]-buf1[k]>TS_AD_NBR_MAX_CZ)) //無效值
	    {

	      return 0; 
	    }
	   //--
	   //將中間指定位數累加
	   for(i=0;i<TS_AD_NBR_PJ;i++)
	   {  
	      temp += buf0[k+i];
	      temp1 += buf1[k+i];
	   }
	   //求平均值,將結果儲存在最低位
	   buf0[0]=temp/TS_AD_NBR_PJ;   
	   buf1[0] = temp1/TS_AD_NBR_PJ; 
	   //--
	   return 1; 
}

static inline bool get_down(void)	//獲取中斷口電平狀態
{
	return (gpio_get_value (S5PV210_GPH0(7)));  	// 中斷管腳電平狀態
}
/*===========================================================================================
    touch_timer_get_value這個函式的呼叫:
    
    1、  觸控筆開始點選的時候, 在中斷函式touch_down裡面被呼叫,不是直接呼叫,而是設定定時器超時後呼叫
         這樣是為了去抖動
         
    2、  touch_timer_get_value被呼叫一次後,如果pendown訊號有效,則touch_timer_get_value會被持續呼叫
         也是通過定時器實現的
         
    touch_timer_get_value這個函式的功能:
      啟動7846轉換,直到連續轉換8次後,再濾波處理,獲得有效值,並向上報告觸控式螢幕事件

============================================================================================*/
static void touch_timer_get_value(unsigned long data); 

static DEFINE_TIMER(touch_timer, touch_timer_get_value, 0, 0);

static void touch_timer_get_value(unsigned long data) {

	int pendown;

	//printk("touch_timer_get_value\n");   
	pendown = get_down();

	if(!pendown)	//	檢測按下狀態
	{
	
	        //關中斷===
	        disable_irq(IRQ_EINT7);
	        //啟動ADS7846轉換==
	        ads7846_conver_start();
		//開中斷==
	        enable_irq(IRQ_EINT7);

			if(touch_ad_data_filter(ts->x_buf,ts->y_buf)) // 濾波函式處理
                       {
		        
			ts->xp = ts->x_buf[0];
			ts->yp = ts->y_buf[0];
			
			printk("ts->xp = %d,ts->yp = %d\n",ts->xp,ts->yp);

			//--------------------------------------------------------------------
				absX = ts->xp ;		
				absY = ts->yp ;				

				input_report_abs(OFN_dev, ABS_X, absX);		//X軸座標
				input_report_abs(OFN_dev, ABS_Y, absY);		//Y軸座標
				input_report_key(OFN_dev, BTN_TOUCH, 1);	//按下 按鍵
				input_report_abs(OFN_dev, ABS_PRESSURE, 1);	//按下 壓力	
				input_sync(OFN_dev);	//事件同步
				input_report_key(OFN_dev, BTN_TOUCH, 0);	//擡起 按鍵值	
				input_report_abs(OFN_dev, ABS_PRESSURE, 0);	//擡起 壓力值
				input_sync(OFN_dev);
			//--------------------------------------------------------------------

		       }
               
		mod_timer(&touch_timer, jiffies + 2);   //重新設定系統定時器,超時後,又會呼叫touch_timer_get_value
		                                         //jiffies變數記錄了系統啟動以來,系統定時器已經觸發的次數。核心每秒鐘將jiffies變數增加HZ次。
		                                        //因此,對於HZ值為100的系統,1個jiffy等於10ms,而對於HZ為1000的系統,1個jiffy僅為1ms
		                                        //這裡系統配置提HZ是100
		
	} 
	else
	{

	/*	ads7846_conver_start();
		if(touch_ad_data_filter(ts->x_buf,ts->y_buf)) // 濾波函式處理
		{

			ts->xp = ts->x_buf[0];
			ts->yp = ts->y_buf[0];

			printk("ts->xp = %d,ts->yp = %d\n",ts->xp,ts->yp);

			//--------------------------------------------------------------------
			absX = ts->xp ;		
			absY = ts->yp ;				

				input_report_abs(OFN_dev, ABS_X, absX);		//X軸座標
				input_report_abs(OFN_dev, ABS_Y, absY);		//Y軸座標
		
				input_report_key(OFN_dev, BTN_TOUCH, 0);	//擡起 按鍵值	
				input_report_abs(OFN_dev, ABS_PRESSURE, 0);	//擡起 壓力值
				input_sync(OFN_dev);
			//--------------------------------------------------------------------

		}
	*/
	}
	
}

static irqreturn_t touch_down(int irqno, void *param)			//中斷處理函式
{
     	printk("Touch down -- Enter Interrupt 0 !\n");   

        //稍後呼叫touch_timer_get_value,去抖動
	mod_timer(&touch_timer, jiffies + 15);  //等ADS7846轉換完成了再讀
	                                        //同時還可以防抖動,如果定時器沒有超時的這段時間裡,
						//發生了擡起和按下中斷,則定時器的值會被重設,不會超時
	                                        //核心配置時HZ值設為100,即1個jiffy等於10ms,
	//touch_timer_get_value(1);  //直接呼叫會有抖動

	return IRQ_RETVAL(IRQ_HANDLED);
}

//-------------------------------------------
static int __init ads7846_ts_init(void)
{
	    printk( KERN_INFO "Enter mini210 ads7846 Touchscreen driver\n" );

	    int ret = 0;

	    printk("ads7846_ts_probe start!\n");
	    //給ads7846_ts_info指標分配記憶體==
	    ts = kzalloc(sizeof(struct ads7846_ts_info), GFP_KERNEL);
	    ts->cnv_nbr = ADS7846_CNV_NBR;
	    ts->xp = 0;
	    ts->yp = 0;
	    ts->count = 0;
	    //申請中斷==
	    s3c_gpio_setpull(S5PV210_GPH0(7), S3C_GPIO_PULL_UP);
	    gpio_direction_input(S5PV210_GPH0(7));
	    s3c_gpio_cfgpin(S5PV210_GPH0(7), S3C_GPIO_SFN(0xf0000000));	//設定為外部中斷 eint0 ,參考GP0CON【】
	    set_irq_type(IRQ_EINT7, IRQ_TYPE_EDGE_FALLING);	
//-------------------------------------------------------------------------
	OFN_dev = input_allocate_device();				// 申請一個輸入裝置
	if (!OFN_dev) return -ENOMEM;
	OFN_dev->name = "TouchScreen Pipe";				// 觸控式螢幕裝置屬性資訊
	OFN_dev->phys = "input(ts)";
	OFN_dev->id.bustype = BUS_RS232;
	OFN_dev->id.vendor  = 0xDEAD;
	OFN_dev->id.product = 0xBEEF;
	OFN_dev->id.version = 0x0101;
	
	input_set_abs_params(OFN_dev, ABS_X, 0, 4000, 0, 0);			// 
	input_set_abs_params(OFN_dev, ABS_Y, 0, 4000, 0, 0);			// 
	input_set_abs_params(OFN_dev, ABS_PRESSURE, 0, 1, 0, 0);		// 壓力
	
	OFN_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
	OFN_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
//---------------------------------------------------------------------------
	ret = request_irq(IRQ_EINT7, touch_down,IRQ_TYPE_EDGE_BOTH, 
		                  "ads7864_touch", ts);
	if (ret != 0) {
		printk("ads7846_ts.c: Could not allocate ts IRQ_EINT !\n");
		ret = -EIO;
		goto fail;
	}
//--------------------------------------------------------------------------------	
	ret = input_register_device(OFN_dev);
	if(ret)
	{	
		free_irq(IRQ_EINT7, OFN_dev);
		input_free_device(OFN_dev);
		return -1;
	}
//--------------------------------------------------------------------------------
	//初始化GPIO==
		set_miso_as_up(); 
		set_miso_as_input(); 
		set_cs_mosi_clk_as_up();
		set_cs_mosi_clk_as_output(); 
		set_gpcx_value(ADS7846_GPIO_MOSI ,1);	
		set_gpcx_value(ADS7846_GPIO_CS ,1);
		set_gpcx_value(ADS7846_GPIO_CLK ,1);
		//--
		printk("ads7846_ts_probe end!\n");
	
   	return 0;

fail:
	disable_irq(IRQ_EINT7);
        free_irq(IRQ_EINT7, ts);

	return ret;	   
}

static void __exit ads7846_ts_exit(void)
{
	printk("Exit mini210 ads7846 Touchscreen driver\n");
	printk(KERN_INFO "ads7846_ts_remove() of TS called !\n");
        
	disable_irq(IRQ_EINT7);
        free_irq(IRQ_EINT7, ts);
}

module_init(ads7846_ts_init);
module_exit(ads7846_ts_exit);

MODULE_AUTHOR("LIGHT");
MODULE_LICENSE("GPL");

相關推薦

S5PV210 Linux 模擬SPI方式控制ADS7846觸控式螢幕驅動

/************************************************************************************** * ads7846 touch screen ads.c * Light < [email&

Linux下使用/dev/mem控制GPIO模擬SPI時序控制LCD5110

關於如何使用/dev/mem直接控制GPIO口參見我的另一篇部落格:http://blog.csdn.net/tq384998430/article/details/53161192。這篇文章將使用GPIO的操作模擬出SPI時序來控制諾基亞LCD5110螢幕,關於該螢幕可以

STM32用SPI方式控制OLED模組

一、OLED 1. OLED模組的外觀 2. OLED模組的電路圖 3. OLED模組引數 專案 說明 介面特性 3.3V(串電阻後,可與 5V 系統連線) 通訊介面 4 線 SPI 螢幕解析度

linux 輸入子系統之電阻式觸控式螢幕驅動

一、輸入子系統情景回憶ING...... 在Linux中,輸入子系統是由輸入子系統裝置驅動層、輸入子系統核心層(Input Core)和輸入子系統事件處理層(Event Handler)組成。其中裝置驅動層提供對硬體各暫存器的讀寫訪問和將底層硬體對使用者輸入訪問的響應轉換為標準的輸入事件,再

Linux模擬控制網絡時延

後來 控制 找到 blog 上網 配置 應用 親測 toc 之前以為可以使用Linux自帶的工具模擬控制網絡時延,所以上網找了一些資料。後來發現,找到的資料目前只支持在一個網卡上模擬發送報文的時延,而不能 設置有差別的網絡時延,或者說當要模擬的向A發送的時延與要模擬的向B發

nRF52832 Timer+PPI+SPI 全硬體觸發方式控制DAC,減少CPU干預,效率極大提升

【問題】:測試Timer每次通過PPI觸發SPI寫,SPI寫完呼叫回撥函式,回撥函式中測試 NRF_SPIM2->TXD.PTR 的每次都是正常遞增變化的,但是DAC就是沒有實際的輸出; PS:1、要寫的源資料測試是正確的; 2、為了不頻繁佔用CPU資

skyeye模擬uboot啟動linux(initrd方式

bootelf命令:啟動vmlinux bootm命令:啟動uImage go命令:啟動zImage 前言:連結指令碼   使用者態程式不用關心section的具體位置;在使用者態,核心會解析elf可執行檔案的各個section、然後把它對映到虛擬地址空間。   然而,u

Linux使用curl 方式安裝docker-compose 後執行docker-compose version 檢查安裝是否成功時出錯的解決辦法

hub 執行 cannot 1.0 使用 文件 curl url 驗證 0x0.緣起: 今天在一臺新的Fedora 25上按照官方文檔,使用curl方式安裝 docker-compose後,驗證是否安裝成功時出錯: 安裝時使用的命令為; curl -L https:/

Linux系統管理 服務控制

linux服務控制系統安裝完成後的設置 1) 關閉防火墻 /etc/init.d/iptables stop 設置開機不加載 chkconfig iptables off 查看防火墻的狀態 iptables -L2)關閉selinux: 查看selinux的狀態 getenforc

Linux在終端和控制臺下復制粘貼命令快捷鍵

ctrl inux ctr ins 選中 linux 復制。 控制臺 過程 1、在終端下: (1)復制命令:Ctrl + Shift + C 組合鍵. (2)粘貼命令:Ctrl + Shift + V 組合鍵. 2、在控制臺下:(即vi編輯過程中) (1)復制命令:Ctrl

Linux分區方式及關閉iptables和selinux的方式

linux中 管理 訪問 lin 當前 table 開機自啟 檢查 學習 分區方式一般有三種 第一種:數據不是很重要 /boot(系統的引導分區): 系統引導的信息/軟件 系統的內核 200M swap( 交換分區): 為了避免系統內存用光了導致系統 宕機 如果系統內

Linux系統賬號安全控制

linux 安全 賬號控制 grub 一、基本安全1.系統賬號清理Linux中賬號有root,手工創建的,維護系統運作的,和非登錄用戶,常見的非登錄用戶有bin.daemon.adm.mail.nobody.apache.mysql.ftp等,其中一部分很少用到,可以刪除,如news.uucp

Linux跨平臺遠程控制

求大神輕噴·Linux跨平臺遠程控制

pycharm linux版快捷方式創建

gen comment eric 文檔 ati 解壓 .sh name top ****************************pycharm_linux安裝and快捷方式創建******************1.下載好安裝包之後解壓: tar -xfz 壓

Linux引導與服務控制

code roc process 內核 故障 配置文件 菜單 服務 centos 一、Linux引導過程 1.引導過程 (1)開機自檢:根據主板BIOS對CPU、內存等進行檢測,成功後根據啟動順序移動系統控制權 (2)MBR引導:根據硬盤第1個扇區中MBR(主引導記錄)的設

linux Nodejs安裝方式

環境 etc x64 問題解決 適合 profile 裏的 幹凈 user 在linux下安裝Nodejs有以下幾種方式。我使用的是CentOS 7版本,其實linux下安裝nodejs都大同小異。直接部署和通過nvm進行部署。 直接部署安裝wegt(如果你有源碼包,跳過該

Linux 使用rpm方式安裝最新mysql(5.7)步驟以及常見問題解決

linu x86_64 linux 很多 輸入密碼 alt oca mysq mon 第一步:下載rpm包 mysql官網下載:http://dev.mysql.com/downloads/mysql/ 但如果你的下載網速不好的話也可以點下面的鏈接下載自己想要的版本 ht

LINUX——關於ansible批量控制,批量命令及部署的使用

python開發 connect status art mirror set mkdir pin 通配 1.ansible簡介ansible是一款自動化運維工具,基於Python開發,集合了眾多運維工具(puppet,cfengine,chef,func,fabric)的優

Linux下yum方式安裝mysql 以及卸載mysql

gre pid lan mysql安裝 方式 紅色 option ide div 安裝 1.安裝rpm包 直接使用yum -y install 命令安裝mysql是無法安裝mysql的高級版本,需要先安裝帶有可用的mysql5系列社區版資源的rpm包,輸入如下命令進行安裝

linuxspi-nor Flash的操作----備份與還原norflash中的uboot

一、環境: Ubuntu 16.02 nor flash型號: spi nor flash S25FL256S ,Sector Size = 64 kbytes,  total size = 256M BIT = 32M Bytes 一、備份: dd if=/dev/mtd