久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 44038|回復: 19
收起左側

關于單片機按鍵處理的程序 100%保證可用

  [復制鏈接]
ID:89632 發表于 2015-9-8 15:05 | 顯示全部樓層 |閱讀模式
最近在網上看見一個不錯的單片機按鍵處理程序。所以就和大家分享一下。這個處理程序,對長按和短按都處理的比較細膩。大家在這塊不懂的都有可以參照這個程序模板進行修改,本人已經試驗,100%保證可用。


單片機按鍵處理技巧及編程方式

141347_eG4Q_266979.jpg
   在基于單片機為核心構成的應用系統中,用戶輸入是必不可少的一部分。輸入可以分很多種情況,譬如有的系統支持PS2鍵盤的接口,有的系統輸入是基于編碼器,有的系統輸入是基于串口或者USB或者其它輸入通道等等。在各種輸入途徑中,更常見的是,基于單個按鍵或者由單個鍵盤按照一定排列構成的矩陣鍵盤(行列鍵盤)。我們這一篇章主要討論的對象就是基于單個按鍵的程序設計,以及矩陣鍵盤的程序編寫。
    按鍵檢測的原理:   它們和我們的單片機系統的I/O口連接一般如下:
1005112315b141c31732153183.jpg
     對于單片機I/O內部有上拉電阻的微控制器而言,還可以省掉外部的那個上拉電阻。簡單分析一下按鍵檢測的原理。當按鍵沒有按下的時候,單片機I/O通過上拉電阻R接到VCC,我們在程序中讀取該I/O的電平的時候,其值為1(高電平);當按鍵S按下的時候,該I/O被短接到GND,在程序中讀取該I/O的電平的時候,其值為0(低電平) 。這樣,按鍵的按下與否,就和與該按鍵相連的I/O的電平的變化相對應起來。結論:我們在程序中通過檢測到該I/O口電平的變化與否,即可以知道按鍵是否被按下,從而做出相應的響應。一切看起來很美好,是這樣的嗎?
10051123156df583461880b461.jpg
     在我們通過上面的按鍵檢測原理得出上述的結論的時候,那就是現實中按鍵按下時候的電平變化狀態。我們的結論是基于理想的情況得出來的,而實際中,由于按鍵的彈片接觸的時候,并不是一接觸就緊緊的閉合,它還存在一定的抖動,盡管這個時間非常的短暫,但是對于我們執行時間以us為計算單位的微控制器來說,
它太漫長了。因而,實際的波形圖應該如下面這幅示意圖一樣。
1005112315627707675b3a6b43.jpg
這樣便存在這樣一個問題。假設我們的系統有這樣功能需求:在檢測到按鍵按下的時候,將某個I/O的狀態取反。由于這種抖動的存在,使得我們的微控制器誤以為是多次按鍵的按下,從而將某個I/O的狀態不斷取反,這并不是我們想要的效果,假如該I/O控制著系統中某個重要的執行的部件,那結果更不是我們所期待的。于是乎有人便提出了軟件消除抖動的思想,道理很簡單:抖動的時間長度是一定的,只要我們避開這段抖動時期,檢測穩定的時候的電平不就可以了嗎?聽起來確實不錯,而且實際應用起來效果也還可以。于是就像下面的偽代碼所描述的一樣。(假設按鍵按下時候,低電平有效)
If(0 ==io_KeyEnter)            //如果有鍵按下了
{
     Delayms(20);             //先延時20ms避開抖動時期
     If(0 ==io_KeyEnter)         //然后再檢測,如果還是檢測到有鍵按下
     {
         return KeyValue;           //是真的按下了,返回鍵值
     }
     else
{
     returnKEY_NULL         //是抖動,返回空的鍵值
}
     while(0 == io_KeyEnter) ;     //等待按鍵釋放
}
乍看上去,確實挺不錯,在實際的系統中,一般是不允許這么樣做的。為什么呢?首先,這里的Delayms(20) ,讓微控制器在這里白白等待了20 ms 的時間,啥也沒干,考慮我在《學會釋放CPU》一章中所提及的幾點,這是不可取的。其次while(0 ==io_KeyEnter) 所以合理的分配好微控制的處理時間,是編寫按鍵程序的基礎。原本是等待按鍵釋放,結果CPU就一直死死的盯住該按鍵,其它事情都不管了,那其它事情不干了嗎?
消除抖動有必要嗎?
    的確,軟件上的消抖確實可以保證按鍵的有效檢測。但是,這種消抖確實有必要嗎?抖動的出現即意味著按鍵已經按下,盡管這個電平還沒有穩定。所以只要我們檢測到按鍵按下,即可以返回鍵值,問題的關鍵是,在你執行完其它任務的時候,再次執行我們的按鍵任務的時候,抖動過程還沒有結束,這樣便有可能造成重復檢測。所以,如何在返回鍵值后,避免重復檢測,或者在按鍵一按下就執行功能函數,當功能函數的執行時間小于抖動時間時候,如何避免再次執行功能函數,就成為我們要考慮的問題了。所以消除抖動的目的是:防止按鍵一次按下,多次響應。
  基于狀態轉移的獨立按鍵程序設計
     本章所描述的按鍵程序要達到的目的:檢測按鍵按下,短按,長按,釋放。即通過按鍵的返回值我們可以獲取到如下的信息:按鍵按下(短按),按鍵長按,按鍵連_發,按鍵釋放。這樣的功能到底是如何實現的呢,今天就讓我們來剖析它的原理吧。下面讓我們來簡單的描繪一下它的狀態流程轉移圖。
