鳕鱼天空

This is Mr Wang's Tech Blog.

Arduino驱动DS3231高精度时钟模块

很想要个时钟模块,自己焊又太麻烦,干脆在TB上买下来了,省时。

  模块参数:
  1.尺寸:38mm(长)*22mm(宽)*14mm(高)
  2.重量:8g
  3.工作电压:3.3--5.5V
  4.时钟芯片:高精度时钟芯片DS3231
  5.时钟精度:0-40℃范围内,精度2ppm,年误差约1分钟
  6.带2个日历闹钟
  7.可编程方波输出
  8.实时时钟产生秒、分、时、星期、日期、月和年计时,并提供有效期到2100年的闰年补偿
  9.芯片内部自带温度传感器,精度为±3℃
  10.存储芯片:AT24C32(存储容量32K)
  11.IIC总线接口,最高传输速度400KHz(工作电压为5V时)
  12.可级联其它IIC设备,24C32地址可通过短路A0/A1/A2修改,默认地址为0x57
  13.带可充电电池LIR2032,保证系统断电后,时钟任然正常走动

接线说明,以Arduino uno r3为例:
  SCL→A5
  SDA→A4
  VCC→5V
  GND→GND

 
 

代码部分:

#include <DS3231.h>
#include <Wire.h>

DS3231 Clock;
bool Century=false;
bool h12;
bool PM;
byte ADay, AHour, AMinute, ASecond, ABits;
bool ADy, A12h, Apm;

byte year, month, date, DoW, hour, minute, second;

void setup() {
 // 启动I2C(IIC)接口
 Wire.begin();
        //以下部分是初始化时间,每次板子通电之后都会初始化成这个时间,只是测试用,以后可以删除。
        Clock.setSecond(50);//Set the second
        Clock.setMinute(59);//Set the minute 设置分钟
        Clock.setHour(11);  //Set the hour 设置小时
        Clock.setDoW(5);    //Set the day of the week 设置星期几
        Clock.setDate(31);  //Set the date of the month 设置月份
        Clock.setMonth(5);  //Set the month of the year 设置一年中的月份
        Clock.setYear(13);  //Set the year (Last two digits of the year) 设置年份(在今年的最后两位数——比如2013年最后的13)
 // Start the serial interface
 Serial.begin(115200);
}
void ReadDS3231()
{
  int second,minute,hour,date,month,year,temperature;
  second=Clock.getSecond();
  minute=Clock.getMinute();
  hour=Clock.getHour(h12, PM);
  date=Clock.getDate();
  month=Clock.getMonth(Century);
  year=Clock.getYear();
 
  temperature=Clock.getTemperature();
 
  Serial.print("20");
  Serial.print(year,DEC);
  Serial.print('-');
  Serial.print(month,DEC);
  Serial.print('-');
  Serial.print(date,DEC);
  Serial.print(' ');
  Serial.print(hour,DEC);
  Serial.print(':');
  Serial.print(minute,DEC);
  Serial.print(':');
  Serial.print(second,DEC);
  Serial.print('\n');
  Serial.print("Temperature=");  //这里是显示温度
  Serial.print(temperature);
  Serial.print('\n');
}
void loop()
{
  ReadDS3231();
  delay(1000);  //间隔1000ms(1000ms=1秒)循环一次。
}

用Arduino制作简易电压测试仪

一直想用锂电给arduino的板子供电,买了2种带充放的模块,其中一种有电量指示灯,另外一种没有,想在锂电为板子供电同时测量锂电电压,类似手机剩余电量显示这种。

电路比较简单,找2个10K的电阻串联在电池两端,电池负极接GND,电阻连接处接A3,代码如下:

#define voltsInPin A3

void setup()
{
  pinMode(voltsInPin, INPUT);

  Serial.begin(9600);
  Serial.println("volts:");
}

void loop()
{
  int rawReading = analogRead(voltsInPin);
  float volts = rawReading / 220.62 * 2;
  Serial.println(volts);
  delay(500);
}

