2011年12月25日星期日

利用Arduino演示C++位运算

用10位的光条来显示二进制应该是很有趣的事情。 下面直接上代码: Exported from Notepad++
/* 免费程序,您可以任意修改与发布。 C++位运算演示程序 从串口监视工具输入数字,可以是二进制/八进制/十进制/十六进制 测试的二进制与十进制效果不错,其余进制的数未测试。 显示结果用10位的光条显示,比较直观。 已知问题: NOT运算的结果可能是错的 */ #include "converter.h" #include "String.h" const int xTyping = 0; //正在输入x值 const int yTyping = 1; //正在输入y值 String x = ""; //接收到的x字符串 String y = ""; //接收到的y字符串 int xValue = 0; //保存x值 int yValue = 0; //保存y值 int flag; //输入进度标识,输入x/输入y/输入运算符选项 int pinFirst = 29; //光条第一脚IO口位置 int pinLast = 38; //光条最后脚IO口位置 Converter converter; //声明转换类变量 void setup() { Serial.begin(9600); flag=xTyping; for (int i=pinFirst; i<=pinLast; i++) pinMode(i,OUTPUT); Diagnose(); //测试光条是否正常 } void loop() { if (Serial.available()>0) { char c; c=Serial.read(); String str; if (c=='\n') //字符串结束标识 { switch (flag) { case xTyping: Serial.print("x = "); Serial.println(x); //显示接收到的字串 Serial.print("x in binary base: "); xValue = converter.StrToInt(x); //转换成数字 str= converter.toBinary(xValue);//转换成二进制,并且用光条显示出来 Serial.println(str); displayStrWithLED(str); Serial.println(""); break; case yTyping: Serial.print("y = "); Serial.println(y); Serial.print("y in binary base: "); yValue=converter.StrToInt(y); str = converter.toBinary(yValue); Serial.println(str); displayStrWithLED(str); Serial.println(""); Serial.println("Please select the bitwise operator "); Serial.println(" 1: & bitwise AND"); Serial.println(" 2: | bitwise OR"); Serial.println(" 3: ^ bitwise XOR"); Serial.println(" 4: ~ bitwise NOT"); Serial.println(" 5: << bitwise LEFT"); Serial.println(" 6: >> bitwise RIGHT"); Serial.print(""); Serial.print("Your choice: "); } flag ++; flag=flag % 3; } else { if (flag == xTyping) //将收到的字符拼接成字符串 x +=c; else if(flag==yTyping) y+=c; else { Serial.println(c); //CHECK doBitwiseOperate(c); } } } } void displayFormula(char op, boolean full)//显示公式,第一个参数是运算符, //第二个参数是决定是否显示y,NOT运算不显示y { Serial.print(" "); Serial.println( converter.toBinary(xValue)); Serial.print(" "); switch (op) { case '<': Serial.print("<<"); break; case '>': Serial.print(">>"); break; default: Serial.print(" "); Serial.print(op); } Serial.print(" "); if (full) Serial.println( converter.toBinary(yValue)); //NOT运算不显示y else Serial.println(""); Serial.println("-----------------"); Serial.print(" = "); } void doBitwiseOperate(char opt) { String result=""; Serial.println(""); switch (opt) { case '1': //bitwise AND displayFormula('&',true); result = converter.toBinary( xValue &=yValue); break; case '2': // bitwise OR displayFormula('|',true); result = converter.toBinary(xValue |= yValue); break; case '3': //bitwise XOR displayFormula('^',true); result = converter.toBinary(xValue ^= yValue); break; case '4': //bitwise NOT Serial.println("NOT operation is not well supported yet"); //NOT运算涉及到负数,有时候会出现错误 Serial.println(""); displayFormula('~',false); yValue = ~ xValue; xValue = yValue; result = converter.toBinary(xValue ); break; case '5': //bitwise LEFT displayFormula('<',true); result = converter.toBinary(xValue << yValue); break; case '6': //bitwise RIGHT displayFormula('>',true); result = converter.toBinary(xValue>>yValue); } Serial.println(result); displayStrWithLED(result); x=""; y=""; xValue=0; yValue=0; Serial.println(""); Serial.println(""); Serial.println(""); } void Clear() { for (int i=pinFirst; i<=pinLast; i++) //清楚前面的显示结果 digitalWrite(i,LOW); } void Diagnose() //诊断光条是否正常 { for (int i=pinFirst; i<=pinLast; i++) { digitalWrite(i,HIGH); delay(50); } delay(500); for (int i=pinLast; i>=pinFirst; i--) { digitalWrite(i,LOW); delay(50); } } void displayStrWithLED(String value) { Clear(); for (int i=0; i<value.length(); i++) { if (value[i]=='1') digitalWrite(i+pinFirst,HIGH); else digitalWrite(i+pinFirst,LOW); } } 下面是转换类的代码: Exported from Notepad++
#ifndef CONVERTER_H #define CONVERTER_H #include "Arduino.h" #include "String.h" //#include "types.h" enum Alignment { alLeft, alCenter, alRight }; class Converter { private: int getActualSize(String value); public: Converter(); unsigned int StrToInt(String value); String toBinary(int value); String toBinary(int value, Alignment align); void setDelimeter(char value); }; #endif // CONVERTER_H
Exported from Notepad++
#include "converter.h" /* 将字符转换成数字 数字字串的字头 b or B for binary numbers, o or O for oct, d or D for decimal, and h or H for hex, if no flag char exists, the number would be consider a decimal. if the number entered is an illegal one, then return -1. This class could NOT handle negative numbers. */ #define errValue -1 //字串中如果有无效的字符,则返回-1 //#include <iostream> using namespace std; char delimeter; int Power(int base, int power) //This is an equivalent of pow, but this one only handle integer, however. { int v = 1; switch (power) { case 0: return 1; break; case 1: return base; break; default: for (int i = 1; i <= power; i++) v *= base; } return v; } int binStr2Int(String value, int len) { int v = 0; for (int i = 1; i <= len ; i++) { switch (value[i]) { case '0': break; case '1': v += Power(2, len - i); break; default: return errValue;// exit the function early when it runs into error } } return v; } int octStr2Int(String value, int len) { int v = 0; int c; for (int i = 1; i <= len ; i++) { c = value[i]; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': v += ((c - '0') * Power(8, len - i)); break; default: return errValue; } } return v; } int decStr2Int(String value, int len) { int v = 0; int c; c = value[0]; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': break; case 'd': case 'D': value[0] ='0'; len++; break; default: return errValue; } char t; for (int i = 0; i <= (len-1 ); i++) { t = value [i]; switch (t) { case '0': break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': v += Power(10, len -1 - i )*(t-'0'); break; default: v = errValue; } } return v; } int hexStr2Int(String value, int len) { int v = 0; int c; for (int i = 1; i <= len ; i++) { c = value[i]; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': v += ((c - '0') * Power(16, len - i)); break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': v += ((c - 'A' + 10) * Power(16, len - i)); break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': v += ((c - 'a' + 10) * Power(16, len - i)); break; default: return errValue; } } return v; } unsigned int lenOfString(String value) //字符长度 { int result =0; char terminal= value[0]; while (terminal!=delimeter) { result ++; terminal = value [result]; } switch (value[0]) { case 'b': case 'B': case 'o': case 'O': case 'd': case 'D': case 'h': case 'H': result --; } return result ; } unsigned int Converter::StrToInt(String value) { char c; unsigned int result; int len; len = lenOfString(value); c= value[0]; switch (c) { case 'b': case 'B': result = ::binStr2Int(value,len); break; case 'o': case 'O': result = octStr2Int(value, len); break ; case 'd': case 'D': result = decStr2Int(value,len); break; case 'h': case 'H': result = hexStr2Int(value,len); break; default: result = decStr2Int(value,len); } return result ; } Converter::Converter() { delimeter = '\0'; //输入时字串的结束标志 } void Converter::setDelimeter(char value) { delimeter=value ; } String Converter::toBinary(int value) { return toBinary(value, alRight); } String Converter::toBinary(int value, Alignment align) { String result="0000000000"; char bin[10]={ '0000000000' }; itoa(value,bin,2); //#define DEBUG; #ifdef DEBUG Serial.println(""); Serial.println("********************************"); Serial.println("parse bin[]"); for (int i=0; i<10; i++) { Serial.print(bin[i]); Serial.print("->"); Serial.println(bin[i],DEC); } Serial.println("length of bin[]"); int len; //len= sizeof(bin)/sizeof(bin[0]); //return 10, which is declared before, is not the actural size data occupied, however. len = getActualSize(bin); Serial.println(len); Serial.println("end of parsing bin[]"); Serial.println("********************************"); #endif if (align==alLeft) result = bin; else { int len; len = getActualSize(bin); int offset = 10- len; for (int i=0; i<len; i++) result[i+offset] = bin[i]; } return result; } int Converter::getActualSize(String value) { int result = 0; for (int i=0; i<10; i++) { if (value[i]=='\0') { result= i; break; } } return result; }
手头上还有几个8*8的LED点阵,用它来做应该更加直观,哪天改改。

