Linkit 7688 DUO(四): 接上各种Arduino传感器和模块——基础篇

Linkit 系列博文:

联发科Linkit 7688 (一) 上手及在Mac下搭建OpenWrt交叉编译环境,C语言编译Hello,World

联发科Linkit 7688 (二)GPIO基本操作与C语言编程

联发科Linkit 7688 DUO(三): 通过 Arduino 控制外设和传感器

Linkit 7688 DUO(四): 接上各种Arduino传感器和模块——基础篇

Linkit 7688 DUO(五) 接上各种Arduino传感器和模块—扩展篇

Linkit 7688 DUO(六) 加入MQTT物联网协议


前一篇讲了 Linkit 7688DUO操作Arduino的原理和基本方法。现在,我们要为开发板接上各类Arduino的传感器和模块了,这些模块提供了各类输入输出。


一、首先要充分了解 Linkit 7688 DUO开发板的引出管脚

      Linkit 7688 DUO开发板上有两个处理器芯片。

       一片是  Linkit 7688, 主处理器

       一片是  ATmega32U4,  这是Arduino的处理芯片,提供Arduino编程接口,用于控制传感器外设等

      两个处理器通过内部串口相连。

      在开发中, 要写两个程序:

           1, 写一个Arduino程序, 写入ATmega32U4中。Arduino程序通过串口接收主处理器Linkit 7688送来的命令, 执行相应动作。

           2, 写一个主程序,写入Linkit7688中。 主程序通过串口向 ATmega32U4 发送命令


      开发板有两排管脚,查了管脚说明书。我做了一个简图如下:




图中标识的 从ATmega32U4管脚接出的众多管脚,其中:D0-D13 为数字IO口, A0-A5为模拟IO口, S0-S3为串口设备SPI管脚

还有几个电源口:  GND(地),  3.3V,  5V


二、获得Arduino的传感器模块

      淘了一套 科易(Keyes)的Arduino传感器套装,37种传感器,70元。便便宜宜,够玩一下了。

      为了把传感器模块接到开发板上,还要买一把杜邦线。

      本篇选主要的几种传感器,连接Linkit 7688 DUO开发板, 练习如何连线,如何控制输入输出

      

三、双色LED灯 ( TWO-COLOR),  见下图,这种LED能显示切换两种颜色

Linkit 7688 DUO(四): 接上各种Arduino传感器和模块——基础篇_第1张图片 


模块有三个管脚,其中  (图中左侧)标注‘-’的管脚接地(GND),(图中右侧)标注"S"的管脚接信号(Signal), 即接在数字IO口上。 中间的管脚接3.3V

用杜邦线把模块三个脚分别接到开发板上,其中把信号线接到 D8 口.   这时LED灯已经亮起,为橙色。

 


编一个Arduino程序,用来控制D8脚的LED灯, 编译上传到ATmegaU32

int pin = 8; //pin connected

void setup() { 
Serial1.begin(57600); //internal serial to MT7688 
pinMode(pin, OUTPUT); //set pin output mode
}

void loop() { 
  int c = Serial1.read(); // read from MT7688
  if (c != -1) {
    switch(c) { 
      case '0': // turn off when receiving "0"
        digitalWrite(pin, 0); 
        break; 
      case '1': // turn on when receiving "1" 
        digitalWrite(pin, 1); 
        break; 
    } 
  } 
}

编一个C语言主程序 serial_test.c,用来向ATmegaU32串口发送命令, 交叉编译后生成serial_test,用scp命令上传到开发板

#include <unistd.h>
#include "serial.h"

int main() {
	int fd;
	char c;

	fd = serial_open(0, 57600);//open serial port 0 : /etc/ttyS0
	if ( fd > 0 ) {

		while (1) {
			c = '0';
			serial_send(fd, &c, 1);  //send '0'
			sleep(1);

			c = '1';
			serial_send(fd, &c, 1); //send '1'
			sleep(1);
		}

		serial_close(fd);
	}

}


SSH进入开发板,执行 serial_test,  则可以看到,双色LED灯每隔一秒换一种颜色(橙-->绿-->橙...)

