• <em id="6vhwh"><rt id="6vhwh"></rt></em>

    <style id="6vhwh"></style>

    <style id="6vhwh"></style>
    1. <style id="6vhwh"></style>
        <sub id="6vhwh"><p id="6vhwh"></p></sub>
        <p id="6vhwh"></p>
          1. 国产亚洲欧洲av综合一区二区三区 ,色爱综合另类图片av,亚洲av免费成人在线,久久热在线视频精品视频,成在人线av无码免费,国产精品一区二区久久毛片,亚洲精品成人片在线观看精品字幕 ,久久亚洲精品成人av秋霞

            單片機串口實現字符串命令解析---使用函數指針(類似哈希表)

            更新時間:2023-12-09 21:29:51 閱讀: 評論:0

            2023年12月9日發(作者:屈平)

            -

            單片機串口實現字符串命令解析---使用函數指針(類似哈希表)

            單片機串口實現字符串命令解析---使用函數指針(類似哈希

            表)

            通常情況下串口通信用的大多數都是用十六進制數據來傳輸指令,比如最常見的modbus的通信,如讀保持寄存器指令:01 03 00 00

            00 01 84 0A,這種十六進制的指令在這里就不討論了。想要詳細了解可以看往期的文章。串口相關文章鏈接如下:

            有時候串口也需要通過字符串命令來實現某些功能, 如果使用AT指令的話,通信就是以字符串格式進行。

            有時候自己做產品的時候,需要通過指令來控制設備,使用字符串的話更方便。比如發送一條開LED燈的指令"led on",關燈指令"led

            off"。這樣通過字符串命令來控制設備,比直接通過16進制數字控制設備看起來更方便直觀。比如今天要實現的功能。

            那么如何解析字符串命令呢?通常第一個想法就是,將讀取到的字符串和程序中存儲的字符串進行比較,如果字符串相等的話,就調

            用某個函數去執行相應的功能。這種辦法是最簡單也就是最容易實現的。通常用一個switch語句就可以搞定,switch的每個分支代表一個指

            令,然后去調用相關的函數執行。相關代碼可以參考 這篇文章。

            還有沒有其他方法呢?如果了解數據結構的話就可以想到哈希表。可以使用類似于哈希表的原理來實現。在一個表中每一個鍵就對應

            一個值。通過鍵就可以找到值。就好像學生的學號和姓名一樣,將學號和姓名連接起來,放在一起,通過學號就可以找到姓名。將字符串和

            要執行的函數對應起來,放在哈希表中。在表中找到字符串后,就可以直接找到對應執行的函數。增加或者刪除串口命令時,只需要操作這

            張表就行了。就不用每次指令或者函數名發生變化時,都要去switch語句中修改。

            在單片機中沒有類似哈希表的這種數據結構,那要怎么實現呢?于是想到了用結構體去實現,在一個結構體里面有兩個元素,一個是字

            符串,一個是需要執行的函數。這樣字符串和函數就被綁定在了一起。在初始化命令的時候,將字符串和對應的函數,都寫在一個結構體

            中。那么只有找到了這個字符串,就自然會找到對應的執行函數。這樣就實現了類似哈希表的功能。

            首先定義一個結構體,用來存儲字符串命令和對應功能的函數

            typedef void ( *functions )( void ); // 函數指針類型

            //命令結構體

            typedef struct

            {

            char cmd_name[MAX_CMD_LENGTH + 1]; //字符數組存儲字符串命令

            functions cmd_functions; //通過指針傳遞函數名

            }CMD_Name_Func;

            在結構體里面有兩個元素,一個字符數組用來存儲字符串命令。一個指針用來存儲函數的入口地址,該函數沒有返回值,沒有參數。

            如果每個命令都對應一個這樣的結構體,命令比較多的時候,如何能方便快速去找到這些結構體呢?最簡單的就是將這些結構體存儲在

            數組中,這樣數組的每一個元素就是一個結構體,通過數組的下標就能很方便的訪問到每一個結構體。

            // 命令列表結構體類型

            typedef struct

            {

            CMD_Name_Func cmdNames[MAX_CMDS_COUNT]; //結構體數組字符串命令 和對應函數

            int num; //統計結構體個數

            }CMD_LIST;

            在另一個結構體中用一個數組來存儲命令結構體,每個結構體的數組元素都代表一個字符串命令和對應的函數,同時用一個計數器來

            統計,共存儲了多少條命令。當串口接收到一個字符串后,就遍歷一次結構體數組,對比里面是否有相同的字符串,如果有,就執行該字符

            串對應的函數。通過這種方式來處理字符串和命令的話,只需要在初始化的時候將字符串命令添加到這個列表中就可以了,而程序的其他地

            方就不需要修改了。

            要實現上面所說的功能,還需要再實現兩個函數,一個函數實現將命令添加到結構體,一個函數實現遍歷結構體數組,尋找匹配的字符

            串并執行相應的函數。

            static CMD_LIST command_list = {NULL, 0}; // 全局命令列表,保存已注冊命令集合

            //注冊命令

            void register_cmds( CMD_Name_Func reg_cmds[], int length )

            {

            int i;

            if ( length > MAX_CMDS_COUNT )

            {

            return;

            }

            for ( i = 0; i < length; i++ )

            {

            if ( command_ < MAX_CMDS_COUNT ) // 命令列表未滿

            {

            strcpy( command_es[command_].cmd_name, reg_cmds[i].cmd_name ); //將字符串命令拷貝到列表中

            command_es[command_].cmd_functions = reg_cmds[i].cmd_functions; //將命令對應的函數存儲在列表中

            command_++; // 數量值默認為0,每添加一個命令,數量加1.

            }

            }

            }

            這個命令注冊函數實現將命令結構體添加到命令列表中。用戶新增加一條指令,就調用一次注冊函數,將字符串命令添加到命令列表

            字符串中,同時將字符串命令對應的函數也添加到列表函數中。如果有新增加的子模塊,只需要在子模塊中調用一次注冊命令,就完成了字

            符串命令的增加。其他代碼不需要修改。

            比如現在led模塊需要添加命令

            //注冊led命令

            void led_register( void )

            {

            //初始化 字符串命令和對應函數

            CMD_Name_Func led_cmds[] =

            {

            {"led1 on", led1_on}, // 添加字符串命令 和 對應的函數

            {"led1 off", led1_off},

            {"led2 on", led2_on},

            {"led2 off", led2_off},

            {"led3 on", led3_on},

            {"led3 off", led3_off}

            };

            //將命令添加到列表中

            register_cmds( led_cmds, ARRAY_SIZE( led_cmds ) ); // ARRAY_SIZE 用來計算結構體數組中,數組的個數。個數=結構體總長度/單個數組長度

            }

            在led模塊中創建命令結構體,并將創建的結構體添加到命令列表中。通過代碼可以看到增加了6條關于led的字符串命令,每個字符串

            命令都對應一個需要執行的函數。

            假如現在還需要添加一個蜂鳴器的子模塊,那么就可以直接在蜂鳴器的子文件內直接注冊命令。

            //注冊 beep命令

            void beep_register( void )

            {

            //初始化 字符串命令和對應函數

            CMD_Name_Func beep_cmds[] =

            {

            {"beep on", beep_on},

            {"beep off", beep_off}

            };

            //將命令添加到列表中

            register_cmds( beep_cmds, ARRAY_SIZE( beep_cmds ) ); // ARRAY_SIZE 用來計算結構體數組中,數組的個數。個數=結構體總長度/單個數組長度

            }

            在蜂鳴器的模塊中添加了兩條命令,然后通過注冊函數將蜂鳴器相關的命令就添加到了命令列表中。

            通過一個注冊命令就實現了命令的添加,而不需要修改其他的代碼,實現了代碼的"

            高內聚

            低耦合"。

            上面實現了命令的注冊,還需要

            實現一個命令的解析。

            void match_cmd( char *cmdStr )

            {

            int i;

            if ( strlen( cmdStr ) > MAX_CMD_LENGTH )

            {

            return;

            }

            for ( i = 0; i < command_; i++ ) // 遍歷命令列表

            {

            {

            command_es[i].cmd_functions();

            }

            }

            }

            if ( strcmp( command_es[i].cmd_name, cmdStr ) == 0 ) //比較接收到的命令字符串 和 列表中存儲的命令字符串是否相等,如果相等就調用命令字

            每次注冊命令的時候,會有個計數器統計注冊命令的數量。在命令解析函數中就循環去判斷接收到的命令是否在命令列表中,如果命

            令列表中存在相等的字符串,就去執行對應的函數。而命令解析函數是不關心接收到的具體字符串命令是什么,需要執行的相應函數是什

            么。所以每次命令添加或者刪除的時候,對命令解析和函數沒有任何的影響。

            這個命令解析函數比較類似于設計模式中的"工廠模式",所謂的工廠模式百度百科解釋如下:

            如果不了解面向對象編程的話,可能上面的這個解釋看的不太明白。舉個簡單的例子就是,工廠生產東西的時候不關心具體生產的是

            什么東西,客戶將需要生產東西的大小尺寸顏色特征告訴工廠,工廠就按照要求去執行。比如客戶要求做一個直徑5cm的玻璃透明圓柱體,

            圓柱體只需要底面,不需要頂面。工廠就按照客戶的要求去生產這樣一個東西,雖然這個東西按照一般經驗來看就是一個透明的玻璃杯。但

            是工廠不用關心這個東西的名稱和用途,只需要按照客戶的要求去實現。

            而上面的命令解析函數,實際上也就是一個工廠,客戶將一個字符串和一個函數送來。工廠就按照指定的字符串去執行指定函數。而工

            廠本身不去關心這個字符串具體是什么?函數具體是什么?這樣的話,只要客戶在命令列表中注冊了字符串命令和相應的執行動作。命令解

            析函數就可以實現想要的功能。

            通過這種模式去解析字符串命令的話,就可以移植到到任何需要命令解析的單片機上,而不用去關心單片機的IO、中斷、寄存器等等其

            他東西。下面就貼出完整的代碼

            命令解析頭文件 cmd.h :

            #ifndef __CMD_H_

            #define __CMD_H_

            #include "iostm8s103F3.h"

            #define ARRAY_SIZE(x) (sizeof(x) / (sizeof((x)[0]))) //用來計算結構體數組中,數組的個數。個數=結構體總長度/單個數組長度

            #define MAX_CMD_LENGTH 15 // 最大命令名長度

            #define MAX_CMDS_COUNT 20 // 最大命令數

            typedef void ( *functions )( void ); // 命令操作函數指針類型

            //命令結構體類型 用于存儲字符串命令和對應函數

            typedef struct

            {

            char cmd_name[MAX_CMD_LENGTH + 1]; // 命令名 字符串末尾系統會自動添加結束符'/0' sizeof("name")大小為 10

            functions cmd_functions; // 命令操作函數 sizeof(func) 大小為 2

            }CMD_Name_Func;

            // 命令列表結構體類型 用于存儲字符串命令數組

            typedef struct

            {

            CMD_Name_Func cmdNames[MAX_CMDS_COUNT]; // 存儲字符串命令 和對應函數

            int num; // 命令數組個數

            }CMD_LIST;

            void register_cmds( CMD_Name_Func reg_cmds[], int num );

            void match_cmd( char *str );

            #endif

            命令解析代碼cmd.c

            #include

            #include "cmd.h"

            #include "uart.h"

            static CMD_LIST command_list = {NULL, 0}; // 全局命令列表,保存已注冊命令集合

            /*

            * 函數介紹: 命令注冊函數 每新添加一個命令,就添加到命令列表中

            * 輸入參數: reg_cmds 待注冊命令結構體數組

            * length 數組個數

            * 輸出參數: 無

            * 返回值 : 無

            * 備 注: length 不得超過 MAX_CMDS_COUNT

            */

            void register_cmds( CMD_Name_Func reg_cmds[], int length )

            {

            int i;

            if ( length > MAX_CMDS_COUNT )

            {

            return;

            }

            for ( i = 0; i < length; i++ )

            {

            if ( command_ < MAX_CMDS_COUNT ) // 命令列表未滿

            {

            strcpy( command_es[command_].cmd_name, reg_cmds[i].cmd_name ); //將字符串命令拷貝到列表中

            command_es[command_].cmd_functions = reg_cmds[i].cmd_functions; //將命令對應的函數存儲在列表中

            command_++; // 數量值默認為0,每添加一個命令,數量加1.

            }

            }

            }

            /*

            * 函數介紹: 命令匹配執行函數

            * 輸入參數: cmdStr 待匹配命令字符串

            * 輸出參數: 無

            * 返回值 : 無

            * 備 注: cmdStr 長度不得超過 MAX_CMD_NAME_LENGTH

            */

            void match_cmd( char *cmdStr )

            {

            int i;

            if ( strlen( cmdStr ) > MAX_CMD_LENGTH )

            {

            return;

            }

            for ( i = 0; i < command_; i++ ) // 遍歷命令列表

            {

            {

            command_es[i].cmd_functions();

            }

            }

            }

            if ( strcmp( command_es[i].cmd_name, cmdStr ) == 0 ) //比較接收到的命令字符串 和 列表中存儲的命令字符串是否相等,如果相

            ed頭文件led.h

            #ifndef __LED_H

            #define __LED_H

            #include "iostm8s103F3.h"

            #define LED1 PD_ODR_ODR4 //藍

            #define LED2 PA_ODR_ODR1 //綠

            #define LED3 PA_ODR_ODR2 //紅

            #define BLUE {LED1=1;LED2=0;LED3=0;}

            #define GREEN {LED1=0;LED2=1;LED3=0;}

            #define RED {LED1=0;LED2=0;LED3=1;}

            #define CYAN {LED1=1;LED2=1;LED3=0;} //青

            #define PURPLE {LED1=1;LED2=0;LED3=1;} //紫

            #define YELLOW {LED1=0;LED2=1;LED3=1;} //黃

            #define ONALL {LED2=1;LED3=1;LED1=1;}

            #define OFFALL {LED1=0;LED2=0;LED3=0;}

            void LED_GPIO_Init( void );

            void led1_on(void);

            void led1_off(void);

            void led2_on(void);

            void led2_off(void);

            void led3_on(void);

            void led3_off(void);

            void led_register(void);

            #endif

            led.c

            #include "led.h"

            #include "cmd.h"

            //3色LED

            void LED_GPIO_Init( void )

            {

            PD_DDR |= ( 1 << 4 ); //PD4 輸出 led

            PD_CR1 |= ( 1 << 4 ); //PD4 推挽輸出

            PD_CR2 |= ( 1 << 4 );

            PA_DDR |= ( 1 << 1 ); //PA1 輸出 led

            PA_CR1 |= ( 1 << 1 ); //PA1 推挽輸出

            PA_CR2 |= ( 1 << 1 );

            PA_DDR |= ( 1 << 2 ); //PA2 輸出 led

            PA_CR1 |= ( 1 << 2 ); //PA2 推挽輸出

            PA_CR2 |= ( 1 << 2 );

            }

            void led1_on( void )

            {

            LED1 = 1;

            }

            void led1_off( void )

            {

            LED1 = 0;

            }

            void led2_on( void )

            {

            LED2 = 1;

            }

            void led2_off( void )

            {

            LED2 = 0;

            LED2 = 0;

            }

            void led3_on( void )

            {

            LED3 = 1;

            }

            void led3_off( void )

            {

            LED3 = 0;

            }

            //注冊led命令

            void led_register( void )

            {

            //初始化 字符串命令和對應函數

            CMD_Name_Func led_cmds[] =

            {

            {"led1 on", led1_on}, // 一個結構體變量大小為 12 (字符串大小10 + 函數名大小2)

            {"led1 off", led1_off}, // 一個結構體變量大小為 12

            {"led2 on", led2_on},

            {"led2 off", led2_off},

            {"led3 on", led3_on},

            {"led3 off", led3_off}

            };

            //將命令添加到列表中

            register_cmds( led_cmds, ARRAY_SIZE( led_cmds ) ); // ARRAY_SIZE 用來計算結構體數組中,數組的個數。個數=結構體總長度/單個數組長度

            }

            beep.h

            #ifndef __BEEP_H

            #define __BEEP_H

            #include "iostm8s103F3.h"

            #define BEEP PB_ODR_ODR4

            void BEEP_GPIO_Init( void );

            void beep_register( void );

            #endif

            beep.c

            #include "beep.h"

            #include "cmd.h"

            void BEEP_GPIO_Init( void )

            {

            PB_DDR |= ( 1 << 4 ); //PB4

            PB_CR1 |= ( 1 << 4 ); //PB4 推挽輸出

            PB_CR2 |= ( 1 << 4 );

            }

            void beep_on( void )

            {

            BEEP = 1;

            }

            void beep_off( void )

            {

            BEEP = 0;

            }

            //注冊 beep命令

            void beep_register( void )

            {

            //初始化 字符串命令和對應函數

            CMD_Name_Func beep_cmds[] =

            {

            {"beep on", beep_on}, // 一個結構體變量大小為 12 (字符串大小10 + 函數名大小2)

            {"beep off", beep_off} // 一個結構體變量大小為 12

            };

            //將命令添加到列表中

            register_cmds( beep_cmds, ARRAY_SIZE( beep_cmds ) ); // ARRAY_SIZE 用來計算結構體數組中,數組的個數。個數=結構體總長度/單個數組長度

            }

            uart.h

            #ifndef __UART_H

            #define __UART_H

            #include "iostm8s103F3.h"

            extern char uartRecStr[20]; //串口接收字符串存儲

            extern unsigned char uartRecCnt; //接收數據個數

            extern _Bool rec_ok;

            void Uart1_IO_Init( void );

            void Uart1_Init( unsigned int baudrate );

            void SendChar( unsigned char dat );

            void SendString( unsigned char* s );

            #endif

            uart.c

            #include "uart.h"

            #include "main.h"

            char uartRecStr[20] = {0}; //串口接收字符串存儲

            unsigned char uartRecCnt = 0; //接收數據個數

            _Bool rec_ok = 0; //接收完成標志位

            //在Library Options中將Printf formatter改成Large

            //重新定向putchar函數,使支持printf函數

            int putchar( int ch )

            {

            while( !( UART1_SR & 0X80 ) ); //循環發送,直到發送完畢

            while( !( UART1_SR & 0X80 ) ); //循環發送,直到發送完畢

            UART1_DR = ( u8 ) ch;

            return ch;

            }

            //串口只用發送口,不用接收口

            void Uart1_IO_Init( void )

            {

            PD_DDR |= ( 1 << 5 ); //輸出模式 TXD

            PD_CR1 |= ( 1 << 5 ); //推挽輸出

            PD_DDR &= ~( 1 << 6 ); //輸入模式 RXD

            PD_CR1 &= ~( 1 << 6 ); //浮空輸入

            }

            //波特率最大可以設置為38400

            void Uart1_Init( unsigned int baudrate )

            {

            unsigned int baud;

            baud = 16000000 / baudrate;

            Uart1_IO_Init();

            UART1_CR1 = 0; //禁止發送和接收

            UART1_CR2 = 0; //8 bit

            UART1_CR3 = 0; //1 stop

            UART1_BRR2 = ( unsigned char )( ( baud & 0xf000 ) >> 8 ) | ( ( unsigned char )( baud & 0x000f ) );

            UART1_BRR1 = ( ( unsigned char )( ( baud & 0x0ff0 ) >> 4 ) );

            UART1_CR2_ = 1; //接收使能

            UART1_CR2_ = 1; //發送使能

            UART1_CR2_ = 1; //接收中斷使能

            }

            //阻塞式發送函數

            void SendChar( unsigned char dat )

            {

            while( ( UART1_SR & 0x80 ) == 0x00 ); //發送數據寄存器空

            UART1_DR = dat;

            }

            //發送字符串

            void SendString( unsigned char* s )

            {

            while( 0 != *s )

            {

            SendChar( *s );

            s++;

            }

            }

            //接收中斷函數 中斷號18

            #pragma vector = 20 // IAR中的中斷號,要在STVD中的中斷號上加2

            __interrupt void UART1_Handle( void )

            {

            unsigned char res = 0;

            res = UART1_DR;

            UART1_SR &= ~( 1 << 5 ); //RXNE 清零

            //SendChar(res); //test

            if( ( res != 'r' ) && ( res != 'n' ) ) //字符串以回車換行符結束

            {

            uartRecStr[uartRecCnt++] = res;

            }

            el

            {

            rec_ok = 1; //置接收完成標志

            }

            }

            主程序main.c

            /*

            *函數功能,實現串口字符串命令解析

            */

            #include "iostm8s103F3.h"

            #include "main.h"

            #include "stdio.h"

            #include "delay.h"

            #include "stdlib.h"

            #include "uart.h"

            #include "string.h"

            #include "cmd.h"

            #include "led.h"

            #include "beep.h"

            void SysClkInit( void )

            {

            CLK_SWR = 0xe1; //HSI為主時鐘源 16MHz CPU時鐘頻率

            CLK_CKDIVR = 0x00; //CPU時鐘0分頻,系統時鐘0分頻

            }

            void main( void )

            {

            __asm( "sim" ); //禁止中斷

            SysClkInit();

            delay_init( 16 );

            LED_GPIO_Init();

            BEEP_GPIO_Init();

            Uart1_Init( 9600 );

            __asm( "rim" ); //開啟中斷

            //注冊命令

            led_register();

            beep_register();

            while( 1 )

            {

            if( rec_ok )

            {

            rec_ok = 0;

            uartRecCnt = 0;

            SendString( uartRecStr );

            SendString( "rn" );

            match_cmd( uartRecStr );

            memt( uartRecStr, 0, sizeof( uartRecStr ) ); //清空備份數組 需要添加頭文件 string.h

            }

            }

            }

            在主函數中檢測串口是否接收到了字符串,串口接收字符串以回車換行結束。若串口接收到了字符串,將接收到的字符串通過串口發

            送出去,并檢查一次接收到的字符串是否和命令列表中的字符串匹配?如果接收到的字符串和命令列表中的字符串匹配,就中執行一次相關

            的函數。最后將串口緩沖區清空,繼續等待下一次命令。

            測試效果如下

            這樣通過字符串命令就可以直接控制LED燈和蜂鳴器了,如果下次需要增加一個繼電器控制模塊,就只需要編寫繼電器模塊的c代碼,

            在進入main函數時,注冊繼電器命令。繼電器的模塊就會被添加進來了。而不需要修改其他的模塊和串口任何代碼。

            通過上面的例子可以看到這種模式是相當的好用,難道這種方法就沒有一點缺點嗎?如果在單片機上用的話,這種模式有一個致命的

            缺點,那就是太占內存了。

            首先看一張圖

            這個是新建了4個led命令結構體,可以看出來每個命令的字符串數組長度都是16,函數指針默認為int型,占兩個字節。一個結構體總

            共占18個字節。 4個led命令占4*18=72個字節的空間。雖然led命令最長的字符串只占8個字節,但是系統依然會分配16個字節空間。

            這個是命令列表,默認的最多命令數是20個,系統初始化的時候就一次性將這20個命令的空間分配好了。雖然代碼中只用了4個命

            令,但是系統空間的占用卻一點也沒有少。

            這樣的話對于空間比較小的單片機來說,雖然這種方法好用,但是太浪費空間。如果指令比較多,或者指令名比較長的話,可能光是

            指令列表就會把單片機的內存占滿。這樣的話還不如直接在switch語句中去比較字符串,直接比較字符串的話,只需要開辟一個字符串的存

            儲空間就可以滿足需求了。

            所以根據不同的情況選擇合適的方法,適合自己的方法就是好方法。

            -

            單片機串口實現字符串命令解析---使用函數指針(類似哈希表)

            本文發布于:2023-12-09 21:29:51,感謝您對本站的認可!

            本文鏈接:http://www.newhan.cn/zhishi/a/1702128591240286.html

            版權聲明:本站內容均來自互聯網,僅供演示用,請勿用于商業和其他非法用途。如果侵犯了您的權益請與我們聯系,我們將在24小時內刪除。

            本文word下載地址:單片機串口實現字符串命令解析---使用函數指針(類似哈希表).doc

            本文 PDF 下載地址:單片機串口實現字符串命令解析---使用函數指針(類似哈希表).pdf

            下一篇:返回列表
            標簽:命令   字符串   函數   結構   數組   需要   列表
            留言與評論(共有 0 條評論)
               
            驗證碼:
            Copyright ?2019-2022 Comsenz Inc.Powered by ? 實用文體寫作網旗下知識大全大全欄目是一個全百科類寶庫! 優秀范文|法律文書|專利查詢|
            主站蜘蛛池模板: 国产精品综合一区二区三区| 国产一区二区午夜福利久久| 在线高清理伦片a| 中文字幕国产精品av| 人妻少妇不满足中文字幕| 精品午夜福利无人区乱码| 欧美日本中文| 久久99国产精品尤物| 日韩激情一区二区三区| 黄色特级片一区二区三区| 亚洲av激情久久精品人| 亚洲伊人成色综合网| 一本久道久久综合中文字幕| 亚洲一区黄色| 久久久久人妻精品一区三寸| 成人午夜天| 日韩精品中文字幕有码| 极品少妇的诱惑| 激情综合色综合啪啪五月| 韩国av无码| 欧美视频网站www色| 欧美中文字幕无线码视频| 你懂的视频在线一区二区| 亚洲男女羞羞无遮挡久久丫| 国产亚洲日韩一区二区三区| 精品人妻二区中文字幕| 青青草原国产精品啪啪视频| 国产不卡的一区二区三区| 超碰成人人人做人人爽| av永久免费网站在线观看| 高清偷拍一区二区三区| 亚洲乱码一二三四区国产| 亚洲精品成人福利网站| 欧洲精品码一区二区三区| 久久精品夜色噜噜亚洲av | 亚洲永久精品唐人导航网址| 少妇av一区二区三区无码| 亚洲高清国产拍精品熟女| 永久无码天堂网小说区| 波多野结衣一区二区免费视频| 日本亚洲欧洲无免费码在线|