2011年12月22日星期四

在Arduino IDE里直接编写类

Arduino支持C/C++,因此理所当然的支持C++的class,如果是经常要用到的类,可以把它们封装成类库,便于日后调用,关于类库的编写,请参见坛子里其它帖子。有时候,有些class不一定是经常要用到的,直接跟主程序搁一起就可以。

编写的方式有很多种,可以用C++的IDE来写,比如VS Studio、Eclipse、Code::Blocks等等,也可以用像Notepad、Notepad2、Source Insight等文本工具,下面要介绍的方法是直接采用arduino的IDE,目的是帮助大家更加熟悉它。

Arduino的IDE支持多文件管理,因此我们可以利用它来编写类。下面我们以建立一个Color类的例子来描述创建经过。

打开IDE,在菜单栏下面有一排button,最右边那个箭头样式的就是用来创建新的tab页,也就是建立一个新的文件,
arduino IDE

单击它之后,便会出现一个提示你键入文件名的对话框,在这里写入文件名。我们先建立类的头文件Color.h,因此敲入Color.h,注意,一定不要忘了文件的扩展名.h,不然IDE会自动加上arduino的扩展名.pde的。
file name


接着我们在Color.h页中编写Color类的头文件,代码如下
/*
  颜色类,可以分别设置色彩的RGB分量

 */