141543_CBa1_266979.jpg
   下面對上面的流程圖進行簡要的分析。
首先按鍵程序進入初始狀態S1,在這個狀態下,檢測按鍵是否按下,如果有按下,則進入按鍵消抖狀態2,在下一次執行按鍵程序時候,直接由按鍵消抖狀態進入按鍵按下狀態3,在此狀態下檢測按鍵是否按下,如果沒有按鍵按下,則返回初始狀態S1,如果有則可以返回鍵值,同時進入長按狀態S4,在長按狀態下每次進入按鍵程序時候對按鍵時間計數,當計數值超過設定閾值時候,則表明長按事件發生,同時進入按鍵連_發狀態S5。如果按鍵鍵值為空鍵,則返回按鍵釋放狀態S6,否則繼續停留在本狀態。在按鍵連_發狀態下,如果按鍵鍵值為空鍵則返回按鍵釋放狀態S6,如果按鍵時間計數超過連_發閾值,則返回連_發按鍵值,清零時間計數后繼續停留在本狀態。 下面讓我們一起來編寫按鍵驅動程序吧。  下面是我使用的硬件的連接圖。
141603_qOd8_266979.jpg
   硬件連接很簡單,四個獨立按鍵分別接在P3^0------P3^3四個I/O上面。
因為51單片機I/O口內部結構的限制,在讀取外部引腳狀態的時候,需要向端口寫1.在51單片機復位后,不需要進行此操作也可以進行讀取外部引腳的操作。因此,在按鍵的端口沒有復用的情況下,可以省略此步驟。而對于其它一些真正雙向I/O口的單片機來說,將引腳設置成輸入狀態,是必不可少的一個步驟。
下面的程序代碼初始化引腳為輸入。
void KeyInit(void)
{
     io_key_1 = 1 ;
     io_key_2 = 1 ;
     io_key_3 = 1 ;
     io_key_4 = 1;            
}
根據按鍵硬件連接定義按鍵鍵值
#defineKEY_VALUE_1              0x0e
#define KEY_VALUE_2              0x0d
#defineKEY_VALUE_3                0x0b
#defineKEY_VALUE_4                0x07
#defineKEY_NULL                    0x0f
下面我們來編寫按鍵的硬件驅動程序。
根據第一章所描述的按鍵檢測原理,我們可以很容易的得出如下的代碼:
static uint8 KeyScan(void)
{
     if(io_key_1 == 0)return KEY_VALUE_1 ;
     if(io_key_2 == 0)return KEY_VALUE_2 ;
     if(io_key_3 == 0)return KEY_VALUE_3 ;
     if(io_key_4 == 0)return KEY_VALUE_4 ;
     return KEY_NULL ;
}
其中io_key_1等是我們按鍵端口的定義,如下所示:
sbit io_key_1 = P3^0 ;
sbit io_key_2 = P3^1 ;
sbit io_key_3 = P3^2 ;
sbit io_key_4 = P3^3 ;

