Arduino--常用函数(二)

一、数字 I/O

  1. pinMode()
    描述
    将指定的引脚配置成输出或输入。详情请见digital pins。
    语法
    pinMode(pin, mode)
    参数
    pin:要设置模式的引脚
    mode:INPUT或OUTPUT
    返回

    例子
ledPin = 13 // LED连接到数字脚13
void setup()
{
pinMode(ledPin,OUTPUT); //设置数字脚为输出
}
void loop()
{
digitalWrite(ledPin,HIGH); //点亮LED
delay(1000); // 等待一秒
digitalWrite(ledPin, LOW); // 灭掉LED
延迟(1000); //等待第二个
}

注意
模拟输入脚也能当做数字脚使用,参见A0,A1等

  1. digitalWrite()
    描述
    给一个数字引脚写入HIGH或者LOW。
    如果一个引脚已经使用pinMode()配置为OUTPUT模式,其电压将被设置为相应的值,HIGH为5V(3.3V控制板上为3.3V),LOW为0V。
    如果引脚配置为INPUT模式,使用digitalWrite()写入HIGH值,将使内部20K上拉电阻(详见数字引脚教程)。写入LOW将会禁用上拉。上拉电阻可以点亮一个LED让其微微亮,如果LED工作,但是亮度很低,可能是因为这个原因引起的。补救的办法是 使用pinMode()函数设置为输出引脚。
    注意:数字13号引脚难以作为数字输入使用,因为大部分的控制板上使用了一颗LED与一个电阻连接到他。如果启动了内部的20K上拉电阻,他的电压将在1.7V左右,而不是正常的5V,因为板载LED串联的电阻把他使他降了下来,这意味着他返回的值总是LOW。如果必须使用数字13号引脚的输入模式,需要使用外部上拉下拉电阻。
    语法
    digitalWrite(pin, value)
    参数
    pin: 引脚编号(如1,5,10,A0,A3)
    value: HIGH or LOW
    返回

    例子
int ledPin = 13; // LED连接到数字13号端口
void setup()
{
pinMode(ledPin, OUTPUT); // 设置数字端口为输入模式
}
void loop()
{
digitalWrite(ledPin, HIGH); // 使LED亮
delay(1000); // 延迟一秒
digitalWrite(ledPin, LOW); // 使LED灭
delay(1000); // 延迟一秒
}

13号端口设置为高电平,延迟一秒,然后设置为低电平。
注释
模拟引脚也可以当做数字引脚使用,使用方法是输入端口A0,A1,A2等。

  1. digitalRead()
    描述
    读取指定引脚的值,HIGH或LOW。
    语法
    digitalRead(PIN)
    参数
    pin:你想读取的引脚号(int)
    返回
    HIGH 或 LOW
    例子
ledPin = 13 // LED连接到13脚
int inPin = 7; // 按钮连接到数字引脚7
int val = 0; //定义变量存以储读值
void setup()
{
pinMode(ledPin, OUTPUT); // 将13脚设置为输出
pinMode(inPin, INPUT); // 将7脚设置为输入
}
void loop()
{
val = digitalRead(inPin); // 读取输入脚
digitalWrite(ledPin, val); //将LED值设置为按钮的值
}

将13脚设置为输入脚7脚的值。
注意
如果引脚悬空,digitalRead()会返回HIGH或LOW(随机变化)。
模拟输入脚能当做数字脚使用,参见A0,A1等。