#ifndef COLOR_H  //预编译指令,防止重复定义类
#define COLOR_H
class Color
{
private:    //私有成员,用来保存色彩的RGB分量
  unsigned int rValue;
  unsigned int gValue;
  unsigned int bValue;
public:
  Color(unsigned int r=255, unsigned int g=0, unsigned int b=0); //类的构造函数,与类名相同
  void setRed(unsigned int value); //类的方法,设置或者读取色彩的RGB分量值
  unsigned int getRed();
  void setGreen(unsigned int value);
  unsigned int getGreen();
  void setBlue(unsigned int value);
  unsigned int getBlue();
};
#endif // COLOR_H

接着就可以建立一个新tab页,编写类的实现代码Color.cpp,步骤跟上面一样,就不啰嗦了。

大家在使用arduino的串口时,经常会用到Serial,其实它是串口类的实例,在某个地方创建之后,我们便可以直接使用。那我们也可以创建一些Color类的实例,比方讲,色彩中的白色、黑色、红色、绿色、蓝色,这几种颜色的RGB分量是固定,因此我们可以仿照Serial的使用方式来创建这几种颜色对象。

依照前面方式,我们新建一个tab页,起名为CommonColors.h,并引用Color.h,写入下面几行代码
#include "Color.h"
//定义一些常见颜色
Color clRed(255,0,0);
Color clGreen(0,255,0);
Color clBlue(0,0,255);
Color clWhite(255,255,255);
Color clBlack(0,0,0);

于是,我们在其它直接或间接引用了CommonColors.h的地方,就可以使用clRed、clGreen了。

写完之后,我们打开文件夹,便可以看到类的所有文件跟主程序文件在一起。这点跟库不一样,库文件是必须存放在libraries下面的与库名相同的文件夹下面。
dir.png


下面我们举个小例子来测试一下
#include "CommonColors.h"
#include "Color.h"
Color cl;
void setup()
{
  Serial.begin(9600);
  Serial.println(clRed.getRed());
  Serial.println(cl.getRed());  //测试构造函数的默认值
}
void loop()
{}




这种方式的好处就是可以在同一个平台下立即编译,当然使用VS等IDE配上适当插件之后比在Arduino下面会更方便。本文只是起到抛砖引玉的作用,希望对大家理解Arduino有点帮助。

贴上类的实现代码Color.cpp
#include "Color.h"
Color::Color(unsigned int r, unsigned int g, unsigned int b)
{
  rValue = r;
  gValue = g;
  bValue = b;
}
void Color::setRed(unsigned int value)
{
  rValue = value;
}
unsigned int Color::getRed()
{
  return rValue;
}
void Color::setGreen(unsigned int value)
{
  gValue = value;
}
unsigned int Color::getGreen()
{
  return gValue;
}
void Color::setBlue(unsigned int value)
{
  bValue = value;
}
unsigned int Color::getBlue()
{
  return bValue;
}

2011年10月26日星期三

红外发光管烧了

