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

Posted JoStudio

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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能显示切换两种颜色

 


模块有三个管脚,其中  (图中左侧)标注‘-’的管脚接地(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

以上是关于Linkit 7688 DUO: 接上各种Arduino传感器和模块——基础篇的主要内容,如果未能解决你的问题,请参考以下文章

Linkit 7688 DUO 加入MQTT物联网协议

https://github.com/MediaTek-Labs/linkit-smart-7688-feed编译失败

LinkIt Smart 7688 建立交叉编译环境

LinkIt Smart 7688 使用官方 SDK 生成 安装包

LinkIt Smart 7688 从源码构建 bootloader (U-Boot)

毫秒分辨率计时器是LinkIT 7688