KeyScan()作為底層按鍵的驅動程序,為上層按鍵掃描提供一個接口,這樣我們編寫的上層按鍵掃描函數可以幾乎不用修改就可以拿到我們的其它程序中去使用,使得程序復用性大大提高。同時,通過有意識的將與底層硬件連接緊密的程序和與硬件無關的代碼分開寫,使得程序結構層次清晰,可移植性也更好。對于單片機類的程序而言,能夠做到函數級別的代碼重用已經足夠了。
在編寫我們的上層按鍵掃描函數之前,需要先完成一些宏定義。
//定義長按鍵的TICK數,以及連_發間隔的TICK數
#define KEY_LONG_PERIOD         100
#define KEY_CONTINUE_PERIOD     25
//定義按鍵返回值狀態(按下,長按,連_發,釋放)
#defineKEY_DOWN                0x80
#defineKEY_LONG                    0x40
#defineKEY_CONTINUE            0x20
#define KEY_UP     0x10        //定義按鍵狀態
#defineKEY_STATE_INIT            0
#defineKEY_STATE_WOBBLE            1
#defineKEY_STATE_PRESS            2
#defineKEY_STATE_LONG            3
#define KEY_STATE_CONTINUE       4
#define KEY_STATE_RELEASE         5
接著我們開始編寫完整的上層按鍵掃描函數,按鍵的短按,長按,連按,釋放等等狀態的判斷均是在此函數中完成。對照狀態流程轉移圖,然后再看下面的函數代碼,可以更容易的去理解函數的執行流程。完整的函數代碼如下:
void GetKey(uint8 *pKeyValue)
{
     static uint8 s_u8KeyState = KEY_STATE_INIT ;
     static uint8 s_u8KeyTimeCount = 0 ;
     static uint8 s_u8LastKey = KEY_NULL ;   //保存按鍵釋放時候的鍵值
     uint8 KeyTemp = KEY_NULL ;

     KeyTemp = KeyScan();         //獲取鍵值
     switch(s_u8KeyState)
     {
         case KEY_STATE_INIT :
                {
                    if(KEY_NULL != (KeyTemp))
                    {
                        s_u8KeyState = KEY_STATE_WOBBLE ;
                    }
                }
         break ;
         case KEY_STATE_WOBBLE:       //消抖
                {
                    s_u8KeyState = KEY_STATE_PRESS ;     
                }
         break ;
         case KEY_STATE_PRESS :
                {
                    if(KEY_NULL != (KeyTemp))
                    {
                        s_u8LastKey = KeyTemp ; //保存鍵值,以便在釋放按鍵狀態返回鍵值
                        KeyTemp |= KEY_DOWN ;   //按鍵按下
                        s_u8KeyState = KEY_STATE_LONG ;
                    }
                    else
                    {
                        s_u8KeyState = KEY_STATE_INIT ;
                    }
                }
         break ;
         case KEY_STATE_LONG :
                {
                    if(KEY_NULL != (KeyTemp))
                    {
                        if(++s_u8KeyTimeCount > KEY_LONG_PERIOD)
                        {
                            s_u8KeyTimeCount = 0 ;
                            KeyTemp |= KEY_LONG ;   //長按鍵事件發生
                            s_u8KeyState = KEY_STATE_CONTINUE ;
                        }
                    }
                    else
                    {
                        s_u8KeyState = KEY_STATE_RELEASE ;
                    }
                }
         break ;
         case KEY_STATE_CONTINUE :
                {
                    if(KEY_NULL != (KeyTemp))
                    {
                        if(++s_u8KeyTimeCount > KEY_CONTINUE_PERIOD)
                        {
                            s_u8KeyTimeCount = 0 ;
                            KeyTemp |= KEY_CONTINUE ;
                        }
                    }
                    else
                    {
                        s_u8KeyState = KEY_STATE_RELEASE ;
                    }
                }
         break ;
         case KEY_STATE_RELEASE :
                {
                    s_u8LastKey |= KEY_UP ;
                    KeyTemp = s_u8LastKey ;
                    s_u8KeyState = KEY_STATE_INIT ;
                }
         break ;
         default : break ;
     }
     *pKeyValue = KeyTemp ; //返回鍵值   
}
關于這個函數內部的細節我并不打算花過多筆墨去講解。對照著按鍵狀態流程轉移圖,然后去看程序代碼,你會發現其實思路非常清晰。最能讓人理解透徹的,莫非就是將整個程序自己看懂,然后想象為什么這個地方要這樣寫,抱著思考的態度去閱讀程序,你會發現自己的程序水平會慢慢的提高。不管怎么樣,這樣的一個程序已經完成了本章開始時候要求的功能:按下,長按,連按,釋放。事實上,如果掌握了這種基于狀態轉移的思想,你會發現要求實現其它按鍵功能,譬如,多鍵按下,功能鍵等等,亦相當簡單,在下一章,我們就去實現它。
在主程序中我編寫了這樣的一段代碼,來演示我實現的按鍵功能。
void main(void)
{     
     uint8 KeyValue = KEY_NULL;
     uint8 temp = 0 ;
       LED_CS11 = 1 ; //流水燈輸出允許
     LED_SEG = 0 ;
     LED_DIG = 0 ;
     Timer0Init() ;
     KeyInit() ;
     EA = 1 ;
     while(1)
     {
         Timer0MainLoop() ;
         KeyMainLoop(&KeyValue) ;
         
         if(KeyValue == (KEY_VALUE_1 |KEY_DOWN)) P0 = ~1 ;
         if(KeyValue == (KEY_VALUE_1 |KEY_LONG)) P0 = ~2 ;
         if(KeyValue == (KEY_VALUE_1 |KEY_CONTINUE)) { P0 ^= 0xf0;}
         if(KeyValue == (KEY_VALUE_1 |KEY_UP)) P0 = 0xa5 ;
     }
}
     按住第一個鍵,可以清晰的看到P0口所接的LED的狀態的變化。當按鍵按下時候,第一個LED燈亮,等待2 S后第二個LED亮,第一個熄滅,表示長按事件發生。再過500 ms 第5~8個LED閃爍,表示連按事件發生。當釋放按鍵時候,P0口所接的LED的狀態為:   滅亮滅亮亮滅亮滅,這也正是P0 =0xa5這條語句的功能。