今天早上手多,把自制的红外发射器直接接入5V电源,期间还短接了电阻,结果问道一股异味,红外管里一团漆黑,彻底报销。:-(


2011年9月18日星期日

穿墙纪念

墙的高度跟砖头的厚度没关,有关系的是对思想控制的欲望和愚民的政策。当初邓公讲打开窗户的时候飞进来几只苍蝇没什么事的。可是多年过去,为了这几只所谓的苍蝇,我们不仅连纱窗关掉,连门窗都要紧闭,彻底关上了我们通往世界的大门,留下的只有我们对外面的偷窥...

牢骚几句,作为穿墙的纪念。

2011年8月19日星期五

Arduino软件生成遥控方波与AUX遥控器原始方波的对比

这篇博客是前面AUX遥控器红外信号分析的续篇。

由于红外协议有好几种,功力原因,分析起来比较困难,因此权衡之后,还是决定采取用软件方式,模拟AUX空调的遥控信号。

前面博文分析得出结论是,帧与帧之间的间隔(这个词语可能跟标准术语有区别,在此是指相邻的下降沿和上升沿之间的间隔时间)是760us,源代码中设置的间隔为912us,这个数字是综合考虑delay以及源程序中其它指令的耗时等因素,以及考察红外方波波形之后计算所得。

而实际产生的波形与遥控器的原始波形之间,存在比较大的差异,而且这个差异比较怪异,
  1. 整个信号宽度比原始信号要宽出一截(见图1);
  2. 1T、2T波形的宽度有比较大的差距,比原始信号要宽(见图2);
  3. 4T比原始信号宽度要窄(见图3);
  4. 两个9T信号之间几乎没有差别(见图3)。
图1

图2

图3



源代码:


char *strOpen="000000000111101101101010101011011011011011010101010110101010101011011011011010101101010101010101010101011010101010101010101010101010101101010101010101010101010101010101010101010101010110101010101010101101101101101011010101010101010101101101010110";

int lenOfOpen =246;
unsigned int openCode[246];
int pinOut= 28;

void setup()
{
  pinMode(pinOut,OUTPUT);
  Serial.begin(9600);
  StringToArray();

}

void StringToArray()
{
  int i;
  char c;

  for (i=0; i<lenOfOpen; i++)
  {
    c= strOpen[i];

    //Serial.print(c);
    //Serial.print('|');


    if (c=='1')
      openCode[i]=1;
    else
      openCode[i]=0;

    //debug mode
    //Serial.println(openCode[i]);

  }
}

void loop()
{
  int i;
  for (i=0; i< lenOfOpen; i++)
  {
    digitalWrite(pinOut, openCode[i]);
    delayMicroseconds(912);
    //Serial.println(openCode[i]);
  }
}

需要重新计算间隔时间。


2011年8月15日星期一

Arduino中digitalWrite的一点疑惑

做Blink试验时,就曾经有点疑惑,digitalWrite往某个pin写入HIGH或LOW之后,该pin的状态是如何维护的呢?用万用表量输出脚的电压时,HIGH状态时是4.90v,LOW状态时是0.00v,但是示波器查看到的波形却有点不一致:

pin13电压波形图


采集到的电压峰值为正负0.3v,中间处于0v上下
从图上看出,状态设置为HIGH or LOW之后,该输出脚的状态处在一个中间值,而并不是一直处于HIGH or LOW,跟万用表量到的数据似乎有点矛盾。

源代码:

int P13=13;
boolean val=true;
void setup()
{
  pinMode(P13,OUTPUT);
  Serial.begin(96000);
}

void loop()
{
  if (val)
    digitalWrite(P13,HIGH);
  else
    digitalWrite(P13,LOW);
  delay(1500);
  val = !val;
  //Serial.println(val);
}


2011年8月13日星期六

AUX立式空调遥控器红外线波形分析

在网上找了一个红外线逻辑分析电路,做了一个分析器,今天拿它捕捉了AUX遥控器的开机信号,波形图如下,第一个图是全部波形,由于太长,后面的放大图分成了多个。

全部波形













高位之间的间隔为0.76ms


第一帧是宽度为9的低位信号,接着是一个宽度为4的高位,接着便是其它信号。

4,2,2,1,1,1,1,2,2,2,2,2,1,1,1,1,2,1,1,1,1,1,2,2,2,2,1,1,2,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,2,2,2,1,2,1,1,1,1,1,1,1,1,2,2,1,1,2


关机波形

关机信号方波


声音信号


信号分析:

第一帧也是9个宽度的地位,紧接着是:(2*6表示6个2个宽度的高位)
4,2,2,1,1,1,1,2 * 5,1,1,2,1,2,2,2,1,1,1,2,2,2,1,2,1 * 12,2,1 * 14,2,1 * 31, 2 * 4, 1,2,1 * 7, 2,1,2,1,1,2