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

專注電子技術(shù)學習與研究
當前位置:單片機教程網(wǎng) >> STM32 >> 瀏覽文章

STM32F10x片上Flash 模擬 EEPROM程序

作者:JASoN   來源:會員上傳   點擊數(shù):  更新時間:2014年05月18日   【字體:

本程序還要用到一個頭文件,完整源碼的下載地址是: http://www.zg4o1577.cn/f/STM32F10x_EEPROM_Emulation.zip

STM32F10x芯片沒有自帶eeprom我們可以用Flash來模擬 EEPROM程序,本程序原理詳見: http://www.zg4o1577.cn/stm32/3581.html

 /**

 *******************************************************************************
 * @file    eeprom.c
 * @author  JASoN
 * @version V1.0
 * @date    2014-03-20
 * @since   2014-03-20
 * @ref     STM32F10x_AN2594_FW_V3.1.0 - MCD Application Team
 * @brief   This file provides all the EEPROM emulation firmware functions.
 *
 * Page map
 * +---------+-------------+----------------------------+
 * | byte    | length      | description                |
 * +---------+-------------+----------------------------+ <- PAGE_BASE_ADDRESS
 * | [0: 1]  | 16 bits     | page status:               |
 * |         |             |   0xFFFF: ERASED           |
 * |         |             |   0xEEEE: RECEIVE_DATA     |
 * |         |             |   0x0000: VALID_PAGE       |
 * +---------+-------------+----------------------------+
 * | [2: 7]  | 48 bits     | reserved                   |
 * +---------+-------------+----------------------------+
 * | [8: 15] | 64 bits     | block 1                    |
 * +---------+-------------+----------------------------+
 * |         |             | ...                        |  鈫揵lock grows
 * +---------+-------------+----------------------------+
 * |         | 64 bits     | block n                    | ------+
 * +---------+-------------+----------------------------+       |
 * |                                                    |       | data offset
 * +---------+-------------+----------------------------+       | (16 bits)
 * |         | 16 bits     | 0xCCCC                     |       |
 * +---------+-------------+----------------------------+ <-----+
 * |         | data length | data n (2 bytes alignment) |
 * +---------+-------------+----------------------------+
 * |         |             | ...                        |  鈫慸ata grows
 * +---------+-------------+----------------------------+
 * |         | 16 bits     | 0xCCCC                     |
 * +---------+-------------+----------------------------+
 * |         | data length | data 1 (2 bytes alignment) |
 * +---------+-------------+----------------------------+  <- PAGE_END_ADDRESS
 *
 * Block map (64 bits)
 * +----------+---------+-----------------------------------------+
 * | bits     | length  | description                             |
 * +----------+---------+-----------------------------------------+
 * | [0: 15]  | 16 bits | block status:                           |
 * |          |         |   0xAAAA: VALID_BLOCK                   |
 * |          |         |   0x0000: EXPIRED_BLOCK                 |
 * +----------+---------+-----------------------------------------+
 * | [16: 23] | 8 bits  | data key                                |
 * +----------+---------+-----------------------------------------+
 * | [24: 31] | 8 bits  | data checksum                           |
 * +----------+---------+-----------------------------------------+
 * | [32: 47] | 16 bits | data offset (base on PAGE_BASE_ADDRESS) |
 * +----------+---------+-----------------------------------------+
 * | [48: 63] | 16 bits | data length (bytes)                     |
 * +----------+---------+-----------------------------------------+
 *
 *******************************************************************************
 */
 
#include "eeprom.h"
 
 
/**
 * Private functions
 */
