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命令上传到开发板
以上是关于Linkit 7688 DUO: 接上各种Arduino传感器和模块——基础篇的主要内容,如果未能解决你的问题,请参考以下文章
https://github.com/MediaTek-Labs/linkit-smart-7688-feed编译失败
LinkIt Smart 7688 使用官方 SDK 生成 安装包