2012年6月29日星期五

arduino教程(提高篇)——編寫擴展庫

鉴于很多童鞋对扩展库的喜好,并对它不清楚,作者谈谈自己的学习心得,望大家一起学习,共同进步。
首先以自己之前编写的小车的程序为例子:
(个人不喜欢像写arduino程序那样,每种参数都在最前面来个宏定义什么的,呵呵~~~)




  1. void run(char pin1,char pin2,char pwmpin,char state,int val)
  2. {
  3.   pinMode(pin1,1);
  4.   pinMode(pin2,1);
  5.    pinMode(pwmpin,1); 
  6. if(state)
  7. {  
  8.   analogWrite(pwmpin,val);
  9.   digitalWrite(pin1,1);
  10.   digitalWrite(pin2,0);
  11. }
  12. else
  13. {  
  14.   analogWrite(pwmpin,val);
  15.   digitalWrite(pin1,0);
  16.   digitalWrite(pin2,1);
  17. }
  18. }

  19. void setup()
  20. {
  21. }
  22. void loop()
  23. {
  24.   run(12,13,11,1,150);
  25.   run(8,9,7,1,150);
  26.   delay(2000);
  27.   run(12,13,11,0,150);
  28.   run(8,9,7,0,150);
  29.   delay(2000);
  30. }

好吧,我们这就开始扩展库的编写,第一步用记事本新建一个cpp主文件,命名为Motor1.cpp

  1. #include <WProgram.h>   
  2. #include "motor1.h"    //include后面加空格(注意这些小细节)

  3. motor1::motor1()
  4. {
  5. }                            //建一个构造函数,当然也可以带参数
  6. void motor1::run(char pin1,char pin2,char pwmpin,char state,int val)    //建一个带参数的子函数 pin1 pin2输入高低电平引脚
  7. {                                                                                          //pwmpin为PWM输入引脚,state为正反转,va为l输入调速值0~255
  8.   pinMode(pin1,1);
  9.   pinMode(pin2,1);
  10.    pinMode(pwmpin,1); 
  11. if(state)
  12. {  
  13.   analogWrite(pwmpin,val);
  14.   digitalWrite(pin1,1);
  15.   digitalWrite(pin2,0);
  16. }
  17. else
  18. {  
  19.   analogWrite(pwmpin,val);
  20.   digitalWrite(pin1,0);
  21.   digitalWrite(pin2,1);
  22. }
  23. }

第二步,编写.h文件   同样用记事本新建一个.h文件命名为Motor1.h

  1. [mw_shl_code=cpp,true]#ifndef  MOTOR1_H_
  2. #define MOTOR1_H_   //防止重复包含
  3. class motor1
  4. {
  5. public:                      //共有部分
  6.       motor1();
  7.       void run(char pin1,char pin2,char pwmpin,char state,int val);     //假如motor1()或run()里定义了变量,就得写出私有部分                                
  8. };                                                                                                     //private:相应的变量。所谓私有,就是不能给用户调用的。
  9. #endif[/mw_shl_code]

第三步 建个keywords.txt文件  里面写出关键字  就是里面的公有部分函数等··

  1. motor1        KEYWORD1
  2. run        KEYWORD2

记得KEYWORD与motor之间用tab,不是空格。

完成这三步后,就将Motor1.cpp  Motor .h  keywords.txt三个文件放在一个新建文件夹Motor1里面,再将Motor1文件夹放在Arduino根目录libraries 目录下面。
这样你就可以重启arduino的IDE,使用你的扩展库了。

  1. #include <Motor1.h>
  2. void setup()
  3. {}
  4. void loop()
  5. {
  6. motor1 c;
  7. c.run(12,13,11,1,150);
  8.   c.run(8,9,7,1,150);
  9. delay(2000);
  10. c.run(12,13,11,0,150);
  11.   c.run(8,9,7,0,150);
  12.   delay(2000);

  13. }