static uint16_t EE_Format(void);
static uint16_t EE_FindValidPage(uint8_t operation);
static uint16_t EE_WriteByVerify(uint8_t key, const uint8_t *pdata, uint16_t size);
static uint16_t EE_PageTransfer(uint16_t page);
static uint16_t EE_CheckData(void);
 
 
/**
 * Function EE_Init
 * Restore the pages to a known good state in case of page's status corruption
 * after a power loss.
 * Page status combinations and actions to be taken as follows:
 * +--------------+-----------------------------------------------------------------------------------+
 * |              |                                     PAGE_0                                        |
 * |    PAGE_1    +-------------------------+----------------------------+----------------------------+
 * |              | ERASED                  | RECEIVE_DATA               | VALID_PAGE                 |
 * +--------------+-------------------------+----------------------------+----------------------------+
 * |              | First EEPROM access or  | Erase PAGE_1 and mark      | OK                         |
 * | ERASED       | invalid state.          | PAGE_0 as valid.           | Run check data routine.    |
 * |              | Erase both pages and    | Run check data routine.    |                            |
 * |              | mark PAGE_0 as valid.   |                            |                            |
 * |--------------+-------------------------+----------------------------+----------------------------+
 * |              | Erase PAGE_0 and mark   | Invalid state.             | Transfer the last updated  |
 * |              | PAGE_1 as valid.        | Erase both pages and       | data from PAGE_0 to PAGE_1 |
 * | RECEIVE_DATA | Run check data routine. | format PAGE_0.             | and mark PAGE_1 as valid   |
 * |              |                         |                            | and erase PAGE_0.          |
 * |              |                         |                            | Run check data routine.    |
 * +--------------+-------------------------+----------------------------+----------------------------+
 * |              | OK                      | Transfer the last updated  | Invalid state.             |
 * |              | Run check data routine. | data from PAGE_1 to PAGE_0 | Erase both pages and mark  |
 * | VALID_PAGE   |                         | and mark PAGE_0 as valid   | PAGE_0 as valid.           |
 * |              |                         | and erase PAGE_1.          |                            |
 * |              |                         | Run check data routine.    |                            |
 * +--------------+-------------------------+----------------------------+----------------------------+
 *
 * @param  none
 * @return success or error status
 *         - FLASH_COMPLETE: on success
 *         - error code:     on error
 */
uint16_t EE_Init(void) {
  uint16_t status = FLASH_COMPLETE;
  uint16_t ps0 = 0; // PAGE_0 status
  uint16_t ps1 = 0; // PAGE_1 status
 
  ps0 = (*(__IO uint16_t*)PAGE_0_BASE_ADDRESS);
  ps1 = (*(__IO uint16_t*)PAGE_1_BASE_ADDRESS);
 
//  printf("PAGE_0 status: %x, PAGE_1 status: %x\n", ps0, ps1);
 
  switch(ps0) {
    /* --------------------------- PAGE_0 erased ---------------------------- */
    case ERASED:
      if(ps1 == VALID_PAGE) {                                    // PAGE_1 valid
        return EE_CheckData();
      }
      else if(ps1 == RECEIVE_DATA) {                           // PAGE_1 receive
        /* erase PAGE_0 */
        status = FLASH_ErasePage(PAGE_0_BASE_ADDRESS);
        if(status != FLASH_COMPLETE) return status;
 
        /* mark PAGE_1 as valid */
        status = FLASH_ProgramHalfWord(PAGE_1_BASE_ADDRESS, VALID_PAGE);
        if(status != FLASH_COMPLETE) return status;
      }
      else {                                                    // invalid state
        /* erase both pages and mark PAGE_0 as valid */
        return EE_Format();
      }
      break;
 
    /* --------------------------- PAGE_0 receive --------------------------- */
    case RECEIVE_DATA:
      if(ps1 == VALID_PAGE) {                                    // PAGE_1 valid
        /* transfer the last updated data from PAGE_1 to PAGE_0
           and mark PAGE_0 as valid and erase PAGE_1
         */
        status = EE_PageTransfer(PAGE_1);
        if(status != FLASH_COMPLETE) return status;
      }
      else if(ps1 == ERASED) {                                  // PAGE_1 erased
        /* erase PAGE_1 */
        status = FLASH_ErasePage(PAGE_1_BASE_ADDRESS);
        if(status != FLASH_COMPLETE) return status;
 
        /* mark PAGE_0 as valid */
        status = FLASH_ProgramHalfWord(PAGE_0_BASE_ADDRESS, VALID_PAGE);
        if(status != FLASH_COMPLETE) return status;
      }
      else {                                                    // invalid state
        /* erase both pages and mark PAGE_0 as valid */
        return EE_Format();
      }
      break;
 
    /* --------------------------- PAGE_0 valid ----------------------------- */
    case VALID_PAGE:
      if(ps1 == VALID_PAGE) {                                   // invalid state
        /* erase both pages and mark PAGE_0 as valid */
        return EE_Format();
      }
      else if(ps1 == ERASED) {                                  // PAGE_1 erased
        return EE_CheckData();
      }
      else {                                                   // PAGE_1 receive
        /* transfer the last updated data from PAGE_0 to PAGE_1
           and mark PAGE_1 as valid and erase PAGE_0
         */
        status = EE_PageTransfer(PAGE_0);
        if(status != FLASH_COMPLETE) return status;
      }
      break;
 
    /* ------------------------------ default ------------------------------- */
    default:
      /* erase both pages and set PAGE_0 as valid */
      return EE_Format();
  }
 
  return EE_CheckData();
}
 