評分

參與人數 1黑幣 +4 收起 理由
八月初 + 4 贊一個!

查看全部評分

回復

使用道具 舉報

ID:89632 發表于 2015-9-8 18:01 | 顯示全部樓層
真的很有用啊!
回復

使用道具 舉報

ID:98238 發表于 2015-12-10 11:30 來自觸屏版 | 顯示全部樓層
為嘛我用就編譯不通過
回復

使用道具 舉報

ID:82098 發表于 2015-12-28 13:19 | 顯示全部樓層
hboy 發表于 2015-12-10 11:30
為嘛我用就編譯不通過

把KeyMainLoop(&KeyValue) ;換成GetKey(&KeyValue);即可。
回復

使用道具 舉報

ID:304849 發表于 2018-5-16 13:43 | 顯示全部樓層
匯編程序,怎么利用鍵值跳轉到子程序呢?
回復

使用道具 舉報

ID:323951 發表于 2018-5-30 15:18 | 顯示全部樓層
這個看起來不是很理解,可以發一下樓主自己改過以后的源碼嗎?
回復

使用道具 舉報

ID:207882 發表于 2018-6-20 19:49 | 顯示全部樓層
如果執行程序的時間小于消抖的時間那不是還是進入了case KEY_STATE_PRESS ????不需要設置消抖時間?
回復

使用道具 舉報

ID:66287 發表于 2018-7-23 16:23 | 顯示全部樓層
實際上應該定時10mS,在定時中斷中執行void GetKey(uint8 *pKeyValue)函數吧,
回復

使用道具 舉報

ID:66287 發表于 2018-7-24 10:21 | 顯示全部樓層
/*********************************************
工程名:狀態機按鍵處理
作者:


*********************************************/
//#include <reg51.h>
#include <STC15F2K60S2.H>
typedef unsigned char uint8;
typedef unsigned int  uint16;

#define KEY_VALUE_1   0x0e
#define KEY_VALUE_2   0x0d
#define KEY_VALUE_3   0x0b
#define KEY_VALUE_4   0x07
#define KEY_NULL      0x0f