每隔半秒测试一次,220.62是我根据万用表测出的数值经过换算得来的,理论上应该是(1023/5=204.8),乘以2是因为两个10K的电阻分压了,所以测量到的电压是实际的一半大小,3.7V锂电充满一般4.2V左右。然后我找了个4位计时器(TM1637),加了几句代码,可以用数码管显示电压值了,两个脚分别接D2和D3。

#define voltsInPin A3

// 下面是4位计时器定义
#include "TM1637.h"
#define CLK 3//pins definitions for TM1637 and can be changed to other ports       
#define DIO 2
TM1637 tm1637(CLK, DIO);

void setup()
{
  pinMode(voltsInPin, INPUT);
  //pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
  Serial.println("volts:");

  tm1637.init();
  tm1637.set(BRIGHT_TYPICAL);

}

void loop()
{
  int rawReading = analogRead(voltsInPin);
  float volts = rawReading / 220.62 * 2;
  Serial.println(volts);
  int num = volts*100;
  // 4位数显示
  tm1637.display(0, num / 1000);
  tm1637.display(1, (num % 1000) / 100);
  tm1637.display(2, (num % 100) / 10);
  tm1637.display(3, num % 10);
  delay(500);
}

最后,请大家保持冷静,上面的代码在用USB供电时一点问题都没有,但是直接用升压模块接锂电供电后,测量的电压低了很多,而且跳跃的很厉害,我暂时还没有搞清楚到底是不能这样测试还是数码管的问题(数码管噪音很大),改天再测试了,或者还是直接用带电压显示的升压模块算了。。。。。。

人肉分析空调红外遥控器 with Arduino & C#(01)——温度篇

最近迷上了Arduino,简直痴迷,感叹小时候咋就没有那么好玩的东西。

那么,今天的主题是用Arduino控制红外发射器控制带红外的电器(没有红外的以后可以考虑另加,比如通过Nano板子:),今天的小白鼠是家里一个老的AUX壁挂空调,据说海尔的加密云云,改天再研究。