这个程序是死循环,按 CTRL+C 可中断执行



四、三色LED灯 ( RGB LED ),   见下图,通过设置R, G, B三种颜色值,可以使这种LED显示任意颜色

Linkit 7688 DUO(四): 接上各种Arduino传感器和模块——基础篇_第2张图片

模块有四个管脚,其中  (图中右侧)标注‘-’的管脚接地(GND), 标注"R", "G", "B"的三个管脚要分别接到三个IO管脚上

用四根杜邦线把模块接到开发板上,其中“-”脚接GND,  RGB三个管脚分别接 D3, D5, D6口

注: ATmega32U4芯片的 3、5、6、9、10、11、13管脚 能使用 Arduino 中的 analogWrite()函数支持8位的PWM输出, PWM管脚可用于输出0-255的不同亮度值


在双色LED例子中,Linkit 7688一次送一个字节的命令给ATmegaU32就可以了,串口通信比较简单。

在本例中,为了显示RGB颜色, Linkit 7688要一次送三个字节的命令给ATmegaU32,以后的例子中,可能要发送不同长度的各种命令给ATmegaU32.

因此, 需要设计一个ATmegaU32 与 Linkit 7688间的通用的串口通信协议, 用于传送比较复杂的命令.


这里提供一个工业用的串口通信 消息协议, 可以实现双向通信, 协议定义如下: 

1,  进行串口通信的两个设备间可以互发消息数据包,简称消息。

2,每个消息是一串连续的字节流,包含:开始字节、命令字节,长度字节、内容、检验字节等

3,  消息数据的格式如下:

 开始字节(1 byte) +  命令字节(1 byte)  + 内容长度( 1 byte ) + 内容(长度可变)  +  校验字节(1字节)

其中:

   开始字节,固定取值为 0x03

   命令字节,由用户自定义各种命令,共256种。   其中:COMMAND_ACK (正确响应) ,  COMMAND_NAK (不正确响应) 是本协议预定义的两种响应命令

   内容长度字节,指其后面跟随的内容的长度, 可以为0

   内容, 长度可变的一串字节

   校验字节,用于校验前面传的数据是否正确,采用BCC算法,取值为从开始字符到命令内容的异或和。

4,消息响应

    接收消息的一方收到消息后,如执行正确应回复一个ACK消息。否则应回复一个NAK消息。以便另一方了解消息是否正确。

   ACK消息以 COMMAND_ACK 为命令字节(COMMAND_ACK=1),内容长度为1, 内容为上条收到消息的命令字节。

   NAK消息以 COMMAND_NAK 为命令字节(COMMAND_NAK=0),内容长度为1, 内容为上条收到消息的命令字节。


然后,在Arduino 和 Linkit 7688主控程序中分别实现这个通信协议。


第1步,编一个Arduino程序,实现消息协议

/**************************************************
 * Message protocol over serial communication 
 * between Arduino and Linkit 7688
 *
 * message format: 
 * START_BYTE(1 byte) + COMMAND(1 byte) + LENGTH(1 byte) + DATA + CHECKSUM(1 byte)
 **************************************************/

const unsigned char COMMAND_ACK = 1; //ACK: command received and processed
const unsigned char COMMAND_NAK = 0; //NAK: command not processed

unsigned char message[258];  //message of serial communication
static const unsigned char START_BYTE = 0x03;   //start byte of message
static int delay_between_bytes = 1;  //delay between bytes

/* message initialization */
void MessageInit(long baudRate) {
  Serial1.begin(baudRate);  //init Serial1 which connect to Linkit 7688
  delay_between_bytes = 1500 / ( baudRate / 8 );
  if (delay_between_bytes <= 0)
      delay_between_bytes = 1;
}

/* calc checksum using BCC algorithm */
static unsigned char bcc_checksum(unsigned char *buf, int len, unsigned char initial)
{
  int i;
  unsigned char checksum = initial;
  for(i = 0; i < len; i++)
    checksum ^= *buf++;
  return checksum;
}