二、模拟 I/O

  1. analogReference()
    描述
    配置用于模拟输入的基准电压(即输入范围的最大值)。选项​​有:
    DEFAULT:默认5V(Arduino板为5V)或3.3伏特(Arduino板为3.3V)为基准电压。
    INTERNAL:在ATmega168和ATmega328上以1.1V为基准电压,以及在ATmega8上以2.56V为基准电压(Arduino Mega无此选项)
    INTERNAL1V1:以1.1V为基准电压(此选项仅针对Arduino Mega)
    INTERNAL2V56:以2.56V为基准电压(此选项仅针对Arduino Mega)
    EXTERNAL:以AREF引脚(0至5V)的电压作为基准电压。
    参数
    type:使用哪种参考类型(DEFAULT, INTERNAL, INTERNAL1V1, INTERNAL2V56, 或者 EXTERNAL)。
    返回

    注意事项
    改变基准电压后,之前从anal​​ogRead()读取的数据可能不准确。
    警告
    不要在AREF引脚上使用使用任何小于0V或超过5V的外部电压。如果你使用AREF引脚上的电压作为基准电压,你在调用analogRead()前必须设置参考类型为EXTERNAL。否则,你将会削短有效的基准电压(内部产生)和AREF引脚,这可能会损坏您Arduino板上的单片机。
    另外,您可以在外部基准电压和AREF引脚之间连接一个5K电阻,使你可以在外部和内部基准电压之间切换。请注意,总阻值将会发生改变,因为AREF引脚内部有一个32K电阻。这两个电阻都有分压作用。所以,例如,如果输入2.5V的电压,最终在在AREF引脚上的电压将为2.5 * 32 /(32 + 5)= 2.2V。

  2. analogRead()
    描述
    从指定的模拟引脚读取数据值。 Arduino板包含一个6通道(Mini和Nano有8个通道,Mega有16个通道),10位模拟数字转换器。这意味着它将0至5伏特之间的输入电压映射到0至1023之间的整数值。这将产生读数之间的关系:5伏特/ 1024单位,或0.0049伏特(4.9 mV)每单位。输入范围和精度可以使用analogReference()改变。 它需要大约100微秒(0.0001)来读取模拟输入,所以最大的阅读速度是每秒10000次。
    语法
    analogRead(PIN)
    数值的读取
    引脚:从输入引脚(大部分板子从0到5,Mini和Nano从0到7,Mega从0到15)读取数值
    返回
    从0到1023的整数值
    注意事项
    如果模拟输入引脚没有连入电路,由analogRead()返回的值将根据多项因素(例如其他模拟输入引脚,你的手靠近板子等)产生波动。
    例子

int analogPin = 3; //电位器(中间的引脚)连接到模拟输入引脚3
                  //另外两个引脚分别接地和+5 V
int val = 0; //定义变量来存储读取的数值
void setup()
{
serial.begin(9600); //设置波特率(9600)
}
void loop()
{
val = analogRead(analogPin); //从输入引脚读取数值
serial.println(val); //显示读取的数值
}
  1. analogWrite() PWM
    描述
    从一个引脚输出模拟值(PWM)。可用于让LED以不同的亮度点亮或驱动电机以不同的速度旋转。analogWrite()输出结束后,该引脚将产生一个稳定的特殊占空比方波,直到下次调用analogWrite()(或在同一引脚调用digitalRead()或digitalWrite())。PWM信号的频率大约是490赫兹。
    在大多数arduino板(ATmega168或ATmega328),只有引脚3,5,6,9,10和11可以实现该功能。在aduino Mega上,引脚2到13可以实现该功能。老的Arduino板(ATmega8)的只有引脚9、10、11可以使用analogWrite()。在使用analogWrite()前,你不需要调用pinMode()来设置引脚为输出引脚。
    analogWrite函数与模拟引脚、analogRead函数没有直接关系。
    语法
    analogWrite(pin,value)
    参数
    pin:用于输入数值的引脚。
    value:占空比:0(完全关闭)到255(完全打开)之间。
    返回

    说明和已知问题
    引脚5和6的PWM输出将高于预期的占空比(输出的数值偏高)。这是因为millis()和delay()功能,和PWM输出共享相同的内部定时器。这将导致大多时候处于低占空比状态(如:0 - 10),并可能导致在数值为0时,没有完全关闭引脚5和6。
    例子
    通过读取电位器的阻值控制LED的亮度
int ledPin = 9; // LED连接到数字引脚9
int analogPin = 3; //电位器连接到模拟引脚3
int val = 0; //定义变量存以储读值
void setup()
{
pinMode(ledPin,OUTPUT); //设置引脚为输出引脚
}
void loop()
{
val = analogRead(analogPin); //从输入引脚读取数值
analogWrite(ledPin,val / 4); // 以val / 4的数值点亮LED(因为analogRead读取的数值从0到1023,而analogWrite输出的数值从0到255)
}