//定義長按鍵的TICK數,以及連_發間隔的TICK數
#define KEY_LONG_PERIOD         100
#define KEY_CONTINUE_PERIOD     25
//定義按鍵返回值狀態(按下,長按,連_發,釋放)
#define KEY_DOWN                0x80
#define KEY_LONG                0x40
#define KEY_CONTINUE            0x20
#define KEY_UP     0x10        //定義按鍵狀態
#define KEY_STATE_INIT           0
#define KEY_STATE_WOBBLE         1
#define KEY_STATE_PRESS          2
#define KEY_STATE_LONG           3
#define KEY_STATE_CONTINUE       4
#define KEY_STATE_RELEASE        5

sbit io_key_1 = P1^1 ;
sbit io_key_2 = P1^2 ;
sbit io_key_3 = P1^3 ;
sbit io_key_4 = P1^4 ;
void Timer0_Init();

void KeyInit(void)
{
     io_key_1 = 1 ;
     io_key_2 = 1 ;
     io_key_3 = 1 ;
     io_key_4 = 1;            
}


static uint8 KeyScan(void)
{
     if(io_key_1 == 0) return KEY_VALUE_1;
     if(io_key_2 == 0) return KEY_VALUE_2;
     if(io_key_3 == 0) return KEY_VALUE_3;
     if(io_key_4 == 0) return KEY_VALUE_4;
     return KEY_NULL ;
}

void GetKey(uint8 *pKeyValue)
{
     static uint8 s_u8KeyState = KEY_STATE_INIT;
     static uint8 s_u8KeyTimeCount = 0 ;
     static uint8 s_u8LastKey = KEY_NULL ;   //保存按鍵釋放時候的鍵值
     uint8 KeyTemp = KEY_NULL ;

     KeyTemp = KeyScan();         //獲取鍵值
     switch(s_u8KeyState)
     {
         case KEY_STATE_INIT :
                {
                    if(KEY_NULL != (KeyTemp))
                    {
                        s_u8KeyState = KEY_STATE_WOBBLE ;
                    }
                }
         break ;
         case KEY_STATE_WOBBLE:       //消抖
                {
                    s_u8KeyState = KEY_STATE_PRESS ;     
                }
         break ;
         case KEY_STATE_PRESS :
                {
                    if(KEY_NULL != (KeyTemp))
                    {
                        s_u8LastKey = KeyTemp ; //保存鍵值,以便在釋放按鍵狀態返回鍵值
                        KeyTemp |= KEY_DOWN ;   //按鍵按下
                        s_u8KeyState = KEY_STATE_LONG ;
                    }
                    else
                    {
                        s_u8KeyState = KEY_STATE_INIT ;
                    }
                }
         break ;
         case KEY_STATE_LONG :
                {
                    if(KEY_NULL != (KeyTemp))
                    {
                        if(++s_u8KeyTimeCount > KEY_LONG_PERIOD)
                        {
                            s_u8KeyTimeCount = 0 ;
                            KeyTemp |= KEY_LONG ;   //長按鍵事件發生
                            s_u8KeyState = KEY_STATE_CONTINUE ;
                        }
                    }
                    else
                    {
                        s_u8KeyState = KEY_STATE_RELEASE ;
                    }
                }
         break ;
         case KEY_STATE_CONTINUE :
                {
                    if(KEY_NULL != (KeyTemp))
                    {
                        if(++s_u8KeyTimeCount > KEY_CONTINUE_PERIOD)
                        {
                            s_u8KeyTimeCount = 0 ;
                            KeyTemp |= KEY_CONTINUE ;
                        }
                    }
                    else
                    {
                        s_u8KeyState = KEY_STATE_RELEASE ;
                    }
                }
         break ;
         case KEY_STATE_RELEASE :
                {
                    s_u8LastKey |= KEY_UP ;
                    KeyTemp = s_u8LastKey ;
                    s_u8KeyState = KEY_STATE_INIT ;
                }
         break ;
         default : break ;
     }
     *pKeyValue = KeyTemp ; //返回鍵值   
}

uint8 KeyValue = KEY_NULL;
void main(void)
{     
     
     uint8 temp = 0 ;
     
     Timer0_Init();  
     KeyInit() ;
     EA = 1 ;
     while(1)
     {
         //Timer0MainLoop() ;
         //KeyMainLoop(&KeyValue) ;
         
     }
}