//read byte:  return 1 if read success, return 0 if fail
static int MessageReadByte(int *byte) {
  int c, times; 
  times = 3;
  c = Serial1.read(); // trying: read one byte from MT7688
  while ( c == -1  &&  times-- > 0 ) {
    delay(delay_between_bytes);
  } 
  
  if ( c == -1 ) {
    return 0;
  } else {
    *byte = c;
    delay(delay_between_bytes);
    return 1;
  }
}
  
/* receive message:  return 1 if message received OK, else return 0 */
int MessageReceive() {
  int c, n, len;
  
  if ( ! MessageReadByte( &c ) )
    return 0; 
    
  if ( c == START_BYTE ) { 
      n = 0; 
      message[ n++ ] = c;        //start byte
      
      if ( ! MessageReadByte( &c ) ) return 0; // command byte
      message[ n++ ] = c; 
      
      if ( ! MessageReadByte( &c ) ) return 0; // length byte
      message[ n++ ] = c; 
      len = c;
      
      while ( len > 0 ) {      // read data bytes
        if ( ! MessageReadByte( &c ) ) return 0;
        message[ n++ ] = c; 
        len--;
      }
      
      if ( ! MessageReadByte( &c ) ) return 0; // checksum byte
      message[ n ] = c;
      
      //verify checksum
      if ( message[n] == bcc_checksum(message, n, 0) )
        return 1;
      else
        return 0;
  } 
  return 0;
}

/* send message: return 1 if success, return 0 if fail */
int MessageSend(unsigned char command, unsigned char *data, unsigned char len) {
  unsigned char buf[3];
  unsigned char checksum;
  int ret;

  buf[0] = START_BYTE;
  buf[1] = command;
  buf[2] = len;
  
  checksum = bcc_checksum(buf, 3, 0);
  if ( len > 0 ) 
    checksum = bcc_checksum(data, len, checksum);

  if ( 3 == Serial1.write(buf, 3) ) {
    if ( len > 0 ) {
      if ( len != Serial1.write( data, len ) )
        return 0;
    }
        
    ret = Serial1.write(checksum);
    return ret == 1;
  }
  return 0;
}

/* send COMMAND_ACK of specified command */
int MessageACK(unsigned char command) {
  return MessageSend( COMMAND_ACK, &command, 1 );
}

/* send COMMAND_NAK of specified command */
int MessageNAK(unsigned char command) {
  return MessageSend( COMMAND_NAK, &command, 1 );
}


/* return command of message */
unsigned char MessageCommand() {
  return message[1];
}

/* return length of message data */
unsigned char MessageDataLength() {
  return message[2];
}

/* return value of specified index of message data */
unsigned char MessageData(int index) {
  return message[ 3 + index ];
}
/******** End of Message Protocol **************/

程序有点长,稍微说明一下:

   unsigned char message[258];  是一个字符数组,用于接收消息的。

   void MessageInit(long baudRate);初始化函数,必须且只需调用一次

   int MessageReceive() ;  接收消息函数,如果接收成功返回1, 此时接收到的数据在message[]中。 如果接收不成功,返回0

          这个函数会先尝试接收一个字节,如果接收到 开始字节,则立即接收后续内容。如果字节间超时,则中断接收。

   int MessageSend(unsigned char command, unsigned char *data, unsigned char len) ;发送消息函数,command是命令,data是内容

   int MessageACK(unsigned char command); 发送ACK消息的函数

   unsigned char MessageCommand();  返回刚才接收到的消息中的命令字节

   unsigned char MessageData(int index); 返回刚才接收到的消息内容 的第index字节


我把上述程序存入一个文件  message_protocol.ino 中(以便重用)。

把 message_protocol.ino 这个文件放在Arduino的任何一个项目文件夹中,然后重新打开项目, 则该项目将自动包含message_protocol.ino模块及上述函数。


第2步,编写一个Arduino主程序,接收消息,控制RGB 三色 LED灯。  把以下Arduino程序编译上传到ATmegaU32


/**************************************************
 *  main program
 **************************************************/

const char COMMAND_RGB = 'r';

int pinRed   = 3;  //PIN 3 connect to Red
int pinGreen = 5;  //PIN 5 connect to Red
int pinBlue  = 6;  //PIN 6 connect to Red