三、高级 I/O

  1. tone()
    描述
    在一个引脚上产生一个特定频率的方波(50%占空比)。持续时间可以设定,否则波形会一直产生直到调用noTone()函数。该引脚可以连接压电蜂鸣器或其他喇叭播放声音。
    在同一时刻只能产生一个声音。如果一个引脚已经在播放音乐,那调用tone()将不会有任何效果。如果音乐在同一个引脚上播放,它会自动调整频率。
    使用tone()函数会与3脚和11脚的PWM产生干扰(Mega板除外)。
    注意:如果你要在多个引脚上产生不同的音调,你要在对下一个引脚使用tone()函数前对此引脚调用noTone()函数。
    语法
    tone(pin, frequency)
    tone(pin, frequency, duration)
    参数
    pin:要产生声音的引脚
    frequency: 产生声音的频率,单位Hz,类型unsigned int
    duration:声音持续的时间,单位毫秒(可选),类型unsigned long
  2. noTone()
    描述
    停止由tone()产生的方波。如果没有使用tone()将不会有效果。
    注意:如果你想在多个引脚上产生不同的声音,你要在对下个引脚使用tone()前对刚才的引脚调用noTone().
    语法
    noTone(pin)
    参数
    pin: 所要停止产生声音的引脚
  3. shiftOut()
    描述
    将一个数据的一个字节一位一位的移出。从最高有效位(最左边)或最低有效位(最右边)开始。依次向数据脚写入每一位,之后时钟脚被拉高或拉低,指示刚才的数据有效。
    注意:如果你所连接的设备时钟类型为上升沿,你要确定在调用shiftOut()前时钟脚为低电平,如调用digitalWrite(clockPin, LOW)。
    注意:这是一个软件实现;Arduino提供了一个硬件实现的SPI库,它速度更快但只在特定脚有效。
    语法
    shiftOut(dataPin, clockPin, bitOrder, value)
    参数
    dataPin:输出每一位数据的引脚(int)
    clockPin:时钟脚,当dataPin有值时此引脚电平变化(int)
    bitOrder:输出位的顺序,最高位优先或最低位优先
    value: 要移位输出的数据(byte)
    返回

    注意
dataPin和clockPin要用pinMode()配置为输出。 shiftOut目前只能输出1个字节(8位),所以如果输出值大于255需要分两步。
//最高有效位优先串行输出
int 数据= 500;
//移位输出高字节
shiftOut(dataPin, clock, MSBFIRST, (data >> 8));
//移位输出低字节
shiftOut(data, clock, MSBFIRST, data);
//最低有效位优先串行输出
data = 500;
//移位输出低字节
shiftOut(dataPin, clock, LSBFIRST, data);
//移位输出高字节
shiftOut(dataPin, clock, LSBFIRST, (data >> 8));

例子

// 相应电路,查看tutorial on controlling a 74HC595 shift register
// ************************************************ ************** //
// 注释:使用74HC595移位寄存器从0到255计数 //
//
// ************************************************ ****************
//引脚连接到74HC595的ST_CP
int latchPin = 8;
//引脚连接到74HC595的SH_CP
int clockPin = 12;
// //引脚连接到74HC595的DS
int dataPin = 11;
void setup() {
//设置引脚为输出
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
}
void loop() {
//向上计数程序
(J = 0; J <256; J + +){
 //传输数据的时候将latchPin拉低
digitalWrite(latchpin, LOW);
 shiftOut的 (dataPin,clockPin,LSBFIRST,J);
//之后将latchPin拉高以告诉芯片
 //它不需要再接受信息了
digitalWrite(latchpin, HIGH);
 delay(1000);
}
}
  1. shiftIn()
    描述
    将一个数据的一个字节一位一位的移入。从最高有效位(最左边)或最低有效位(最右边)开始。对于每个位,先拉高时钟电平,再从数据传输线中读取一位,再将时钟线拉低。
    注意:这是一个软件实现;Arduino提供了一个硬件实现的SPI库,它速度更快但只在特定脚有效。
    语法
shiftIn(dataPin,clockPin,bitOrder)

参数
dataPin:输出每一位数据的引脚(int)
clockPin:时钟脚,当dataPin有值时此引脚电平变化(int)
bitOrder:输出位的顺序,最高位优先或最低位优先

  1. pulseIn()
    描述
    读取一个引脚的脉冲(HIGH或LOW)。例如,如果value是HIGH,pulseIn()会等待引脚变为HIGH,开始计时,再等待引脚变为LOW并停止计时。返回脉冲的长度,单位微秒。如果在指定的时间内无脉冲函数返回。
    此函数的计时功能由经验决定,长时间的脉冲计时可能会出错。计时范围从10微秒至3分钟。(1秒=1000毫秒=1000000微秒)
    语法
pulseIn(pin, value)
pulseIn(pin, value, timeout)

参数
pin:你要进行脉冲计时的引脚号(int)。
value:要读取的脉冲类型,HIGH或LOW(int)。
timeout (可选):指定脉冲计数的等待时间,单位为微秒,默认值是1秒(unsigned long)
返回
脉冲长度(微秒),如果等待超时返回0(unsigned long)
例子