/**
 * Function EE_ReadData
 * Get the last stored data correspond to the passed data key.
 * @param  key:   8 bits key of the stored data
 * @param  pdata: pointer to the variable that will contain the read value
 * @param  size:  data size in bytes
 * @return success or error status
 *         - 0: if data found
 *         - 1: if data not found
 *         - EE_NO_VALID_PAGE:       if no valid page found
 *         - EE_DATA_SIZE_ERROR:     if the passed size not equal to data length
 *         - EE_DATA_CHECKSUM_ERROR: if data checksum error
 */
uint16_t EE_ReadData(uint8_t key, uint8_t* pdata, uint16_t size) {
  uint16_t page = PAGE_0;
  uint32_t base = EEPROM_START_ADDRESS;
  uint32_t address = EEPROM_START_ADDRESS;
  uint16_t value = 0;
  uint16_t dataOffset = 0;
  uint16_t dataLength = 0;
  uint8_t dataChecksum = 0;
  uint8_t checksum = 0;
  uint16_t flag = 1; // data not found
 
  /* get valid page for read operation */
  page = EE_FindValidPage(READ_FROM_VALID_PAGE);
  if(page == EE_NO_VALID_PAGE) return page;
 
  /* page base address */
  base = EEPROM_START_ADDRESS + (uint32_t)(page * PAGE_SIZE);
 
  /* block address */
  address = base + 8;
 
  /* check each valid block starting from begining */
  while(address < base + PAGE_SIZE) {
    value = (*(__IO uint16_t*)address); // block status
 
    /* in case the valid block found */
    if(value == VALID_BLOCK) {
      value = (*(__IO uint16_t*)(address + 2)); // key & checksum
 
      /* in case the key matched (valid data found) */
      if((value & 0x00FF) == key) {
        dataChecksum = (uint8_t)(value >> 8);
        dataOffset = (*(__IO uint16_t*)(address + 4));
        dataLength = (*(__IO uint16_t*)(address + 6));
 
        /* check data size */
        if(dataLength != size || (base + dataOffset + size) > (base + PAGE_SIZE)) {
          return EE_DATA_SIZE_ERROR;
        }
 
//        printf("read data from: %x\n", base + dataOffset);
 
        /* read data */
        for(; size > 0; size--) {
          value = (*(__IO uint8_t*)(base + dataOffset));
          checksum = (checksum << 1) || (checksum >> 7);
          checksum += value;
          *(pdata++) = value;
          dataOffset++;
        }
 
        /* verify the checksum */
        if(checksum != dataChecksum) {
          return EE_DATA_CHECKSUM_ERROR;
        }
 
        flag = 0;
        break;
      }
    }
    else if(value != EXPIRED_BLOCK) { // end of block area
      break;
    }
    address += 8; // next block
  }
 
  return flag;
}
 
/**
 * Function EE_WriteData
 * Writes/upadtes data in EEPROM.
 * @param  key:   8 bits key of the data
 * @param  pdata: pointer to the data that will be written
 * @param  size:  data size in bytes
 * @return success or error status
 *         - FLASH_COMPLETE: on success
 *         - error code:     on error
 */
uint16_t EE_WriteData(uint8_t key, const uint8_t *pdata, uint16_t size) {
  uint16_t status = FLASH_COMPLETE;
 
  status = EE_WriteByVerify(key, pdata, size);
 
  /* in case the valid page is full, then perform page transfer */
  if(status == EE_PAGE_FULL) {
    status = EE_PageTransfer(EE_FindValidPage(WRITE_IN_VALID_PAGE));
    if(status != FLASH_COMPLETE) return status;
 
    /* write again */
    status = EE_WriteByVerify(key, pdata, size);
  }
 
  return status;
}
 
/**
 * Function EE_Format
 * Erase both pages and mark PAGE_0 as valid.
 * @param  none
 * @return status of the last operation
 */