void setup() {
  MessageInit(57600);  //init message protocol, set baud rate
  
  //set pins mode to OUPTPU
  pinMode( pinRed,   OUTPUT );
  pinMode( pinGreen, OUTPUT );
  pinMode( pinBlue,  OUTPUT );
}


void setLedColor(int red, int green, int blue) {
  analogWrite( pinRed,   red );
  analogWrite( pinGreen, green );
  analogWrite( pinBlue,  blue );
} 


void loop() {

  if ( MessageReceive() ) { //if message received
    
    switch( MessageCommand() ) {  //get command of message
    
    case COMMAND_RGB:  //if command is COMMAND_RGB
        setLedColor( MessageData(0), MessageData(1), MessageData(2) ); //set LED color
        MessageACK( COMMAND_RGB );  //send ACK message back
        break;
    } 
    
  }
  
  delay(1); //wait 1 milli-second
}

setup()函数中,要调用一次MesasgeInit()。  loop()函数中,当接收到 COMMAND_RGB, 将调用setLedColor()设置LED灯颜色, 并发送ACK消息。



第3步 编一个C语言模块 message_protocol.c ,实现消息协议。具体我就不详细说了,可以下载代码看一下。

        模块共两个文件:  message_protocol.c ,message_protocol.h,  其中包含的函数与Arduino的函数基本相同,但调用方式和函数命名方式略有不同。(见以下例程)


第4步 编一个C语言主程序 rgb_led.c,  向ATmegaU32发送消息, 将C程序交叉编译后生成rgb_led,用scp命令上传到开发板

             项目中要使用到  串口函数模块serial.c 和 消息协议模块 message_protocol.c

#include <stdio.h>
#include <unistd.h>
#include <memory.h>
#include "serial.h"
#include "message_protocol.h"

#define COMMAND_RGB 'r'

//send COMMAND_RGB message to Arduino
int  send_rgb_message(message_t * msg, int r, int g, int b) {
	unsigned char buf[3];
	buf[0] = r;
	buf[1] = g;
	buf[2] = b;
	return message_send(msg, COMMAND_RGB, buf, 3);
}

//wait for ACK message from Arduino
int wait_ack_message(message_t * msg) {
	int times, ret;

	times = 0;
	while ( (ret = message_receive(msg)) != 1 && times++ < 30) {
		usleep(1*1000);
	}

	if ( ret == 1 ) {
		if ( message_command(msg) == COMMAND_ACK ) {
			printf("receive ACK\n");
			fflush(stdout);
		}
	}
}


int main(int argc, char **argv) {
	int fd;   //file descriptor of serial port
	message_t msg; //message object


	fd = serial_open(0, 57600);//open serial port 0, /etc/ttyS0
	if ( fd > 0 ) {
		message_init( &msg, fd, 57600 ); //init message with fd, set baud rate

		send_rgb_message( &msg, 255, 0, 0) ; //set LED color RED
		wait_ack_message( &msg );  //wait for ACK message
		sleep(1);

		send_rgb_message( &msg, 0, 255, 0) ; //set LED color GREEN
		wait_ack_message( &msg );  //wait for ACK message
		sleep(1);


		send_rgb_message( &msg, 0, 0, 255) ; //set LED color BLUE
		wait_ack_message( &msg );  //wait for ACK message
		sleep(1);


		send_rgb_message( &msg, 255, 255, 0) ; //set LED color YELLOW
		wait_ack_message( &msg );  //wait for ACK message
		sleep(1);

		send_rgb_message( &msg, 255, 0, 255) ; //set LED color PURPLE
		wait_ack_message( &msg );  //wait for ACK message
		sleep(1);

		send_rgb_message( &msg, 0xFF, 0xC0, 0xCB) ; //set LED color PINK
		wait_ack_message( &msg );  //wait for ACK message
		sleep(1);

		send_rgb_message( &msg, 0, 0, 0) ; //set LED turn off
		wait_ack_message( &msg );  //wait for ACK message

		serial_close(fd);
	}

	return 0;
}


SSH进入开发板,执行 rgb_led,  则可以看到,三色LED灯每隔一秒换一种颜色(红-->绿-->蓝->黄->紫色->粉红..熄灯)