int pin = 7;
unsigned long duration;
void setup()
{
pinMode(pin, INPUT);
}
void loop()
{
duration = pulseIn(pin, HIGH);;
}

四、时间

  1. millis()
    描述
    返回Arduino开发板从运行当前程序开始的毫秒数。这个数字将在约50天后溢出(归零)。
    参数

    返回
    返回从运行当前程序开始的毫秒数(无符号长整数)。
    例子
unsigned long time;
void setup(){
 Serial.begin(9600);
}
void loop(){
serial.print(“Time:”);
time = millis();
//打印从程序开始到现在的时间
serial.println(time);
//等待一秒钟,以免发送大量的数据
 delay(1000);
}

提示
注意,参数 millis 是一个无符号长整数,试图和其他数据类型(如整型数)做数学运算可能会产生错误。
当中断函数发生时,millis()的数值将不会继续变化。

  1. micros()
    描述
    返回 Arduino 开发板从运行当前程序开始的微秒数。这个数字将在约70分钟后溢出(归零)。在 16MHz 的 Arduino 开发板上(比如 Duemilanove 和 Nano),这个函数的分辨率为四微秒(即返回值总是四的倍数)。在 8MHz 的 Arduino 开发板上(比如 LilyPad),这个函数的分辨率为八微秒。
    注意 :每毫秒是1,000微秒,每秒是1,000,000微秒。
    参数

    返回
    返回从运行当前程序开始的微秒数(无符号长整数)。
    例子
unsigned long time;
void setup(){
 Serial.begin(9600);
}
void loop(){
Serial.print(“Time:”);
time = micros();
//打印从程序开始的时间
Serial.println(time);
//等待一秒钟,以免发送大量的数据
 delay(1000);
}
  1. delay()
    描述
    使程序暂定设定的时间(单位毫秒)。(一秒等于1000毫秒)
    语法
    delay(ms)
    参数
    ms:暂停的毫秒数(unsigned long)
    返回

    例子
ledPin = 13 / / LED连接到数字13脚
void setup()
{
pinMode(ledPin, OUTPUT); // 设置引脚为输出
}
void loop()
{
digitalWrite(ledPin, HIGH); // 点亮LED
delay(1000); // 等待1秒
digitalWrite(ledPin, LOW); // 灭掉LED
delay(1000); // 等待一秒
}

警告
虽然创建一个使用delay()的闪烁LED很简单,并且许多例子将很短的delay用于消除开关抖动,delay()确实拥有很多显著的缺点。在delay函数使用的过程中,读取传感器值、计算、引脚操作均无法执行,因此,它所带来的后果就是使其他大多数活动暂停。其他操作定时的方法请参加millis()函数和它下面的例子。大多数熟练的程序员通常避免超过10毫秒的delay(),除非arduino程序非常简单。
但某些操作在delay()执行时任然能够运行,因为delay函数不会使中断失效。通信端口RX接收到得数据会被记录,PWM(analogWrite)值和引脚状态会保持,中断也会按设定的执行。

  1. delayMicroseconds()
    描述
    使程序暂停指定的一段时间(单位:微秒)。一秒等于1000000微秒。 目前,能够产生的最大的延时准确值是16383。这可能会在未来的Arduino版本中改变。对于超过几千微秒的延迟,你应该使用delay()代替。
    语法
    delayMicroseconds(us)
    参数
    us:暂停的时间,单位微秒(unsigned int)
    返回

    例子
int outPin = 8; // digital pin 8
void setup()
{
pinMode(outPin,OUTPUT); //设置为输出的数字管脚
}
void loop()
{
digitalWrite(outPin,HIGH); //设置引脚高电平
delayMicroseconds(50); // 暂停50微秒
digitalWrite(outPin, LOW); // 设置引脚低电平
delayMicroseconds(50); // 暂停50微秒
}

将8号引脚配置为输出脚。它会发出一系列周期100微秒的方波。
警告和已知问题
此函数在3微秒以以上工作的非常准确。我们不能保证,delayMicroseconds在更小的时间内延时准确。
Arduino0018版本后,delayMicroseconds()不再会使中断失效。

五、数学运算

  1. min()
    min(x, y)
    描述
    计算两个数字中的最小值。
    参数
    X:第一个数字,任何数据类型
    Y:第二个数字,任何数据类型
    返回
    两个数字中的较小者。
    举例
sensVal = min(sensVal,100); //将 sensVal 或 100 中较小者赋值给 sensVal
//确保它永远不会大于 100。