static uint16_t EE_Format(void) {
  FLASH_Status status = FLASH_COMPLETE;
 
//  printf("format page.\n");
 
  /* erase PAGE_0 */
  status = FLASH_ErasePage(PAGE_0_BASE_ADDRESS);
  if(status != FLASH_COMPLETE) return status;
 
  /* mark PAGE_0 as valid */
  status = FLASH_ProgramHalfWord(PAGE_0_BASE_ADDRESS, VALID_PAGE);
  if(status != FLASH_COMPLETE) return status;
 
  /* erase PAGE_1 */
  status = FLASH_ErasePage(PAGE_1_BASE_ADDRESS);
  return status;
}
 
/**
 * Function EE_FindValidPage
 * Find valid page for write or read operation.
 * @param  operation: operation to achieve on the valid page
 *         @arg READ_FROM_VALID_PAGE: read operation from valid page
 *         @arg WRITE_IN_VALID_PAGE:  write operation to valid page
 * @return valid page number (PAGE_0 or PAGE_1) or EE_NO_VALID_PAGE
 */
static uint16_t EE_FindValidPage(uint8_t operation) {
  uint16_t ps0 = 0; // PAGE_0 status
  uint16_t ps1 = 0; // PAGE_1 status
 
  ps0 = (*(__IO uint16_t*)PAGE_0_BASE_ADDRESS);
  ps1 = (*(__IO uint16_t*)PAGE_1_BASE_ADDRESS);
 
  switch(operation) {
    /* write operation */
    case WRITE_IN_VALID_PAGE:
      if(ps1 == VALID_PAGE) {
        if(ps0 == RECEIVE_DATA) { // transfer PAGE_1 -> PAGE_0
          return PAGE_0;
        }
        else {
          return PAGE_1;
        }
      }
      else if(ps0 == VALID_PAGE) {
        if(ps1 == RECEIVE_DATA) { // transfer PAGE_0 -> PAGE_1
          return PAGE_1;
        }
        else {
          return PAGE_0;
        }
      }
      else {
        return EE_NO_VALID_PAGE;
      }
 
    /* read operation */
    case READ_FROM_VALID_PAGE:
      if(ps0 == VALID_PAGE) {
        return PAGE_0;
      }
      else if(ps1 == VALID_PAGE) {
        return PAGE_1;
      }
      else {
        return EE_NO_VALID_PAGE;
      }
 
    default:
      return PAGE_0;
  }
}
 
/**
 * Function EE_WriteByVerify
 * Verify if valid page is full and write data to EEPROM.
 * @param  key:   8 bits key of the data
 * @param  pdata: pointer to the data that will be written
 * @param  size:  data size in bytes
 * @return success or error status
 *         - FLASH_COMPLETE:     on success
 *         - EE_NO_VALID_PAGE:   if no valid page was found
 *         - EE_PAGE_FULL:       if valid page is full
 *         - EE_DATA_SIZE_ERROR: if passed data size is zero
 *         - error code:         on write flash error
 */