五、接键开关(Switch),   见下图,


Linkit 7688 DUO(四): 接上各种Arduino传感器和模块——基础篇_第3张图片


模块有三个管脚,其中  (图中右侧)标注‘-’的管脚接地(GND), 右侧标注"S”的管脚接信号(数字I/O) , 中间的管脚接 5V

用三根杜邦线把模块接到开发板上,其中“-”脚接GND,  S脚接 D3,  中间脚接5V

编一个Arduino程序,监控开关状态,如有状态变化,通过串口发送消息到到主控板。 编译Arduino程序上传到ATmegaU32

注:程序将使用message_protocol.ino模块,要把 message_protocol.ino 这个文件放在本Arduino项目文件夹中, 然后重新打开项目即可包含此模块

#define COMMAND_SWITCH 's'

int pinSwitch = 3;  // digital pin 3 has a pushbutton attached to it
int lastValue = 1;  // last value of pin

void setup() {
  MessageInit( 57600 ); // init message < need message_protocol.ino >
  pinMode(pinSwitch, INPUT); //set pin as INPUT
  lastValue = digitalRead(pinSwitch); //get last value
}


void loop() {
  int value = digitalRead(pinSwitch);  // read the input pin:
  if ( value != lastValue ) {   //if value changed 
    unsigned char c = value & 0xFF; //change to unsigned char
    MessageSend( COMMAND_SWITCH, &c, 1); //send message 
    lastValue = value;  //store last value
  }
  delay(10);        // delay in between reads for stability
}


然后,编一个C语言主程序 switch_test.c,监听串口有否消息送到,打印出按键值。  将C程序交叉编译后生成switch_test,用scp命令上传到开发板

注:项目中要使用到  串口函数模块serial.c 和 消息协议模块 message_protocol.c

#include <stdio.h>
#include <unistd.h>
#include <memory.h>
#include "serial.h"
#include "message_protocol.h"

#define COMMAND_SWITCH 's'

int main(int argc, char **argv) {
	int fd;   //file descriptor of serial port
	message_t msg; //message object


	fd = serial_open(0, 57600);//open serial port 0, /etc/ttyS0
	if ( fd > 0 ) {
		message_init( &msg, fd, 57600 ); //init message with fd, set baud rate

		//loop
		while ( 1 ) {

			if ( 1 == message_receive(&msg) ) {  //if received message
				if ( message_command(&msg) == COMMAND_SWITCH ) {
					printf("switch value = %d\n", message_data(&msg, 0 ) ); //print value
					fflush(stdout);
				}

			}

			usleep(10*1000); //wait 10 milli-seconds
		}

		serial_close(fd);
	}

	return 0;
}

主程序不断监测有否消息送到,如为 COMMAND_SWITCH ,则打印出第0字节的数据值

SSH进入开发板,执行 switch_test,   每次按下按钮时,主程序收到消息并打印一行 switch value = 0。 弹起按钮时主程序收到消息并打印一行 switch value = 1
就是说,这个开关模块的值是:按下为0, 弹起为1
运行结果如下:

switch value 0

switch value 1

switch value 0

switch value 1

switch value 0


主程序是死循环,在SSH中按下CTRL+C可中断程序




六、继电器模块(RELAY)

继电器是一种电子开关元件。数字I/O管脚的输出电压常为直流3.3V,不能直接驱动交流220V的开关,因此需要继电器模块来驱动外部开关。当数字I/O输出0或1时,继电器使外部开关打开或关闭。 继电器常用于控制其它电器、电动机等。

继电器模块见下图:

Linkit 7688 DUO(四): 接上各种Arduino传感器和模块——基础篇_第4张图片


图中:模块下方有三个管脚,其中  (图中右侧)标注‘-’的管脚接地(GND), 标注"+”的管脚接 5V, (图中左侧)标注‘S'的管脚接信号(数字I/O)
模块上方是一个继电器,继电器有三个接线口(图中0, 1, 2),用电表量一下,0和1接线口之间是通的(常闭),0和2接线口之间是不通的(常开)