注释
直观的比较,max() 方法常被用来约束变量的下限,而 min() 常被用来约束变量的上限。
警告
由于 min() 函数的实现方式,应避免在括号内出现其他函数,这将导致不正确的结果。
min(a++, 100); //避免这种情况 - 会产生不正确的结果
a++;
min(a, 100); //使用这种形式替代 - 将其他数学运算放在函数之外

  1. max()
    max(x,y)
    描述
    计算两个数的最大值。
    参数
    X:第一个数字,任何数据类型
    Y:第二个数字,任何数据类型
    返回
    两个参数中较大的一个。
    例子
sensVal = max(senVal, 20); // 将20或更大值赋给sensVal
//(有效保障它的值至少为20)

注意
和直观相反,max()通常用来约束变量最小值,而min()通常用来约束变量的最大值。
警告
由于max()函数的实现方法,要避免在括号内嵌套其他函数,这可能会导致不正确的结果。
max(a–, 0); //避免此用法,这会导致不正确结果
a–; // 用此方法代替
max(a, 0); // 将其他计算放在函数外

  1. abs()
    ABS(X)
    描述
    计算一个数的绝对值。
    参数
    X:一个数
    返回
    如果x大于或等于0,则返回它本身。 如果x小于0,则返回它的相反数。
    警告
    由于实现ABS()函数的方法,避免在括号内使用任何函数(括号内只能是数字),否则将导致不正确的结果。
    ABS(a+ +); //避免这种情况,否则它将产生不正确的结果
    a ++; //使用这段代码代替上述的错误代码
    ABS(a); //保证其他函数放在括号的外部
  2. constrain()
    描述
    将一个数约束在一个范围内
    参数
    x:要被约束的数字,所有的数据类型适用。
    a:该范围的最小值,所有的数据类型适用。
    b:该范围的最大值,所有的数据类型适用。
    返回值
    x:如果 x是介于 a 和 b之间
    a:如果 x小于a
    b:如果 x大于b
    例子
sensVal = constrain(sensVal, 10, 150);
//传感器返回值的范围限制在10到150之间
  1. map()
map(value, fromLow, fromHigh, toLow, toHigh)

描述
将一个数从一个范围映射到另外一个范围。也就是说,会将 fromLow 到 fromHigh 之间的值映射到 toLow 在 toHigh 之间的值。
不限制值的范围,因为范围外的值有时是刻意的和有用的。如果需要限制的范围, constrain() 函数可以用于此函数之前或之后。
注意,两个范围中的“下限”可以比“上限”更大或者更小,因此 map() 函数可以用来翻转数值的范围,例如:

y = map(x, 1, 50, 50, 1);

这个函数同样可以处理负数,请看下面这个例子:

y = map(x, 1, 50, 50, -100);

是有效的并且可以很好的运行。
map() 函数使用整型数进行运算因此不会产生分数,这时运算应该表明它需要这样做。小数的余数部分会被舍去,不会四舍五入或者平均。
参数
value:需要映射的值
fromLow:当前范围值的下限
fromHigh:当前范围值的上限
toLow:目标范围值的下限
toHigh:目标范围值的上限
返回值
被映射的值。
例子

/映射一个模拟值到8位(0到255)/
void setup(){}
void loop()
{
int val = analogRead(0);
val = map(val, 0, 1023, 0, 255);
analogWrite(9, val);
}

附录:

关于数学的实现,这里是完整函数

long map(long x, long in_min, long in_max, long out_min, long out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
  1. pow()
    pow(base, exponent)
    描述
    计算一个数的幂次方。Pow()可以用来计算一个数的分数幂。这用来产生指数幂的数或曲线非常方便。
    参数
    base:底数(float)
    exponent:幂(float)
    返回
    一个数的幂次方值(double)
    例子
    详情见 库代码中的fscale函数。
  2. sqrt()
    sqrt(x)
    描述
    计算一个数的平方根。
    参数
    x:被开方数,任何类型
    返回值
    此数的平方根,类型double
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 225,165评论 6 523
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,476评论 3 405
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 172,446评论 0 368
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 61,157评论 1 301
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,164评论 6 400
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,615评论 1 316
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,969评论 3 430
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,959评论 0 279
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,495评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,529评论 3 347
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,641评论 1 355
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,233评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,976评论 3 340
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,407评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,552评论 1 277
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,218评论 3 381
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,715评论 2 366

推荐阅读更多精彩内容