static uint16_t EE_WriteByVerify(uint8_t key, const uint8_t *pdata, uint16_t size) {
  uint16_t status = FLASH_COMPLETE;
  uint16_t page = PAGE_0;
  uint32_t base = EEPROM_START_ADDRESS;
  uint32_t address = EEPROM_START_ADDRESS;
  uint32_t dataAddress = EEPROM_START_ADDRESS;
  uint16_t value = 0;
  uint8_t checksum = 0;
  uint16_t i = 0;
 
  if(size == 0) return EE_DATA_SIZE_ERROR;
 
  /* get valid page for write operation */
  page = EE_FindValidPage(WRITE_IN_VALID_PAGE);
  if(page == EE_NO_VALID_PAGE) return page;
 
  /* page base address */
  base = EEPROM_START_ADDRESS + (uint32_t)(page * PAGE_SIZE);
 
  /* block address */
  address = base + 8;
 
  /* check each valid block starting from begining */
  while(address < base + PAGE_SIZE) {
    value = (*(__IO uint16_t*)address); // block head
 
    /* in case the valid block found */
    if(value == VALID_BLOCK) {
      value = (*(__IO uint16_t*)(address + 2)); // key & checksum
 
      /* in case the key matched, then set the block expired */
      if((value & 0x00FF) == key) {
        status = FLASH_ProgramHalfWord(address, EXPIRED_BLOCK);
        if(status != FLASH_COMPLETE) return status;
      }
    }
    else if(value != EXPIRED_BLOCK) { // end of block area
      break;
    }
    address += 8; // next block
  }
 
  /* get the last empty data address (2 bytes alignment) */
  if(address == (base + 8)) {
    dataAddress = base + PAGE_SIZE - 2;
  }
  else {
    value = (*(__IO uint16_t*)(address - 4)); // data offset
    if(value >= PAGE_SIZE) {
      return EE_PAGE_FULL; // trigger the page transfering action
    }
    dataAddress = base + value - 4;
  }
 
  /* check if has enough space for the new block and data */
  if((address + 8 + 2 + size) > dataAddress) { // accurate: dataAddress + 2
    return EE_PAGE_FULL;
  }
 
  /* check empty space for new block */
  for(i = 0; i < 4; i++) {
    value = (*(__IO uint16_t*)(address + i * 2));
    if(value != 0xFFFF) return EE_PAGE_FULL;
  }
 
  /* check empty space for new data (2 bytes alignment) */
  for(i = 0; i < size; i += 2) {
    value = (*(__IO uint16_t*)dataAddress);
    if(value != 0xFFFF) return EE_PAGE_FULL;
    dataAddress -= 2; // pointer to data flag
  }
 
  /* check empty space for data flag */
  value = (*(__IO uint16_t*)dataAddress);
  if(value != 0xFFFF) return EE_PAGE_FULL;
 
  /* write new block */
  status = FLASH_ProgramHalfWord(address, VALID_BLOCK); // block status
  if(status != FLASH_COMPLETE) return status;
 
  status = FLASH_ProgramHalfWord(address + 4, (dataAddress - base + 2)); // data offset (2 bytes after data flag)
  if(status != FLASH_COMPLETE) return status;
 
  status = FLASH_ProgramHalfWord(address + 6, size); // data length
  if(status != FLASH_COMPLETE) return status;
 
  /* write new data begin flag (0xCCCC)*/
  status = FLASH_ProgramHalfWord(dataAddress, DATA_FLAG);
  if(status != FLASH_COMPLETE) return status;
  dataAddress += 2;
 
//  printf("write data at: %x, space left: %d\n", dataAddress, (dataAddress - address - 8));
 
  /* write new data */
  for(i = 0; i < size; i++) {
    checksum = (checksum << 1) || (checksum >> 7);
    checksum += *(pdata + i);
 
    /* 2 bytes alignment */
    if((i & (uint16_t)0x1) == 0) {
      value = (i == (size - 1)) ? (uint16_t)(*(pdata + i)) : (*(uint16_t*)(pdata + i));
      status = FLASH_ProgramHalfWord(dataAddress, value);
      if(status != FLASH_COMPLETE) return status;
      dataAddress += 2;
    }
  }
 
  /* write key & checksum */
  value = ((uint16_t)checksum << 8 | key);
  status = FLASH_ProgramHalfWord(address + 2, value);
  return status;
}
 
/**
 * Function EE_PageTransfer
 * Transfer the last updated data from the passed page to an empty target page
 * and mark the target page as valid and erase passed page.
 * @param  page: page to be transfered
 * @return success or error status
 *         - FLASH_COMPLETE: on success
 *         - error code:     on error
 */