拿出UNO的板子,红外发送和接收的模块,装好库(https://github.com/z3t0/Arduino-IRremote ),翻翻示例,先是找到了IRrecvDumpV2,数据针脚插11,按下遥控器按钮后提示(IR code too long. Edit IRremoteInt.h and increase RAWBUF),问了万能的度娘,说是很多空调编码过长,100个缓存不够用,然调到255并无效果,此法弃疗。

接着问度娘,找到了一片神作,《“高帅富”空调的红外解码和遥控方法”》 http://www.arduino.cn/thread-5775-1-1.html,光看这个名字就厉害的不得了,我就转个代码,具体文章请去原地址拜读

/*
Author: AnalysIR
Revision: 1.0
 
This code is provided to overcome an issue with Arduino IR libraries
It allows you to capture raw timings for signals longer than 255 marks & spaces.
Typical use case is for long Air conditioner signals.
 
You can use the output to plug back into IRremote, to resend the signal.
 
This Software was written by AnalysIR.
 
Usage: Free to use, subject to conditions posted on blog below.
Please credit AnalysIR and provide a link to our website/blog, where possible.
 
Copyright AnalysIR 2014
 
Please refer to the blog posting for conditions associated with use.
[url]http://www.analysir.com/blog/2014/03/19/air-conditioners-problems-recording-long-infrared-remote-control-signals-arduino/[/url]
 
Connections:
IR Receiver      Arduino
V+          ->  +5v
GND          ->  GND
Signal Out   ->  Digital Pin 2
(If using a 3V Arduino, you may connect V+ to +3V)
*/
 
#define LEDPIN 13
//you may increase this value on Arduinos with greater than 2k SRAM
#define maxLen 600
 
volatile  unsigned int irBuffer[maxLen]; //stores timings - volatile because changed by ISR
volatile unsigned int x = 0; //Pointer thru irBuffer - volatile because changed by ISR
 
void setup() {
  Serial.begin(9600); //change BAUD rate as required
  attachInterrupt(0, rxIR_Interrupt_Handler, CHANGE);//set up ISR for receiving IR signal
}
 
void loop() {
  // put your main code here, to run repeatedly:
 
  //Serial.println(F("Press the button on the remote now - once only"));
  delay(5000); // pause 5 secs
  if (x) { //if a signal is captured
    digitalWrite(LEDPIN, HIGH);//visual indicator that signal received
    Serial.println();
    Serial.print(F("Raw: (")); //dump raw header format - for library
    Serial.print((x - 1));
    Serial.print(F(") "));
    detachInterrupt(0);//stop interrupts & capture until finshed here
    for (int i = 1; i < x; i++) { //now dump the times
      //if (!(i & 0x1)) Serial.print(F("-"));
      Serial.print(irBuffer[i] - irBuffer[i - 1]);
      Serial.print(F(","));
    }
    x = 0;
    Serial.println();
    Serial.println();
    digitalWrite(LEDPIN, LOW);//end of visual indicator, for this time
    attachInterrupt(0, rxIR_Interrupt_Handler, CHANGE);//re-enable ISR for receiving IR signal
  }
 
}
 
void rxIR_Interrupt_Handler() {
  if (x > maxLen) return; //ignore if irBuffer is already full
  irBuffer[x++] = micros(); //just continually record the time-stamp of signal transitions
 
}

仔细通篇看完,用此君的代码,读到了211和423,这里遇到了一个坑,其实此君代码会把连按的2次操作合到一起,最后经过多次测试,这只AUX遥控的按键RAW统统是211长度。经过对IRremote库的源代码的涩读(ir_NEC.cpp),发现了这个基本符合NEC代码的开头,先来段读出来的RAW:

9016,4520,540,1684,544,1684,544,560,544,556,544,556,544,560,544,1684,544,1684,544,1684,548,1680,548,1684,544,556,544,556,544,560,544,1684,544,1684,544,560,544,556,544,556,544,560,544,556,544,556,544,560,544,556,544,556,544,560,544,556,544,556,544,560,544,556,544,556,544,556,548,556,544,556,544,560,544,556,544,556,544,1688,544,556,544,1684,544,556,544,560,544,556,544,556,544,560,544,556,544,556,548,556,544,556,544,556,548,556,564,536,544,556,548,556,544,1684,568,532,568,532,568,536,564,540,540,560,560,540,564,540,564,536,564,536,544,556,568,532,548,556,568,532,568,532,568,536,568,532,564,540,564,536,568,532,568,532,568,536,568,532,568,532,568,536,568,532,564,540,568,532,568,532,568,532,572,532,568,532,568,536,568,532,568,1660,568,532,568,1664,564,536,568,532,568,536,564,536,568,532,568,1664,568,1660,568,1660,568,1660,568,536,568,1660,568,1660,568,532,596

看花了吧,然后用IRsendRawDemo竟然可以成功实现功能,但arduino那么小的内存如何装得下所有的代码,就算装下了,这不分析下具体原理和代码生成方式晚上如何睡得着。

于是,摘下眼镜揉一揉接着戴上,要上了!

第一个9000左右和第二个4500左右的值代表了NEC的特征,后面209个的最后一个600左右的是结尾,那么中间剩下208个就好分析了(什么,分析无力,那么多数字看花了,问我什么意思我也不知道,于是乎又去找度娘姐姐研究了下红外的原理:【扫盲贴】浅谈38K红外发射接受编码(非常好) http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=21227800&id=5747884 一遍看不懂多看几遍,还是看不懂就往下看,那个数字代表高电平低电平的时长,但是用38KHz波来实现的,有这个波低电平,没这个波高电平,一低一高的组合可以变成0或1),于是乎又从库的源代码中找到了转换的方法(ir_NEC.cpp)就2个方法,其中一个就是解码:

  // Build the data
  for (int i = 0;  i < NEC_BITS;  i++) {
    // Check data "mark"
    if (!MATCH_MARK(results->rawbuf[offset], NEC_BIT_MARK))  return false ;
    offset++;
    // Suppend this bit
    if  (MATCH_SPACE(results->rawbuf[offset], NEC_ONE_SPACE ))  data = (data << 1) | 1 ;
    else if (MATCH_SPACE(results->rawbuf[offset], NEC_ZERO_SPACE))  data = (data << 1) | 0 ;
    else                return false ;
    offset++;
  }

先来560左右的NEC_BIT_MARK,然后判断是NEC_ONE_SPACE就是1,NEC_ZERO_SPACE就是0,转换如下:

11000011111000110000000000000000000001010000000000000010000000000000000000000000000000001010000011110110

共104bit,换算成16进制就是C3E3000005000200000000A0F6(此时按的32度,除湿,微风,关机)

此时你心中也许会有千万匹神兽飞奔而过,那么多数字眼睛也看花了,老兄,你看看标题好不好,C#写的转换代码啦,你还真以为全人肉解码啊!

同理下面是开机代码:

11000011111000110000000000000000000001010000000000000010000000000000000000000100000000001010000011110001

共104bit C3E3000005000200000400A0F1(32度,除湿,微风,开机)

28度 除湿 风速无,开

11000011111001010000000000000000000001010000000000000010000000000000000000000100000000001010000011110110 
104bit C3E5000005000200000400A0F6

28度 除湿 风速无,关

11000011111001010000000000000000000001010000000000000010000000000000000000000000000000001010000011110010 
104bit C3E5000005000200000000A0F2

27度 除湿 风速无,开

11000011111110010000000000000000000001010000000000000010000000000000000000000100000000001010000011100110 
104bit C3F9000005000200000400A0E6

27度 除湿 风速无,关

11000011111110010000000000000000000001010000000000000010000000000000000000000000000000001010000011100010 
104bit C3F9000005000200000000A0E2

好,下面就找到了变化的部分,C3之后和结尾,结尾估计是校验码,以后再说,C3之后1个字节估计就和温度有关了,经过人肉演算,发现了如下规律:

E3 11100011 32

……

E5 11100101 28

F9 11111001 27

E9 11101001 26

F1 11110001 25

E1 11100001 24

FE 11111110 23

……

E2 11100010 16

什么,你没有找到规律!从第8位倒着往前看5位,还是不明白?我又抄了一遍。。。。。。

11000 32(度)

……

10100 28

10011 27

10010 26

10001 25

10000 24

01111 23

……

01000 16度(8)

基本上就是这样了,温度应该就是这几位了,顺便补充一下,大多数空调遥控器会把所有的状态一起发过去,所以每次一样长,而且带有所有信息,今天就分析到这,下次继续。

下面放一些也许值得参考的文章(次序不分先后):

万用遥控之红外解码分析仪(上位机源码、下位机源码、详细的制作讲解)

【扫盲贴】浅谈38K红外发射接受编码(非常好)

C#+Arduino使用红外遥控器

“高帅富”空调的红外解码和遥控方法【精华】 

Arduino教程(提高篇)——红外遥控(接收篇)

C#实现读取红外线遥控器

Arduino-IRremote库

海尔彩电遥控器型号对照表

 

Arduino语音模块-DFPlayer Mini模块

Arduino语音模块-DFPlayer Mini模块

外观

外观

简介

  • DFPlayer Mini是一款小巧且价格低廉的MP3模块,可以直接接驳扬声器。
  • 模块配合供电电池、扬声器、按键可以单独使用,也可以通过串口控制,作为Arduino UNO或者是任何有串口的单片机的一个模块。
  • 模块本身完美的集成了MP3、WAV、WMA的硬解码。
  • 同时软件支持TF卡驱动,支持FAT16、FAT32文件系统。
  • 通过简单的串口指令即可完成播放指定的音乐,以及如何播放音乐等功能,无需繁琐的底层操作,使用方便,稳定可靠。

产品参数

  • 支持采样率(KHz):8/11.025/12/16/22.05/24/32/44.1/48
  • 24位DAC输出,动态范围支持:90dB,信噪比支持:85dB
  • 完全支持FAT16、FAT32文件系统,最大支持32G的TF卡,支持32G的U盘、64M字节的NORFLASH
  • 多种控制模式可选。IO控制模式、串口模式、AD按键控制模式
  • 广播语插播功能,可以暂停正在播放的背景音乐。广告播放完毕回到背景音继续播放
  • 音频数据按文件夹排序,最多支持100个文件夹,每隔文件夹可以分配255首曲目
  • 30级音量可调,6级EQ可调

引脚说明

DFPlayer_Mini_Pin

引脚号 引脚名称 功能描述 备注
1 VCC 模块电源输入 3.3V—5V,建议5V,不要超过5.2V
2 RX UART串行数据输入  
3 TX UART串行数据输出  
4 DAC_R 音频输出右声道 驱动耳机、功放
5 DAC_L 音频输出左声道 驱动耳机、功放
6 SPK2 接小喇叭 驱动小于3W喇叭
7 GND 电源地
8 SPK1 接小喇叭 驱动小于3W喇叭
9 IO1 触发口 默认上一曲(长按音量减)
10 GND 电源地
11 IO2 触发口 默认下一曲(长按音量加)
12 ADKEY1 AD口1 当触发时是第一首(长按循环第一首)
13 ADKEY2 AD口2 当触发时是第五首(长按循环第五首)
14 USB+ USB+ DP 接U盘或插电脑的USB口
15 USB- USB- DM 接U盘或插电脑的USB口
16 Busy 播放状态 有音频,输出低;无音频,输出高

资料说明
串口模式

模块支持异步串口通讯模式,通过串口接受控制命令

串口指令格式

指令名称 对应功能 功能描述
$S 起始位0x7E 每条命令反馈均以$开头,即0x7E
VER 版本 版本信息[目前默认为0xFF]
Len len后字节个数 校验和不计算在内
CMD 命令字 表示具体的操作,比如播放/暂停等等
Feedback 命令反馈 是否需要反馈信息,1反馈,0不反馈
para1 参数1 查询的数据高字节(比如歌曲序号)
para2 参数2 查询的数据低字节
checksum 校验和[占两个字节] 累加和校验[不计起始位$]
$0 结束位 结束位0xEF

举个例子,如果我们制定播放NOR FLASH,就需要发送:7E FF 06 09 00 00 04 FF DD EF,数据长度为6,这6个字节分别是[FF 06 09 00 00 04]。 不计算起始、结束、和校验。再然后对得到的结果进行取反。

串口控制指令

CMD命令(指令) 对应的功能 参数(16位)
0x01 下一曲  
0x02 上一曲  
0x03 指定曲目(NUM) 1-2999
0x04 音量+  
0x05 音量-  
0x06 指定音量 0-30
0x07 指定EQ 0/1/2/3/4/5 Normal/Pop/Rock/Jazz/Classic/Bass
0x08 单曲循环指定曲目播放 1-2999
0x09 指定播放设备 1/2/3/4/5 U盘/SD/AUX/SLEEP/FLASH
0x0A 进入休眠——低功耗  
0x0B 保留  
0x0C 模块复位  
0x0D 播放  
0x0E 暂停  
0x0F 指定文件夹播放 1-10(需要自己设定)
0x10 扩音设置(无) [DH=1:开扩音][DL:设置增益0-31]
0x11 全部循环播放 [1:循环播放][0:停止循环播放]
0x12 指定MP3文件夹曲目 1-9999
0x13 插播广告 1-9999
0x14 支持15个文件夹 见下面的详细说明
0x15 停止播放,播放背景  
0x16 停止播放  

串口查询指令

CMD命令详解(指令) 对应的功能 参数(16位)
0x3C STAY  
0x3D STAY  
0x3E STAY  
0x3F 发送初始化参数 0-0x0F(低四位每位代表一种设备)
0x40 返回错误,请求重发  
0x41 应答  
0x42 查询当前状态  
0x43 查询当前音量  
0x44 查询当前EQ  
0x45 查询当前播放模式 该版本保留此功能
0x46 查询当前软件版本 该版本保留此功能
0x47 查询UDISK的总文件数  
0x48 查询TF卡的总文件数  
0x49 查询FLASH卡的总文件数  
0x4A 保留  
0x4B 查询UDISK的当前曲目  
0x4C 查询TF的当前曲目  
0x4D 查询FLASH的当前曲目  

AD按键模式

模块还支持AD按键控制方式,取代了传统了矩阵键盘的接法,这样做的好处是充分利用了MCU越来越强大的AD功能。设计简约而不简单,我们模块默认配置2个AD口,20个按键的阻值分配。

  • 按键使用示意原理图

DFPlayer_Mini

IO模式

本模块也支持简单的IO模式

DFPlayer_Mini

使用教程
连线图
DFPlayer_Mini

样例代码
注意:文件夹命名需要是mp3,放置在SD卡根目录下,而mp3文件命名需要是4位数字,例如"0001.mp3",放置在mp3文件夹下。 如需中英文命名,可以添加在数字后,例如"0001hello.mp3"或者"0001后来.mp3"。

库文件下载链接:DFPlayer_Mini_mp3

/*
 *  Copyright:  DFRobot
 *  name:       DFPlayer_Mini_Mp3 sample code
 *  Author:     lisper <lisper.li@dfrobot.com>
 *  Date:       2014-05-30
 *  Description:    sample code for DFPlayer Mini, this code is test on Uno
 *          note: mp3 file must put into mp3 folder in your tf card
 */
#include <SoftwareSerial.h>
#include <DFPlayer_Mini_Mp3.h>

void setup () {
    Serial.begin (9600);
    mp3_set_serial (Serial);    //set Serial for DFPlayer-mini mp3 module 
    mp3_set_volume (15);
}

void loop () {        
    mp3_play (1);
    delay (6000);
    mp3_next ();
    delay (6000);
    mp3_prev ();
    delay (6000);
    mp3_play (4);
    delay (6000);
}

/*
   mp3_play ();     //start play
   mp3_play (5);    //play "mp3/0005.mp3"
   mp3_next ();     //play next 
   mp3_prev ();     //play previous
   mp3_set_volume (uint16_t volume);    //0~30
   mp3_set_EQ ();   //0~5
   mp3_pause ();
   mp3_stop ();
   void mp3_get_state ();   //send get state command
   void mp3_get_volume (); 
   void mp3_get_u_sum (); 
   void mp3_get_tf_sum (); 
   void mp3_get_flash_sum (); 
   void mp3_get_tf_current (); 
   void mp3_get_u_current (); 
   void mp3_get_flash_current (); 
   void mp3_single_loop (boolean state);    //set single loop 
   void mp3_DAC (boolean state); 
   void mp3_random_play (); 
 */

疑难解答

  • 问 :Arduino控制DFPlayer mini时,喇叭有杂音,如何处理?

    答:在TX,RX与MP3模块的RX,TX之间连一个1k电阻。因为DFPlayer Mini模块工作电压应该是3.3V,而主控板传入电压为5V,因此需要1K左右电阻分压。

  • 问 :SD卡中的文件和文件夹名字有什么格式要求吗?

    答:函数mp3_play (1); 播放文件的格式为"0001***.mp3(或支持其他格式)". 您可能需要注意这些:

(1). 该音频文件的名称应该命名一个四位数字开始,如:

0001.mp3
0002Chasing The Sun.mp3
0003.mp3
0004Try.mp3
0010FourFiveSeconds.mp3 

MP3-1.png

(2).该音频文件应该放在“MP3”'它坐落在TF卡的根目录'文件夹;
MP3-2.png

本文转自:http://www.ncnynl.com/archives/201606/190.html 创客制作 ,整理自 DFRobot wiki