用三根杜邦线把继电器模块的三个管脚接到开发板上,其中“-”脚接GND,  ‘+’脚接5V,  S脚接 D3

编一个Arduino程序,采用消息协议,根据接收的消息内容,设置 继电器信号脚D3为0或1。 编译Arduino程序上传到ATmegaU32

注:程序将使用message_protocol.ino模块,要把 message_protocol.ino 这个文件放在本Arduino项目文件夹中, 然后重新打开项目即可包含此模块

#define COMMAND_RELAY 'l'

int pinRelay = 3;  // digital pin 3 has a relay module attached to it
int lastValue = 1;  // last value of pin

void setup() {
  MessageInit( 57600 ); // init message < need message_protocol.ino >
  pinMode(pinRelay, OUTPUT); //set pin as OUTPUT
}


void loop() {

  if ( MessageReceive() ) {   //if message is read
    if ( MessageCommand() == COMMAND_RELAY ) { //if is COMMAND_RELAY
      int value = MessageData(0); //get value from message data
      digitalWrite(pinRelay, value); //output to pin of relay
      MessageACK( COMMAND_RELAY );  //send ACK back
    }
  }
  
  delay(1);        // delay in between reads for stability
}


编一个C语言主程序 relay_test.c,采用串口消息协议,发消息控制继电器开关。  交叉编译后生成relay_test,用scp命令上传到开发板

注:项目中要使用到  串口函数模块serial.c 和 消息协议模块 message_protocol.c

#include <stdio.h>
#include <unistd.h>
#include <memory.h>
#include "serial.h"
#include "message_protocol.h"


#define COMMAND_RELAY 'l'

int main(int argc, char **argv) {
	int fd;   //file descriptor of serial port
	message_t msg; //message object
	unsigned char value;


	fd = serial_open(0, 57600);//open serial port 0, /etc/ttyS0
	if ( fd > 0 ) {
		message_init( &msg, fd, 57600 ); //init message with fd, set baud rate

		value = 1;
		message_send( &msg, COMMAND_RELAY, &value, 1); //send COMMAND_RELAY message,value=1

		sleep(10); //wait 10 seconds

		value = 0;
		message_send( &msg, COMMAND_RELAY, &value, 1); //send COMMAND_RELAY message,value=0

		serial_close(fd);
	}

	return 0;
}

SSH进入开发板,执行 relay_test,   当主程序向 串口发 COMMAND_RELAY消息,值为1时, 则听到 ”啪“的一声,继电器闭合,用电表一量,继电器的0与1接线口之间从闭合(通)变为打开(不通),继电器的0与2接线口从打开(不通)变为闭合(通)。

隔10秒后,当主程序向 串口发 COMMAND_RELAY消息,值为0时,再次听到 ”啪“的一声,继电器还原。用电表一量,继电器的0与1接线口之间恢复为闭合(通),继电器的0与2接线口恢复为打开(不通)


继电器的特性: 有一对常开、常闭的开关,当信号脚设置为HIGH,则常开变常闭,常闭变常开。

把继电器串联在灯泡电路上,则可以实现用开发板控制电灯开和关了。

要注意继电器上标明的最大电压和电流,不要超,否则继电器会烧掉。我的继电器是: 250V AC, 10A电流。除了大功率空调,一般的家电都可以带。


七、蜂鸣器模块(BUZZER), 见下图

Linkit 7688 DUO(四): 接上各种Arduino传感器和模块——基础篇_第5张图片


蜂鸣器可以用来发出“嘟嘟”声,也可以发出简单的音乐声音。


蜂鸣器模块有三个管脚,其中  (图中右侧)标注‘-’的管脚接地(GND), 中间的管脚接 5V , (图中左侧)标注"S"的管脚接信号(数字I/O)
用三根杜邦线把模块接到开发板上,其中“-”脚接GND, 中间管脚接5V, "S"脚 接 D3口 ( D3口可以用作 PWM)

编一个Arduino程序,采用消息协议,当接到 COMMAND_BUZZER消息,用 tone()函数通过D3口写蜂鸣器,发出指定频率的声音。

