1. 程式人生 > >Linkit 7688 DUO(四): 接上各種Arduino感測器和模組——基礎篇

Linkit 7688 DUO(四): 接上各種Arduino感測器和模組——基礎篇

Linkit 系列博文:

前一篇講了 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能顯示切換兩種顏色

 

模組有三個管腳,其中  (圖中左側)標註‘-’的管腳接地(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顯示任意顏色


模組有四個管腳,其中  (圖中右側)標註‘-’的管腳接地(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),   見下圖,


模組有三個管腳,其中  (圖中右側)標註‘-’的管腳接地(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時,繼電器使外部開關開啟或關閉。 繼電器常用於控制其它電器、電動機等。

繼電器模組見下圖:


圖中:模組下方有三個管腳,其中  (圖中右側)標註‘-’的管腳接地(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), 見下圖


蜂鳴器可以用來發出“嘟嘟”聲,也可以發出簡單的音樂聲音。

蜂鳴器模組有三個管腳,其中  (圖中右側)標註‘-’的管腳接地(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 ...等頻率值,是音符對應的頻率。其它音符可以百度一下 “音符與頻率的對照表”。

有興趣的童鞋,可以據此寫一個播放音樂的小程式了。

相關推薦

Linkit 7688 DUO(): 各種Arduino感測器模組——基礎

Linkit 系列博文: 前一篇講了 Linkit 7688DUO操作Arduino的原理和基本方法。現在,我們要為開發板接上各類Arduino的感測器和模組了,這些模組提供了各類輸入輸出。 一、首先要充分了解 Linkit 7688 DUO開發板的引出管

應用程式框架實戰十:DDD分層架構之領域實體(基礎

using System.ComponentModel.DataAnnotations; using System.Text; namespace Util.Domains { /// <summary> /// 領域實體 /// </summary

實現HTTP協議Get、Post檔案傳功能——設計模組

        本系列不再將技術限定於WinHttp介面,還引入curllib庫。同時為了公正且方便測試程式碼的正確性,我們將引入成熟的技術方案進行測試。 測試環境         使用Python搭建一個Http伺服器,用於檢測Get和Post請求。    

真的懂了:TCP協議中的三次握手和四次揮手(關閉連時, 當收到對方的FIN報文時, 僅僅表示對方不在發送數據了, 但是還能接收數據, 己方也未必全部數據都發送對方了。相當於一開始還沒接上話不要緊,後來接上話以後得讓人把話講完)

流程圖 .cn 服務 soc knowledge ber tcp連接 是什麽 一次 一、TCP報文格式   下面是TCP報文格式圖:              (1) 序號, Seq(Sequence number), 占32位,用來標識從TCP源端向目的端發送的字節

Java編碼(三)——Java網路I/O(JavaWeb)的編碼解碼過程(

在JavaWeb中涉及的編碼解碼的方面:   使用者想伺服器傳送一個HTTP請求,需要編碼的地方有url、cookie、parameter,經過編碼後伺服器接受HTTP請求,解析HTTP請求,然後對url、cookie、parameter進行解碼。在伺服器進行業務邏輯處理過程中可能需要讀取資

沒有顯示器如何SSH連樹莓派

wpa 空白 etc 虛擬機 linux虛擬機 保存 退出 顯示器 燒錄 1.在用讀卡器燒錄系統後先用Linux虛擬機連接上讀卡器,修改 sudo gedit /etc/wpa_supplicant/wpa_supplicant.conf加入 network={ ss

[書一回]在Oracle Enterprise Linux (v5.7) 中安裝DB - (4/4)

學習 查詢 src log 成功 尋找 image 需要 密碼 選擇自己創建的安裝數據庫路徑。 Sample Schemas 打鉤。 調整內存大小。 選擇官方建議的字符集編碼。 是否創建創建的腳本,如需要請

[書一回]在Oracle Enterprise Linux (v5.7) 中安裝DB - (3/4)

www 數據庫 splay spl x86-64 width .html nbsp eight 安裝p10404530_112030_Linux-x86-64_6of7.zip解壓下的example。 修改軟件路徑,為dbhome_1.

checkbox數據的帶入

append his clas treelist inf n) cnblogs es2017 str 首先帶回回來的json數據 先轉換為樹形結構 才能方便之後遍歷 添加數據,這是一段遞歸把數據變為樹形結構的方法 // 把返回到LIST轉為樹形結構 function

Linux無法連127.0.0.1,拒絕連,更新時提示無法下載,無法正常使用apt-get update

tab pda 忽略 update ber mirrors 情況 vscode keys 你是否遇到過這種情況,在Linux以apt-get update 時更新的時候無法更新,提示一下內容 p { margin-bottom: 0.25cm; line-height: 1

2

pen break ack demo main 代碼 scan ++ next 分析以下需求,並用代碼實現: (1)從鍵盤循環錄入錄入一個字符串,輸入"end"表示結束 (2)將字符串中大寫字母變成小寫字母,小寫字母變成大寫字母,其它字符用"*"代替,並統計字母的個數 舉

關於Jedis無法連LinuxRedis問題

cte bsp 無法連接 進行 服務 無法 -m 說明 ble 環境:CentOS7、Redis 主要解決Jedis客戶端無法連接Linux上Redis服務問題 1、修改Redis目錄下的redis.conf配置文件 註釋掉bind本地回環地址:bin

Linux並發連百萬的配置

servers tlb lin window pan bucket mach ast hint To support over 500k users, you *need* - A 64 bits hardware/kernel (AMD64, Opterons) - A

css基礎 text-decoration 鼠標放超鏈變色並加下劃線

ide ack 學習資源 meta tca ebr www https :hover 禮悟:   公恒學思合行悟,尊師重道存感恩。葉見尋根三返一,江河湖海同一體。 虛懷若谷良心主,願行無悔給最苦。讀書鍛煉養身心,誠勸且行且珍惜。   

昂達 v891 連adb 調試

文件 平板 log 嘗試 win ice 顯示 tar aid USB線連上之後,可以直接訪問文件,所以連接本身肯定沒有問題的。 可是,總是Windows 那邊MTP驅動裝不上,導致adb devices 顯示沒有設備文件。 嘗試這用下面的方法安裝 MTP 驅動,看起來

js備戰春招ののdevtool中各種錯誤、調試的使用技巧

gpo 拋出異常 具體步驟 執行 reserve 數字 racket 異常 沒有 try 語句允許我們定義在執行時進行錯誤測試的代碼塊。 catch 語句允許我們定義當 try 代碼塊發生錯誤時,所執行的代碼塊。 JavaScript 語句 try 和 catch 是成對出

python 闖關之路)(並發編程與數據庫理論)

nsa 更新數據 主線程 數值類型 基礎設施 環境 文件路徑 他還 組合 並發編程重點: 並發編程:線程、進程、隊列、IO多路模型 操作系統工作原理介紹、線程、進程演化史、特點、區別、互斥鎖、信號、 事件、join、GIL、進程間通信、管道、隊列。 生產者消息者模型、

2.Keepalived介紹 (keepalived安裝配置並測試)

cpu ash 說明 多臺 curl sta 配置 keepaliv ipaddress 2.Keepalived介紹在這裏我們使用Keeplived來實現高可用集群,因為heartbeat在centos6上有一些問題,影響實驗效果(切換不及時問題)keepalived通過

TCP建立連的三次握手TCP連斷開的次揮手

bubuko 信息 發送數據 數據 可靠的 註意 過程 接收 告訴 1. TCP建立連接的3次握手 2. TCP斷開連接的四次揮手 【註意】中斷連接端可以是Client端,也可以是Server端。 圖3

,php生成靜態頁面,加上頁面時間緩存

art color 修改 sel rom require while execute ror <?php require_once(dirname(__FILE__).‘/include/config.inc.php‘); ?> <?php $