Arduino介绍
Arduino是一种开放源代码的硬件平台,可以用于制作各种电子项目和原型设计。它基于易于使用的软件和硬件接口,使得非专业人士也能够轻松创建交互式电子设备。
Arduino的主要特性包括:
- 简单易用:Arduino使用C/C++编程语言,并提供了简单的API,使得编程变得容易上手。它还有一个友好的集成开发环境(IDE),具有代码编辑器、编译器和调试工具等功能。
- 开放源代码:Arduino的相关软件和硬件设计文件都是开放源代码的,可以自由获取和修改。这意味着用户可以根据自己的需求进行定制和扩展,共享自己的创意和成果。
- 丰富的库和示例:Arduino社区提供了大量的库和示例代码,涵盖了各种传感器、执行器和通信模块等常用组件。这些库和示例代码可以帮助用户快速构建项目,并提供了学习和参考的资源。
- 强大的兼容性:Arduino兼容性广泛,支持各种扩展板(称为"Shields")和传感器模块,如Wi-Fi模块、蓝牙模块、LCD显示屏等。这些扩展板可以轻松地插入到Arduino主板上,扩展其功能。
Arduino Uno介绍:
它一款广泛使用的Arduino开发板,以下是其核心技术参数:
- 微控制器:ATmega328P
- 工作电压:5V
- 输入电压(建议范围):7-12V
- 输入电压(极限):6-20V
- 数字输入/输出引脚:14个(其中6个可用作PWM输出)
- 模拟输入引脚:6个
- 直流电流每个I/O引脚:20 mA
- 直流电流所有I/O引脚总和:200 mA
- Flash存储器容量:32 KB(其中约0.5 KB由引导程序占用)
- SRAM容量:2 KB
- EEPROM容量:1 KB
- 时钟频率:16 MHz
- USB接口:用于编程和供电
- 推荐编程环境:Arduino IDE(集成开发环境)
- 支持操作系统:Windows、Mac OS X、Linux
串口通信协议
实现功能
- 读取三路0.0V~5.0V模拟量输入,读取端口A0~A2
- 设置三路0.0V~5.0V的模拟量输出,输出管脚3,5,6
- 读取三路数字量输入,读取管脚2,4,7
- 设置三路数字量输出,输出管脚8,12,13
协议格式
1)读取三路0.0V~5.0V模拟量输入:
发送:VOLTS:GET?\r\n
接受:VOLTS:GET:2.5:1.5:3.2\r\n
2)设置三路0.0V~5.0V的模拟量输出:
发送:VOLTS:SET:2.1:2.2:3.2\r\n
接受:VOLTS:SET:2.1:2.2:3.2\r\n
3)读取三路数字量输入:
发送:DIGITS:GET?\r\n
接受:DIGITS:GET:1:1:0\r\n
4) 设置三路数字量输出:
发送:DIGITS:SET:1:0:1\r\n
接受:DIGITS:SET:1:0:1\r\n
实现代码如下
/*
Serial Event 示例
当新串行数据到达时,这个程序添加它到一个String。
当一个新行被接收时,loop打印这个字符串并且清除它。
注意:serialEvent()特性在the Leonardo, Micro和其他基于ATmega32U4的版块上不可用。
*/
#define CHANNELS 3
String inputString = ""; // 一个保存到来数据的String
String outputString = ""; //一个保存发送数据的String
String num = "";
bool stringComplete = false; // 字符串是否结束
const int inputAs[CHANNELS] ={A0 , A1, A2};
float voltages[CHANNELS] = {0.0, 0.0, 0.0};
const int outputAs[CHANNELS] = {3, 5, 6};
int voltagesout[CHANNELS];
const int inputDs[CHANNELS] = {2, 4, 7};
byte dis[CHANNELS];
const int outputDs[CHANNELS] = {8, 12, 13};
byte dos[CHANNELS] = {0, 0, 0};
float convertToVoltage(int input)
{
int range = constrain(input, 0, 1023);
return 5.0 / 1024 * range;
}
int convertToIntensity(float voltage)
{
float range = constrain(voltage, 0.0, 5.0);
return (int)(range / 5.0 * 255);
}
float inverseIntensity(int intensity)
{
int range = constrain(intensity, 0, 255);
return 5.0 * intensity / 255;
}
void setup() {
for (int i = 0; i < CHANNELS; i++){
pinMode(outputAs[i], OUTPUT);
analogWrite(outputAs[i], 0.0);
pinMode(inputDs[i], INPUT);
pinMode(outputDs[i], OUTPUT);
digitalWrite(outputDs[i], LOW);
}
// 初始化串口:
Serial.begin(9600);
// 为inputString保留200个字节
inputString.reserve(200);
outputString.reserve(200);
num.reserve(20);
}
void loop() {
int index;
for (int i = 0; i < CHANNELS; i++){
voltages[i] = convertToVoltage(analogRead(inputAs[i]));
dis[i] = digitalRead(inputDs[i]);
}
// 当新行到达时,打印这个字符串
if (stringComplete) {
// Serial.println(inputString);
if (inputString.startsWith("VOLTS:GET?")){
outputString = "VOLTS:GET:";
for (int i = 0; i < CHANNELS; i++){
outputString += voltages[i];
if (i < CHANNELS - 1)
{
outputString += ":";
}
}
outputString += "\r\n";
}
else if (inputString.startsWith("DIGITS:GET?"))
{
outputString = "DIGITS:GET:";
for (int i = 0; i < CHANNELS; i++){
outputString += dis[i];
if (i < CHANNELS - 1)
{
outputString += ":";
}
}
outputString += "\r\n";
}
else if (inputString.startsWith("DIGITS:SET:")){
int end, start;
outputString = inputString;
inputString.replace("DIGITS:SET:", "");
inputString.replace("\r\n", "");
start = 0;
for (int i = 0; i < CHANNELS; i++)
{
end = inputString.indexOf(":");
inputString.setCharAt(end, ' ');
// Serial.print("end:");
// Serial.println(end);
if (end != -1){
num = inputString.substring(start, end);
start = end + 1;
}
else{
num = inputString.substring(start);
}
// Serial.println(num);
if (num.toInt() == 0 && dos[i] == 1){
dos[i] = LOW;
digitalWrite(outputDs[i], dos[i]);
}
else if (num.toInt() != 0 && dos[i] == 0){
dos[i] = HIGH;
digitalWrite(outputDs[i], dos[i]);
}
}
}
else if ( inputString.startsWith("VOLTS:SET:")){
int start, end;
float vol;
outputString = "VOLTS:SET:";
int intensity;
inputString.replace("VOLTS:SET:", "");
inputString.replace("\r\n", "");
start = 0;
for (int i = 0; i < CHANNELS; i++){
end = inputString.indexOf(":");
inputString.setCharAt(end, ' ');
if (end != -1){
num = inputString.substring(start, end);
start = end + 1;
}
else{
num = inputString.substring(start);
}
vol = num.toFloat();
intensity = convertToIntensity(vol);
if (intensity != voltagesout[i]){
voltagesout[i] = intensity;
analogWrite(outputAs[i], voltagesout[i]);
}
outputString += inverseIntensity(voltagesout[i]);
if (i < CHANNELS - 1){
outputString += ":";
}
}
outputString += "\r\n";
}
Serial.println(outputString);
// 清除这个字符串:
inputString = "";
stringComplete = false;
}
}
/*
serialEvent在一个数据在硬件串口RX上出现时发生。在每次loopp()运行之间,SerialEvent发生,因此
在loop中使用delay可以延时响应。多个字节数据可以获取。
*/
void serialEvent() {
while (Serial.available()) {
// 获取字符:
char inChar = (char)Serial.read();
// 添加它到inputString:
inputString += inChar;
// 如果到来的字符串时一个新行,设置一个标记,因此,主循环可以对它做某件事
if (inChar == '\n') {
stringComplete = true;
}
}
}
测试
pyserial的功能简介
1、打开/dev/ttyXX并设置波特率为9600, 只适用于Linux
serial = serial.Serial(‘/dev/ttyXXX’, 9600)
2、往串口里面写数据
serial.write(b“hello”)
3、读一行数据,以/n结束,要是没有/n就一直读,阻塞。
data = serial.readline()
使用python串口包pyserial进行测试
基于以上Arduino的响应消息,在Arduino上实现了基于字符串消息通信的功能。
控制客户端
基于以上串口通信协议,编写客户端控制程序,实现了对模拟量和数字量的读取和设置,如下:
- 1、DO0~DO2对应设置Arduino的数字量输出-管脚[8,12,13]。
- 2、DI0~DI2对应读取Arduino的数字量输入-管脚[2,4,7]。
- 3、AO0~AO2对应设置Arduino的模拟量输出-管脚[3,5,6]。
- 4、AI0~AI2对应设置Arduino的模拟量输入-管脚[A0,A1,A2]。
控制客户端使用EPICS IOC,具体实现请见EPICS和Arduino Uno之间基于串行文本协议的控制开发-CSDN博客