static uint16_t EE_PageTransfer(uint16_t page) {
  uint16_t status = FLASH_COMPLETE;
  uint32_t sourceBase = EEPROM_START_ADDRESS;
  uint32_t targetBase = EEPROM_START_ADDRESS;
  uint32_t address = EEPROM_START_ADDRESS;
  uint16_t value = 0;
  uint64_t block = 0;
  uint16_t *pcell = 0; // pointer to block cell in 2 bytes
  uint16_t dataOffset = 0;
  uint16_t dataLength = 0;
  uint32_t targetL = 0;
  uint32_t targetH = 0;
  uint16_t i = 0;
 
//  printf("transfer page%d\n", page);
 
  if(page == PAGE_0) {
    sourceBase = PAGE_0_BASE_ADDRESS;
    targetBase = PAGE_1_BASE_ADDRESS;
  }
  else if(page == PAGE_1) {
    sourceBase = PAGE_1_BASE_ADDRESS;
    targetBase = PAGE_0_BASE_ADDRESS;
  }
  else {
    return EE_NO_VALID_PAGE;
  }
 
  address = (uint32_t)(sourceBase + 8);         // source block address
  targetL = (uint32_t)(targetBase + 8);         // target block address
  targetH = (uint32_t)(targetBase + PAGE_SIZE); // target data address
 
  /* confirm target page erased */
  value = (*(__IO uint16_t*)targetBase);
  if(value != ERASED) {
    status = FLASH_ErasePage(targetBase);
    if(status != FLASH_COMPLETE) return status;
  }
 
  /* mark target page RECEIVE_DATA status */
  status = FLASH_ProgramHalfWord(targetBase, RECEIVE_DATA);
  if(status != FLASH_COMPLETE) return status;
 
  /* check each valid block in source page starting from begining */
  while(address < sourceBase + PAGE_SIZE) {
    value = (*(__IO uint16_t*)address); // block status
 
    /* in case of VALID_BLOCK */
    if(value == VALID_BLOCK) {
      block = (*(__IO uint64_t*)(address));
      pcell = (uint16_t*)(&block);
      dataOffset = (*(pcell + 2));
      dataLength = (*(pcell + 3));
 
      /* continue if data size error */
      if(dataLength == 0) {
        continue;
      }
      if((sourceBase + dataOffset + dataLength) > (sourceBase + PAGE_SIZE)) {
        continue;
      }
 
      /* calculate target data address */
      targetH = targetH - dataLength;
      if((targetH & (uint32_t)0x1) == 1) {
        targetH--; // 2 bytes alignment
      }
 
      if((targetH - targetL) < 10) continue; // block + data flag = 10 bytes
 
      /* update data offset cell in block */
      *(pcell + 2) = targetH - targetBase;
 
      /* write target block */
      for(i = 0; i < 4; i++) {
        status = FLASH_ProgramHalfWord(targetL + i * 2, *(pcell + i));
        if(status != FLASH_COMPLETE) return status;
      }
      targetL += 8;                                              // next targetL
 
      /* copy data */
      for(i = 0; i < dataLength; i += 2) {
        value = (*(__IO uint16_t*)(sourceBase + dataOffset + i));
        status = FLASH_ProgramHalfWord(targetH + i, value);
        if(status != FLASH_COMPLETE) return status;
      }
 
      /* write data begin flag (0xCCCC) */
      targetH = targetH - 2;                                     // next targetH
      status = FLASH_ProgramHalfWord(targetH, DATA_FLAG);
      if(status != FLASH_COMPLETE) return status;
    }
    else if(value != EXPIRED_BLOCK) { // end of block area
      break;
    }
    address += 8; // next source block
  }
 
  /* mark targer page as valid */
  status = FLASH_ProgramHalfWord(targetBase, VALID_PAGE);
  if(status != FLASH_COMPLETE) return status;
 
  /* erase source page */
  status = FLASH_ErasePage(sourceBase);
  return status;
}
 
/**
 * Function EE_CheckData
 * TODO: Verify data by calculating the checksum.
 */
static uint16_t EE_CheckData(void) {
  return FLASH_COMPLETE;
}
 
/**
 * Extensions
 */
uint16_t memcpy_to_eeprom_with_checksum(uint8_t key, const void *pdata, uint16_t size) {
  return EE_WriteData(key, (uint8_t*)pdata, size);
}
 
uint16_t memcpy_from_eeprom_with_checksum(void *pdata, uint8_t key, uint16_t size) {
  return EE_ReadData(key, (uint8_t*)pdata, size);
}
 
關(guān)閉窗口

相關(guān)文章

主站蜘蛛池模板: 99久久精品国产一区二区三区 | 中文字幕av网站 | 国产一级片 | 综合色在线 | 国产成人精品午夜视频免费 | 爱爱无遮挡 | 久久中文字幕一区 | 精品国产一区二区三区久久 | 国产91黄色 | 国产美女一区二区三区 | 成人做爰www免费看 午夜精品久久久久久久久久久久 | 午夜久久久 | 国产精品片aa在线观看 | 日日操日日干 | 精品欧美乱码久久久久久 | 夜夜精品浪潮av一区二区三区 | 国产精品久久久久久久白浊 | 日本免费一区二区三区视频 | 一级片视频免费 | 狠狠色综合久久婷婷 | 久久久性色精品国产免费观看 | 91欧美激情一区二区三区成人 | 久久久久国产 | 日韩精品一区二区三区在线观看 | 国产高清亚洲 | 精品久久久一区二区 | 久久一区 | 欧美成人不卡 | 99re热这里只有精品视频 | 99久久精品视频免费 | 国产日韩一区二区三免费高清 | 亚洲午夜精品视频 | www.com久久久| 成人在线观看免费 | 亚洲国产精品人人爽夜夜爽 | 精品在线 | 高清久久久 | 中文字幕在线一区二区三区 | 成人国产精品色哟哟 | 国产精品视频久久 | 人操人人 |