之前遇到过的困难就是:如果扩展库里包含得有其他扩展库,例如我扩展库里面用到了软串口库SoftwareSerial就一定要在你使用之前,在arduino的IDE里面不仅要包含你自己编的扩展库,还得包含软串口库(就假如会在#include <Motor1.h>下面加一句#include <SoftwareSerial.h>)

如何用W7100A實現HTTP伺服器(一)

大家好,这篇文档将介绍如何利用iMCU7100EVB实现HTTP服务器,同时也会介绍代码并且显示一个简单的demo测试。今天为大家分享第一部分,周末过后将继续为大家分享后面的部分,希望对大家有帮助~
注意: 这篇文档是基于W7100A 100引脚封装,因为W7100A 64引脚封装的芯片在GPIO引脚数目上比100引脚封装的要少,所以用GPIO口控制LED和LCD的示例可能不能正常工作。



1. HTTP服务器
HTTP是超级文本传输协议(HyperText Transfer Protocol)的简称。更多关于HTTP协议的细节,请参考下面的链接。(http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol)如果使用W7100A的全硬件TCP/IP协议栈可以很容易地实现HTTP服务器。下面的图1显示了HTTP服务器和客户端之间的通信。
1 HTTP通信


HTTP服务器的通信过程大概可以分为三个步骤:
1. 连接 : W7100A指定HTTP服务器的socket,打开socket并且监听的过程。
2. 通信 :已经建立的连接。W7100A在接收到来自客户端的HTTP请求之后发送HTTP响应。
3. 关闭 : 在所有的HTTP请求/响应之后完成连接的过程。


2. HTTP服务器实现
2.1 网络设置

W7100A HTTP服务器代码中的网络设置和MAC地址都保存在W7100A255字节用户数据存储器中。如果用户想要改变网络设置或者MAC地址,运行WizISP程序,设置“Flash Operation Mode”为“Data”,然后点击Erase按钮来擦除用户数据存储器。完成之后,MAC地址和网络设置都会变成W7100A task_config.c文件下default_network()函数中的默认设置。初始化后的MAC地址和网络设置将会再次被写入到用户数据存储器。
另外一个改变网络设置的方法是连接到W7100A的HTTP服务器网页,并且使用网页配置页面。



2.2 默认主页设置
当HTTP客户端作为W7100A的IP地址被连接时,W7100A HTTP服务器代码主要是连接到‘index.html’(例如: http://192.168.1.2)。 如果用户想要改变基本的页面,需要从main.c文件下按照下面的代码来修改。

2.3 生成Romfile
示范网页必须作为一个Romfile组合在一起,并且和固件程序一起写入到W7100A的闪存中。提供的ROMFILEMaker.exe程序用来将示范网页组合到一个Romfile。可以通过WIZnet’shomepage -> download center (http://www.wiznet.co.kr/en/)来下载该程序。下图为ROMFILEMaker.exe运行的屏幕截图。
2 ROMFILEMaker程序

在W7100A代码中的区域1为types.h文件分配相关的路径。点击2区中的Add Files按钮,添加应该组合的网页。如果同时添加太多的网页,将会发生错误;因此,用户要组合很多网页,就需要重复添加步骤。此时,网页存在于相同的文件夹下。用户点击3区中的Make Image按钮,组合的文件就会被生成以4区中的名称命名的文件。由于改变4中的Rom Image文件的名称将会致使用户修改用来组合程序二进制和网页的批处理文件命令,所以不要修改该名称。
 
 

2012年6月28日星期四

如何用W7100A實現DDNS用戶端(二)

大家好,前面我们为大家分享了如何用W7100A实现DDNS客户端()DDNS规格和DDNS演示今天继续为大家分享后面一部分即代码演示希望对大家有帮助~

第一部分请参考:http://wiznet2012.blogspot.kr/2012/06/w7100addns.html



4. 代码演示
这一章节将会介绍在DDNS协议中用到的所有代码。这些代码保存在DDNS.cDDNS.h中。为了能够执行DDNS的更新,主程序必须调用DDNS_update()函数。
DDNS_update() 函数按照下面的流程图:
尽管DDNS是一个很简单的请求和响应协议,仍然需要很多步骤准备DDNS请求信息。下面的章节介绍DDNS协议的所有的不同的阶段。

4.1 DDNS_Update()函数
4.1.1 检索用户输入
在这一部分的代码中, 将会检索ISP提供的用户DNS地址以及网络服务器的新的IP地址。
start:
//输入ISP’s DNS IP地址
for(x = 0; x<4; x++)
{
//将字符串分割成一个整数数组
if(x == 0)
split = strtok(tmp_str,".");
else
{
split = strtok(NULL,".");
}
dns_ip[x] = atoi(split);
//检测IP是否有效
if(dns_ip[x] >255 || dns_ip[x] < 0)
{
//检测到无效的IP,重新输入IP地址
goto start;
}
}
// 打印 DNS IP地址
// 输入网络服务器新的IP地址并且检测IP地址是否有效(与上面相同的代码)
//打印网络服务器的IP地址
4.1.2 对比新旧IP地址
DDNS程序将读取上次更新时保存在闪存中的IP地址。然后,进行比较来决定是否需要更新。

for(x = 0; x<4; x++)
{
//从闪存中读取之前更新的IP地址
flash_ip[x] = EEP_Read(x);
}
// 比较之前的IP地址和当前的IP地址
if(memcmp(flash_ip,current_ip,4)==0)
{
//检测到相同的IP地址,通知用户并且离开
goto exit; // 退出程序
}
// 继续DDNS协议

4.1.3 执行DNS查询并且连接到服务器
DNS查询请求从DDNS服务器主机名上查找实际的IP地址。在找到IP地址后,程序会连接到服务器。更多关于dns_query()函数的详细信息,请参阅文档“如何用W7100A实现DNS客户端”。

dns_query(s,"members.dyndns.org"); //DNS查询找到members.dyndns.orgIP地址
//去确认我们是否从DNS查询获得IP地址
if( dyndns_ip[0] == 0 && dyndns_ip[1] == 0 && dyndns_ip[2] == 0 && dyndns_ip[3] == 0)
{
//从主机名获取IP地址失败
goto start; // 请求用户重新输入DNS IP地址信息
}
//使用loop循环来发送请求并且接收来自服务器的响应
switch(getSn_SR(s)) //确认socket状态
{
case SOCK_CLOSED:
port++;
if(socket(s,Sn_MR_TCP, port, 0x00) == 1) // 打开TCP模式下的Socket
{
//创建socket成功
sent_flag = 0;
}
else
{
// 创建socket失败
close(s);
}
break;
case SOCK_INIT:
if(connect(s,dyndns_ip,80) == 1) // 连接到DynDNS服务器
{
//成功连接到服务器并且打印连接的IP地址
}
else
{
//连接服务器失败
}
break;

4.1.4 发送请求信息
Base64_encode()将用户名和密码字符串转换成一个base64编码格式。在用户名和密码字符串编译完成后,HTTP GET请求信息也已经准备就绪。请确保在每一个\r\n之后没有空格,否则请求信息将不会被认为是http数据包。请求信息成功发送后,程序将等待服务器的回应。

case SOCK_ESTABLISHED:
// 将用户名和密码编译成base64
base64_encode(user_pass,base64_user_pass,strlen(user_pass)); // 创建HTTP HET请求信息
sprintf(request_msg,"GET/nic/update?hostname=%s&myip=%s&wildcard=NOCHG&mx=NOCHG&backm x=NOCHG HTTP/1.1 \r\nHost: members.dyndns.org \r\nAuthorization: Basic %s \r\nUser-Agent:%s \r\n\r\n",host_name ,ip_addr_str ,base64_user_pass,user_agent);
//发送请求信息
send(s,request_msg,strlen(request_msg));
//确认接收缓存器中是否有数据 len = getSn_RX_RSR(s);
if (len > 0)
{
//确认接收到的数据长度是否比缓存器空间大
if(len > MAX_LENGTH) len = MAX_LENGTH;
len = recv(s, reply_msg, len); //将数据存放到reply_msg
}
break;

4.1.5 解析应答信息
程序检查回复信息并且打印出结果。

//在回复串中搜索更新成功
split = strstr(reply_msg,"good");
if(split != NULL)
{
//更新来自回复信息的解析数据成功
split_end = strstr(split,"\r\n0\r\n\r\n");
len = split_end - split;
//将最新的IP地址写入闪存
EEP_Block_Write(0,current_ip, 4);
//打印回应信息
}
else
{
//更新来自回复信息中的失败解析数据
split = strstr(reply_msg,"\r\n\r\n");
split = strstr(split+4,"\r\n");
split += 2;
split_end = strstr(split,"\r\n0\r\n\r\n");
len = split_end - split;
//打印回应信息
}

4.1.6 Base64编码
base64_encode()函数使用一个编码表将字符串转换成base64格式。例如,如果编码字符‘a’,结果将会是“YQ==”。字符‘a’有8位(“01100001”)。提出前6位(“011000”)并且插入到编码表输出‘Y’。同样的过程应用到最后两位(“010000”)。注意最后这两位要移动到第一和第二的位置。在插入完成之后,结果为‘Q’。请注意编码函数通常以4的倍数输出字符。结果,对于空白字符,将会添加“==”到编译好的字符串。

Void base64_encode(uint8* usrpwd,uint8* base64_userpwd,int len)
{
// 编码表
static char xdata encodingTable [64] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'};
uint8 xdata in_idx = 0; //输入缓存索引
uint8 xdata out_idx = 0; //输出缓存索引
uint8 xdata remaining = 0; //剩余待处理的字符串
uint8 xdata num = 0; //待处理的字符串数目
uint8 xdata tmp[100] = {0,}; // tmp字符串
num = (len%3); //确定有多少个字符串
if(num == 0)
{
remaining = len + (len/3); //计算编码字符串的长度
}
else
{
switch(num)
{
case 1: remaining = ((len *4)/3)+ 3; //计算编码字符串的长度 break;
case 2: remaining = ((len *4)/3)+ 2; // 计算编码字符串的长度 break;
default:
break;
}
}
while(remaining > 0)
{
//提取前6位并且插入到编码表中
tmp[out_idx] = (usrpwd[in_idx]&0xFC) >> 2;
base64_userpwd[out_idx] = encodingTable[tmp[out_idx]];
//提取下一个6位并且插入到编码表中
tmp[out_idx + 1] = (((usrpwd[in_idx]&0x03) <<4) | ((usrpwd[in_idx+1]&0xF0)>>4));
base64_userpwd[out_idx + 1] = encodingTable[tmp[out_idx + 1]];
//提取下一个6位并且插入到编码表中
tmp[out_idx + 2] = (((usrpwd[in_idx+1]&0x0F) <<2) | ((usrpwd[in_idx+2]&0xC0)>>6));
base64_userpwd[out_idx + 2] = encodingTable[tmp[out_idx + 2]];
//提权下一个6位并且插入到编码表中
tmp[out_idx + 3] = (usrpwd[in_idx+2]&0x3F);
base64_userpwd[out_idx + 3] = encodingTablse[tmp[out_idx + 3]];
//最后4位的特殊情况应用
if(remaining == 4)
{
switch(num)
{
case 1: // 添加字符 ‘==’到空白编译好的字符串 memcpy(base64_userpwd+out_idx + 2,"==",2);
break;
case 2: // 添加一个 ‘=’字符到空白编译好的字符串
tmp[out_idx + 2] = (((usrpwd[in_idx+1]&0x0F) <<2) | ((usrpwd[in_idx+2]&0xC0)>>6));
base64_userpwd[out_idx + 2] = encodingTable[tmp[out_idx + 2]]; memcpy(base64_userpwd+out_idx + 3,"=",1);
break;
default:
break;
}
}
out_idx += 4; // 索引递增
in_idx += 3; //索引递增
remaining -= 4; //剩余字符串计数器递减
}
}

2012年6月27日星期三

Arduino教程(提高篇)——使用EEPROM斷電也能保存資料


EEPROM (Electrically Erasable Programmable Read-Only Memory),电可擦可编程只读存储器--一种掉电后数据不丢失的存储芯片。
简而言之就是你想断电后arduino还要保存一些参数,就使用EEPROM吧。
在各型号的arduino控制器上的AVR芯片均带有EEPROM,也有外接的EEPROM芯片,常见arduino控制器的EEPROM大小:
Arduino UNO、Arduino duemilanove-m328、Zduino m328均使用ATmega328芯片,EEPROM都为1K
Arduino duemilanove-m168的EEPROM为512bytes
Arduino 2560的EEPROM为4K

下面我们介绍arduino自带的EEPROM使用方法,arduino的库已经为我们准备好了EEPROM类库,我们要使用得先调用EEPROM.h,然后使用write和read方法,即可操作EEPROM。
另:下面的官方例子由于写成较早,所以讲EEPROM的大小都定为了512字节,实际使用中,大家可参照上面所说的EEPROM大小,自行更改。

1.写入
选择 File>Examples>EEPROM>eeprom_write

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/*
 * EEPROM Write
 *
 * Stores values read from analog input 0 into the EEPROM.
 * These values will stay in the EEPROM when the board is
 * turned off and may be retrieved later by another sketch.
 */
 
#include <EEPROM.h>
 
// EEPROM 的当前地址,即你将要写入的地址,这里就是从0开始写
int addr = 0;
 
void setup()
{
}
 
void loop()
{
  //模拟值读出后是一个0-1024的值,但每字节的大小为0-255,所以这里将值除以4再存储到val
  int val = analogRead(0) / 4;
   
  // write the value to the appropriate byte of the EEPROM.
  // these values will remain there when the board is
  // turned off.
  EEPROM.write(addr, val);
   
  // advance to the next address.  there are 512 bytes in
  // the EEPROM, so go back to 0 when we hit 512.
  addr = addr + 1;
  if (addr == 512)
    addr = 0;
   
  delay(100);
}



2.读取
选择 File>Examples>EEPROM>eeprom_read

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/*
 * EEPROM Read
 *
 * Reads the value of each byte of the EEPROM and prints it
 * to the computer.
 * This example code is in the public domain.
 */
 
#include <EEPROM.h>
 
// start reading from the first byte (address 0) of the EEPROM
int address = 0;
byte value;
 
void setup()
{
  // initialize serial and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }
}
 
void loop()
{
  // read a byte from the current address of the EEPROM
  value = EEPROM.read(address);
   
  Serial.print(address);
  Serial.print("\t");
  Serial.print(value, DEC);
  Serial.println();
   
  // advance to the next address of the EEPROM
  address = address + 1;
   
  // there are only 512 bytes of EEPROM, from 0 to 511, so if we're
  // on address 512, wrap around to address 0
  if (address == 512)
    address = 0;
     
  delay(500);
}



3.清除
选择 File>Examples>EEPROM>eeprom_clear
清除EEPROM的内容,其实就是把EEPROM中每一个字节写入0,因为只用清一次零,所以整个程序都在setup部分完成。

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
/* * EEPROM Clear
 *
 * Sets all of the bytes of the EEPROM to 0.
 * This example code is in the public domain.
 
 */
#include <EEPROM.h>
 
void setup()
{
  // 让EEPROM的512字节内容全部清零
  for (int i = 0; i < 512; i++)
    EEPROM.write(i, 0);
     
  // 清零工作完成后,将L灯点亮,提示EEPROM清零完成
  digitalWrite(13, HIGH);
}
 
void loop()
{
}