void Timer0() interrupt 1 //定時器中斷
        {                    
                          //16位自動重裝模式,無需重賦初值
     GetKey(&KeyValue);
         if(KeyValue == (KEY_VALUE_1|KEY_DOWN)) P0 = ~1 ;
         if(KeyValue == (KEY_VALUE_1 |KEY_LONG)) P0 = ~2 ;
         if(KeyValue == (KEY_VALUE_1 |KEY_CONTINUE)) { P0 ^= 0xf0;}
         if(KeyValue == (KEY_VALUE_1 |KEY_UP)) P0 = 0xa5 ;         
   
        }
       
void Timer0_Init()  //定時器初始化
   {
         AUXR &= 0x7F;        //定時器時鐘12T模式,10mS
         TMOD &= 0xF0;        //設置定時器模式
         TH0 = 0xDC;        //設置定時初值
         TL0 = 0x00;        //設置定時初值
         //TF0 = 0;                //清除TF0標志
         TR0 = 1;                //定時器0開始計時
         ET0 =1;
         EA = 1;
  }
回復

使用道具 舉報

ID:20672 發表于 2018-8-2 21:24 | 顯示全部樓層
不錯!!!
回復

使用道具 舉報

ID:377382 發表于 2018-8-4 13:36 | 顯示全部樓層
不錯不錯
回復

使用道具 舉報

ID:330092 發表于 2018-8-30 03:50 來自觸屏版 | 顯示全部樓層
學習了,謝謝分享
回復

使用道具 舉報

ID:718536 發表于 2020-4-17 17:58 | 顯示全部樓層
樓主大大,如果按鍵進水或者卡死了,導致所有程序運行錯誤。要如何屏蔽掉這個卡死或者進水的按鍵呢?
回復

使用道具 舉報

ID:711507 發表于 2020-5-8 23:53 | 顯示全部樓層
學習學習
回復

使用道具 舉報

ID:662183 發表于 2020-5-9 00:38 | 顯示全部樓層
打卡學習,小白報道,感謝分享
回復

使用道具 舉報

ID:374361 發表于 2020-5-9 08:58 | 顯示全部樓層
樓主有點東西
回復

使用道具 舉報

ID:771858 發表于 2020-6-7 14:01 | 顯示全部樓層
感謝樓主
回復

使用道具 舉報

ID:763926 發表于 2020-6-7 16:20 | 顯示全部樓層
很管用的東西啊
回復

使用道具 舉報

ID:173256 發表于 2020-6-8 22:35 | 顯示全部樓層
單個按鍵組合好像有沖突  比如KYE1要長按實現退出功能  但長按動作前面肯定會經過短按(短按—長按—連擊)這時短按就會被觸發了
回復

使用道具 舉報

ID:771727 發表于 2020-6-9 03:51 來自觸屏版 | 顯示全部樓層
多個按鍵同時按住,會不會發生沖突。
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 色影视| 91成人午夜性a一级毛片 | 精品免费av | 91精品麻豆日日躁夜夜躁 | 国产精品久久 | 国产目拍亚洲精品99久久精品 | 欧美日韩在线免费 | 欧美精品a∨在线观看不卡 国产精品久久国产精品 | 狠狠夜夜 | 成人区精品 | 国产在线一区二区 | 日韩在线免费视频 | 色中文在线 | 在线观看成人 | 综合国产第二页 | 黄色精品| 国产免费一区二区三区免费视频 | 丝袜毛片 | 欧美久久一级特黄毛片 | 久久久久久久av | 性欧美精品一区二区三区在线播放 | 操操日| 欧美日韩一区二区在线 | 999国产精品视频 | 久久综合伊人一区二区三 | 91伊人网 | 中文字幕成人在线 | 久热久| 亚洲免费在线视频 | 午夜精品久久久久久久久久久久久 | 精品久久久久久久久久久久久久久久久 | 中文二区 | 欧美三级在线 | 97精品超碰一区二区三区 | 精品国产一区二区三区久久久四川 | 久久亚洲一区二区三区四区 | 久久久久国产精品一区三寸 | 亚洲人成人一区二区在线观看 | 九九久久精品 | 欧美日韩在线精品 | 在线观看中文字幕 |