注:程序将使用message_protocol.ino模块,要把 message_protocol.ino 这个文件放在本Arduino项目文件夹中, 然后重新打开项目即可包含此模块

这里先定义一下  COMMAND_BUZZER消息, 该消息的内容数据共4字节,前两个字节是 声音频率(高位在前),后两个字节是声音时长(毫秒,高位在前)

Arduino程序如下,将程序编译上传到ATmegaU32

#define COMMAND_BUZZER 'b'

int pinBuzzer = 3; //pin 3 connects to Buzzer module

void setup() {
  MessageInit(57600); //init message
  pinMode(pinBuzzer, OUTPUT); //set pinBuzzer OUTPUT
}

void loop() { 
   if ( MessageReceive() ) { //if  message received
   
     if ( MessageCommand() == COMMAND_BUZZER ) { //if is COMMAND_BUZZER
        //get frequecy from first two bytes in data 
        long frequency = (MessageData(0) << 8) + MessageData(1); 
        //get duration, the duration of the tone in milliseconds
        long duration = (MessageData(2) << 8) + MessageData(3);  
                
        if ( frequency == 0 )
           noTone(pinBuzzer);
        else
           tone(pinBuzzer, frequency, duration );
     }
   } 
   
}

编一个C语言主程序 music_test.c,采用消息协议,向Arduino发送COMMAND_BUZZER,播放音乐。 交叉编译后生成music_test,用scp命令上传到开发板

注:项目中要使用到  串口函数模块serial.c 和 消息协议模块 message_protocol.c

#include <stdio.h>
#include <unistd.h>
#include "serial.h"
#include "message_protocol.h"

#define COMMAND_BUZZER 'b'

/**
 * send message to Arduino to play tone of specified frequency in specified duration
 *
 * @param msg  message handle
 * @param frequency tone frequency
 * @param duration  the duration of the tone in milliseconds
 * @param imediately_return if this value is 1, this function will return immediately
 *
 * @return none
 */
void play_tone(message_t *msg, int frequency, int duration, int imediately_return) {
	unsigned char data[4];

	//byte 0 and 1 is frequecy
	data[0] = (frequency & 0xFF00) >> 8;
	data[1] = (frequency & 0xFF);

	//byte 2 and 3 is duration
	data[2] = (duration & 0xFF00) >> 8;
	data[3] = (duration & 0xFF);

	//send message to Arduino
	message_send(msg, COMMAND_BUZZER, data, 4);

	if ( imediately_return != 1 )
	  usleep( duration  * 1000);
}

int main(int argc, char **argv) {
	int fd;   //file descriptor of serial port
	message_t msg; //message object
	int duration = 600;

	fd = serial_open(0, 57600);//open serial port 0, /etc/ttyS0
	if ( fd > 0 ) {
		message_init( &msg, fd, 57600 ); //init message with fd, set baud rate

		play_tone(&msg, 523, duration, 0); // music tone "1"
		play_tone(&msg, 587, duration, 0); // music tone "2"
		play_tone(&msg, 659, duration, 0); // music tone "3"
		play_tone(&msg, 698, duration, 0); // music tone "4"
		play_tone(&msg, 784, duration, 0); // music tone "5"
		play_tone(&msg, 880, duration, 0); // music tone "6"
		play_tone(&msg, 988, duration, 0); // music tone "7"
		play_tone(&msg, 1046, duration * 2, 0);// music tone high "1"

		serial_close(fd);
	}

	return 0;
}

说明: 其中  play_tone() 函数向Arduino发送 COMMAND_BUZZER 消息,消息内容中指定了频率、时长


SSH进入开发板,执行 music_test.

呵呵, 蜜蜂鸣叫器发出一段音乐声:  1, 2, 3, 4, 5, 6, 7 ...   


主程序中 采用的频率值 523, 587, 659 ...等频率值,是音符对应的频率。其它音符可以百度一下 “音符与频率的对照表”。

有兴趣的童鞋,可以据此写一个播放音乐的小程序了。


相关代码可以在我的资源中下载:Linkit7688DUO开发板连接多种Ardunio模块的范例程序




你可能感兴趣的:(arduino)