完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
1、模块介绍
本实验主要用到两个模块模块的图片如下所示: esp32模块型号为ESP32Devkitc v4 ,是启明云端官方的开发板。与乐鑫官方的完全一致,引脚顺序,原理图一摸一样。可以直接使用乐鑫官方的示例源程序进行调试。 tiny RTC模块包含一个ds1307 一个 24c32 eeprom,一个电池,一个ds8b02温度传感器,温度传感器没有焊接。 板子的原理图如下: 从图中可以看出,DS1307和AT24C32共一组I2C接口。 I2C总线采用的是7位地址,每一帧数据第一个字节都是地址。所以二者共用一组接口是可以通过各自不同的地址来访问不同的器件。而电路中常用的另外一种总线SPI用的是片选CS信号来保证同一时刻与cpu通信的只有一个唯一的器件,所以SPI总线上所有的器件是没有地址的,这是与I2C总线有区别的地方。 在本次模块调试中,耗时较长的是调试DS1307,这个RTC时钟芯片看似简单,却又几个关键的需要注意的地方,否则很容易出现读不到数据,或者数据出错的情况。 2、DS1307调试 DS1307 调试分为3个部分2.1 DS1307数据手册分析 DS1307说明 数据格式为BCD码,BCD码是一种特殊的16进制数,通常的16进制范围是0-F,遇到16进1,BCD码范围是0-9 ,遇到10进1。简单举例说明 BCD 码: 0x01 。。。。 0x09 后面一个数是 0x10,在后面是0x11 。相当于采用额是16进制,而里面存储的却是10进制数。 uint8_t a = 0x59 //定义一个BCD码 uint8_t b = 0; b = (a>>4)*10+(a&0x0f) //转换为10进制数,转换结果实际上也是59 日历功能,包含秒 分钟 小时 星期 日期 月 年 ,还包含24和12小时切换功能。最多能存储100年的时间。理论上说到了100年后,时间将会从新开始从0计数。 pinmap和典型原理图
这几时间寄存器看表格就明白了比较简单就不详细说明了,采用的BCD码。简单举一个例子。如果是59s 则00h寄存器的值是,十进制显示就是89,十六进制是0x59 [tr]bit7bit6bit5bit4bit3bit2bit1bit0[/tr]
07h寄存器死控制寄存器RS1和RS0是控制out = 1 sqwe = 1 则时钟输出接口正常输出。RS1和RS0调控时钟输出的频率。RS1=RS0=0时输出的时钟频率为1HZ。这一部分暂时用不上就不做详细说明。 I2C通信频率模式为100Khz,最高可为400Hz,这一点在写代码的时候要注意!!! 读写时序图 上图两个图中,分别说明了向DS1307写数据和读数据的时序图,读写由7bit地址+地址控制位控制。0:写数据;1:读数据。 DS1307默认的地址是:0x68,貌似是不支持修改地址,datasheet中没有找到相关的说明。不过一般I2C设备的地址都是在引脚接高低电平来设置,没有见到过支持软件修改地址的设备。 注意:如果只依靠上面的两个图去写程序,读到的数据肯定是乱码,这是因为DS1307内部有一个寄存器指针,当读取了一个寄存器的值之后,指针值+1,而上述途中并没有关于寄存器指针的设置。所以一开始我就默认为每次上电寄存器指针指向的是00h寄存器(秒寄存器),结果发现读到的数据没有连续变化的趋势。当时想时间寄存器一共就7个,也就是相当于读取了7个字节之后,数据将会重复一次,结果发现并不是如此。其实08h-3Fh寄存器也是会被读取的。所以不好进行分析。 后来百度网上找数据,也没有找到相关的数据说明,因为网上关于DS1307的资料确实比较少。这也是笔者要将DS1307整个调试过程写出来的原因。所以在这里卡了将近1天的时间。 后来看到一个ds1307 51单片机的例程,发现里面在读取数据前,先写入了0x00,上面标注是将将寄存器指针清零。后来安装这个方式加上了写入0x00的代码,奇迹出现,读取到的时间正在按照每秒+1的方式在串口终端上显示。大功告成!!! 实际后来再看了下数据手册,在上述两个图表之后,下一页还有一个图表,当时没有注意看。下面来看下这个图表的内容。 在这个图表中显示了在读取数据前要将pointer清0!!! 看到这里当时心里有种憋屈,datasheet就不写的明确点吗???图5里面说的是readdata,图6又来了一个readdata,这很容易让人写错程序好吗???说多了都是泪!!!!! 最后分析了一下,还是自己快2年没碰程序了,手生疏了!!!。后来看了其他I2C器件的操作方式,一般写入和读取前,都要指定寄存器的地址,再来进行数据读写操作。如果一早就知道这个经验,也不至于在这里卡了将近1天的时间!!!!! 2.2 ESP32代码分析 在2.1中,实际上已经将代码编写过程中会遇到的一些坑都写出来。但实际中一般不会这么顺利,一般都是初步看了数据手册就去写代码,写代码调试出来问题,回过头来再看数据手册。反复接受这个过程的锤炼,模块是一定能够调试成功的。 下面就esp32代码的一些重点进行说明。具体源码请参见文末的链接。 笔者采用的编程环境是win10+vscode+platformIO。 接口及相关宏定义 #define DS1307_I2C_CHANNEL I2C_NUM_0 #define DS1307_MASTER_SCL_IO (19) // esp32 pin19 -> DS1307 SCL #define DS1307_MASTER_SDA_IO (18) // esp32 pin18 -> DS1307 SCL #define DS1307_FREQ_HZ (100000) // default setting 100Hhz refer to ds1307 datasheet #define DS1307_ADDR 0x68 //7 bit addres 采用的是esp32自带的硬件I2C,如果要采用模拟的方式就需要参照数据手册中,每个信号的时长及时序,也比较简单,不过建议采用自带的硬件I2C。mcu内置好的模块一定要用起来!!!! 写数据,具体可以参见图表4 i2c_master_start(cmd); //发送start信号 i2c_master_write_byte(cmd, DS1307_ADDR << 1 | WRITE_BIT, ACK_CHECK_EN); i2c_master_write_byte(cmd, 0x00, ACK_CHECK_EN); //从机发送ACK信号 i2c_master_write(cmd,(uint8_t*)&time,7,ACK_CHECK_EN); // i2c_master_write_byte(cmd, 0x00, ACK_CHECK_EN); i2c_master_stop(cmd) 读数据,具体可参见图表6 i2c_master_start(cmd); i2c_master_write_byte(cmd, DS1307_ADDR << 1 | WRITE_BIT, ACK_CHECK_EN); i2c_master_write_byte(cmd, 0x00, ACK_CHECK_EN); //寄存器指针清零0 i2c_master_stop(cmd); ret = i2c_master_cmd_begin(DS1307_I2C_CHANNEL, cmd, 1000 / portTICK_RATE_MS); //这一行是最终发送的代码,程序执行到这一句,硬件I2C才会将上述命令发送到总线上 i2c_cmd_link_delete(cmd); if (ret != ESP_OK) { ESP_LOGE(TAG,"DS1307 write failed!"); return ret; } vTaskDelay(30 / portTICK_RATE_MS); cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, DS1307_ADDR << 1 | READ_BIT, ACK_CHECK_EN); // vTaskDelay(1/portTICK_PERIOD_MS); if (size >1) { i2c_master_read(cmd,(uint8_t*)&time,size-1,ACK_VAL); //读取时间的7个寄存器 } i2c_master_read_byte(cmd,(uint8_t*)&time+size-1, NACK_VAL); i2c_master_stop(cmd); ret = i2c_master_cmd_begin(DS1307_I2C_CHANNEL, cmd, 1000 / portTICK_RATE_MS); i2c_cmd_link_delete(cmd); BCD时间转化 这里用的到C语言中的位域,这个方式比左右移位的方式更为高效,代码可读性更强。 data[0] = time.seconds.ten_sec*10 + time.seconds.seconds; data[1] = time.minutes.ten_min*10 + time.minutes.minutes; data[2] = time.hours.ten_hour*10 + time.hours.hours; data[3] = time.weeks.week; data[4] = time.dates.ten_date*10 + time.dates.date; data[5] = time.months.ten_month*10 + time.months.month; data[6] = time.years.ten_year*10 +time.years.year 位域简单说明 位域属于c语言中比较高阶的用法,非常适用于对一个字节总的某几个位进行赋值和取值操作。00h秒寄存器位域设置如下所示。 typedef struct { union { uint8_t value; struct { uint8_t seconds :4; uint8_t ten_sec :3; uint8_t ch :1; }; }; }second_data_TypeDef 位域利用的是union共用体和匿名struct结构体来实现。上述的union和struct实际上用的是一个内存地址,里面数据也是完全一样的。具体几种操作如下 second_data_TypeDef second; /*将second赋值为0xff*/ second.value = 0xff; /*其中seconds的值*/ second.seconds //值为0x0f second.ten_sec //值为 0x07 second.ch //值为0x1 /*赋值ch,字节初始值为0*/ second.ch = 1 ; //此时second.value = 0x80 通过上述的方式就可以完全避免了采用的移位的方式来取值和赋值。读写操作更为直观明了。 3.经验教训 本次调试过程中遇到的问题,上文中已经陆续都提到了,下面就进行一个简单总结
AT24C32相对简单调试过程较为顺利,此部分就简单记录下寄存器原理及代码简易说明。 //读写数据前要先写入内存地址,然后才能进行数读取 i2c_master_start(cmd); i2c_master_write_byte(cmd, EEROM24C32_ADDR << 1 | WRITE_BIT, ACK_CHECK_EN); i2c_master_write_byte(cmd, (addr>>8), ACK_CHECK_EN); i2c_master_write_byte(cmd, (addr), ACK_CHECK_EN) |
|||||||
|
|||||||
只有小组成员才能发言,加入小组>>
3311 浏览 9 评论
2994 浏览 16 评论
3493 浏览 1 评论
9058 浏览 16 评论
4087 浏览 18 评论
1178浏览 3评论
605浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
599浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2335浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1896浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-23 01:38 , Processed in 0.996020 second(s), Total 48, Slave 39 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号