深圳市华胄科技有限公司 >> MCU专题 >> 单片机教程


AVR 单片机与GCC 编程----之六



提交者 阳光总在风雨后  在  2008-6-12 14:03:20 

上一篇 下一篇
第六章 硬件TWI 端口编程


6.1 TWI 模块概述


ATMega 系列单片机片内集成两线制串行接口模块,Atmel 文档称它为TWI 接口。事实上TWI 与PHILIPS 的I2C 总线是同一回事,之所以叫它TWI 是因为这样的命名可使Atmel 避免交术语版税。所以,TWI 兼容I2C 更一种说法。


关于I2C 协议参考PHILIPS 相关文档。


AVR 硬件实现的TWI 接口是面向字节和基于中断的,相对软件模拟I2C 总线有更好的实时性和代码效率,引脚输入部分还配有毛刺抑制单元,可去除高频干扰。另外,结合AVR I/0端口功能,在TWI 使能时可设置SCL 和SDA 引脚对应的I/O 口内部上拉电阻有效,这样可省去I2C 要求的外部两个上拉电阻。


下面以MEGA8 为例,简要介绍TWI 接口的工作方式。


在I2C 总线上MEGA8 可扮演主控制器(主控模式)和从器件(被控模式)的角色。


不论主控模式还是被控模式都应当将TWI 控制寄存器TWCR 的TWEN 位置1 从而使能TWI模块。TWEN 位被置位后I/O 引脚PC5 和PC4 被转换成SCL 和SDA,该管脚上的斜率限制和毛刺滤波器生效。如果外部没有接上拉电阻可用类似如下的操作使能该管脚上的内部上拉电阻:


DDRC&=0Xcf;
PORTC|=0X30;
对TWI 控制寄存器TWCR 的操作可在总线上产生START 和STOP 信号,从一个START 到STOP 被认为是主控模式的行为。


将TWI 地址寄存器TWAR 的第一位TWGCE 置有效,同时将TWI 控制寄存器TWCR 的TWEA(应答允许)位置1,TWI 模块就可以对总线上对它的寻址做出应答,并置状态字。


对总线的操作或总线上产生事件后应用程序应当根据TWI 状态寄存器值来确定下一步的操作。关于不同模式下的状态值的详细描述参考MEGA8 的数据手册。


对TWI 模块的操作均为寄存器的读写操作,Avr-libc 没有提供专门的API。 文件twi.h定义了状态字的常量和一个返回状态字的宏。


6.2 主控模式操作实时时钟DS1307


一 实时时钟DS1307 介绍


DS1307是低功耗、两线制串行读写接口、日历和时钟数据按BCD码存取的时钟/日历芯片。


它提供秒、分、小时、星期、日期、月和年等时钟日历数据。另外它还集成了如下几点功能:
?? 56 字节掉电时电池保持的NV SRAM 数据存储器
?? 可编程的方波信号输出
?? 掉电检测和自动切换电池供电模式


DS1307 把8 个寄存器和56 字节的RAM 进行了统一编址,具体地址和寄器数据组织格式如下表:




表 6-1 DS1307 内存组织结构


在读写过程中DS1307 内部维护一个地址指针,通过写操作可对它负值,读和写每一字节时自动加一,当指针越过DS1307 内部RAM 尾部时指针将返回到0 地址处。


DS1307 的时钟和日历数据按BCD 码存储。


方波信号输出功能:
方波信号输出功能从SQW/OUT 引脚输出设置频率的方波,CONTROL 寄存器用于控制SQW/OUT 脚的输出。


BIT7(OUT):此位表示在方波输出被禁止时SQW/OUT 脚的逻辑电平,在SQWE=0(输出禁止)时若OUT 为1 则SQL/OUT 脚为高电平, 反之亦然。
BIT4(SQWE)方波输出允许/禁止控制位,1 有效。
BIT0(RS0)、BIT1(RS1)用于设定输出波形的频率,如下表:


表6-2 方波信号输出频率设置


RS1 RS0 输出频率(Hz)
0 0 1
0 1 4096
1 0 8192
1 1 32768


要注意的是,00h 地址的第7 位为器件时钟允许位(CH),由于在在开始上电时内部RAM内容随机,所以在初始化时将CH 位设零(时钟允许)是非常重要的。DS1307 在TWI 总线上是个从器件,地址(SLA)固定为1101000。DS1307 写操作– TWI 被控接收模式主控器件按如下顺序将数据写入到DS1307 寄存器或内部RAM 中:


1.START 信号
2.写SLA+W(0xd0)字节,DS1307 应答(ACK)
3.写1 字节内存地址(在以下第四步写入的第一字节将存入到DS1307 内该地址处,DS1307应答)
4.写数据(可写多个字节,每一字节写入后DS1307 内部地址计数器加一,DS1307 应答)
5.STOP 信号
DS1307 读操作– TWI 被控发送模式


主控器件按如下顺序将DS1307 寄存器或内部RAM 数据读取:
1.START 信号
2.写SLA+R(0xd1)字节,DS1307 应答(ACK)
3.读数据(可读多个字节,读取数据的DS1307 内部地址由上次写操作或读操作决定,读取每一字节DS1307 内部地址计数器加一,主器件应答,读取最后一字节时主器件回应一NACK信号)
4.STOP 信号


二 DS1307 实验电路


DS1307 与Mega8 间的连接如6-1 所示,DS1307 的X1 和X2 管脚需接32768Hz 晶振。
Vbat 引脚接的电池电压必需在2.0V~3.5V 范围内。当VCC 引脚上的电压降到1.25 倍电池电压时DS1307 内部写保护电路生效,RTC 数据和内部RAM 的读写补禁止。




图6-1 Mega8 与DS1307 的连接电路


三 程序设计


以下是操作DS1307 的示例程序,利用基于USART 的标准I/O 实现读写日历和时钟。
/*
Mega8 硬件TWI 接口读写实时时钟DS1307 程序
文件名:main.c
芯艺 2004-09-02 ---------- 2004-09-07
*/
#include
#include
#include
#include
#include
#define uint unsigned int
#define uchar unsigned char
#define FREQ 4
#define DS1307_ADDR 0XD0
#define TW_ACK 1
#define TW_NACK 0
#define RTC_READ 1
#define RTC_WRITE 0
FILE *g_hFile;
uchar g_aTimeBuf[7];//日历/时钟BCD 格式缓冲区
uchar g_aTimeBin[7];//时钟/日历二进制格式缓冲区
void DelayMs(uint ms)
{
uint i;
for(i=0;i_delay_loop_2(FREQ *250);
}
/*******************标准I/O 功能****************开始********/
//标准I/O 输出函数
int usart_putchar(char c)
{
if(c=='\n')
usart_putchar('\r');
loop_until_bit_is_set(UCSRA,UDRE);
UDR=c;
return 0;
}
//标准I/O 输入函数
int usart_getchar(void)
{
loop_until_bit_is_set(UCSRA,RXC);
return UDR;
}
//初始化
void IoInit(void)
{
//串行口初始化
UCSRB=_BV(RXEN)|_BV(TXEN);/*(1<UBRRL=25; //9600 baud 6MHz:38 4MHz:25
//UART 用于标准I/O 输入输出
g_hFile=fdevopen(usart_putchar,usart_getchar,0);
}
/*******************标准I/O 功能**************结束**********/
/*************主模式TWI 操作部分*************开始**************/
//总线上起动停止条件
void twi_stop(void)
{
TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
}
//总线上起动开始条件
uchar twi_start(void)
{
TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);
while ((TWCR & _BV(TWINT)) == 0) ;
return TW_STATUS;
}
//写一字节
uchar twi_writebyte(uchar c)
{
TWDR = c;
TWCR = _BV(TWINT) | _BV(TWEN);
while ((TWCR & _BV(TWINT)) == 0);
return TW_STATUS;
}
//读一字节 ack: true 时发ACK,false 时发NACK
uchar twi_readbyte(uchar *c ,uchar ack)
{
uchar tmp=_BV(TWINT)|_BV(TWEN);
if(ack)
tmp|=_BV(TWEA);
TWCR=tmp;
while ((TWCR & _BV(TWINT)) == 0) ;
*c=TWDR;
return TW_STATUS;
}
/*************主模式TWI 操作部分*************结束**************/
/*************DS1307 操作**************开始**************/
//对DS1307 内存连续的写操作
uchar rtc_write(uchar addr,uchar *buf,uchar len)
{
uchar i;
twi_start();
twi_writebyte(DS1307_ADDR|TW_WRITE);
twi_writebyte(addr);//write address
for(i=0;itwi_writebyte(buf[i]);
twi_stop();
return 0;
}
//对DS1307 内存连续的读操作
uchar rtc_read(uchar addr,uchar *buf,uchar len)
{
uchar i;
rtc_write(addr,0,0);//set address
DelayMs(10);
twi_start();
twi_writebyte(DS1307_ADDR|TW_READ);
for(i=0;itwi_readbyte(buf+i,TW_ACK);
twi_readbyte(buf+i,TW_NACK);
twi_stop();
return 0;
}
/*************DS1307 操作**************结束**************/
/*************接口部分****************开始**************/
//初始化TWI 功能
void RtcInit(void)
{
TWBR=73;
}
//更新或读取DS1307 日历/时间数据
uchar RtcUpdateData(uchar direction)
{
uchar ret;
if(direction) //读
ret=rtc_read(0,g_aTimeBuf,7);
else //写
ret=rtc_write(0,g_aTimeBuf,7);
return ret;
}
//读DS1307 用户RAM
uchar RtcReadRAM(uchar addr,uchar *buf,uchar len)
{
addr+=8;
return rtc_read(addr,buf,len);
}
//写DS1307 用户RAM
uchar RtcWriteRAM(uchar addr,uchar *buf,uchar len)
{
addr+=8;
return rtc_write(addr,buf,len);
}
uchar byte_bintobcd(uchar bin)
{
uchar ret;
bin&=0x7f;
bin%=100;
ret=bin/10;
ret <<=4;
ret|=bin%10;
return ret;
}
uchar byte_bcdtobin(uchar bcd)
{
uchar ret;
ret=bcd & 0x0f;
ret+=(bcd>>4)*10;
return ret;
}
//将二进制格式缓冲区(g_aTimeBin)内容转换成BCD 格式后保存到
//BCD 格式缓冲区(g_aTimeBuf)
void RtcBinToBCD()
{
uchar i;
g_aTimeBin[0]&=0x7f;
g_aTimeBin[1]&=0x7f;
g_aTimeBin[2]&=0x3f;
g_aTimeBin[3]&=0x07;
g_aTimeBin[4]&=0x3f;
g_aTimeBin[5]&=0x1f;
g_aTimeBin[6]&=0xff;
for(i=0;i<7;i++)
g_aTimeBuf[i]=byte_bintobcd(g_aTimeBin[i]);
}
//将BCD 格式缓冲区(g_aTimeBuf)内容转换成二进制格式后保存到
//二进制格式缓冲区(g_aTimeBin)
void RtcBCDToBin()
{
uchar i;
for(i=0;i<7;i++)
g_aTimeBin[i]=byte_bcdtobin(g_aTimeBuf[i]);
}
//写DS1307 配置字节
void RtcSetSQWOutput(uchar en,uchar level)
{
//en:方波输出允许 TRUE 有效 level:如果输出禁止 OUT 口的逻辑电平
uchar c=0;
if(en) //enable
c=0x10;
else //disable
{
if(level)
c=0x80;
}
rtc_write(7,&c,1);
}
/*************接口部分***************结束**************/
int main(void)
{
uchar i;
char c;
int tmp[7];//从标准I/O 读取缓冲区
IoInit();
RtcInit();
printf_P(PSTR("输入命令:g - 打印日历/时钟,s - 设置日历/时钟,\
h - 帮助信息\n"));
while(1) //main loop
{
scanf("%c",&c);
if(c=='g')
{
RtcUpdateData(RTC_READ);
RtcBCDToBin();
printf_P(PSTR("当前日历/时钟:%d 年%d 月%d 日 星期\
%d %d:%d:%d\n"),\
g_aTimeBin[6],g_aTimeBin[5],g_aTimeBin[4],g_aTimeBin[3],\
g_aTimeBin[2],g_aTimeBin[1],g_aTimeBin[0]);
}
else if(c=='s')
{
printf_P(PSTR("请按 < 年 月 日 星期 小时 分 秒 > 格式输入:\n"));
scanf("%d,%d,%d,%d,%d,%d,%d",\
tmp+6,tmp+5,tmp+4,tmp+3,tmp+2,tmp+1,tmp);
for(i=0;i<7;i++)
g_aTimeBin[i]=(uchar)tmp[i];
RtcBinToBCD();
RtcUpdateData(RTC_WRITE);
printf_P(PSTR("\n 设置完成!\n"));
}
else if(c=='h')
printf_P(PSTR("输入命令:g - 打印日历/时钟,s - 设置日历/时钟,\
h - 帮助信息\n"));
}//main loop
return 0;
}


将编译后的代码写入Mega8, 按第四章所示电路连接到计算机就可以用PrintMonitor测试程序了,测试结果如图6-2 所示。





图 6-2 PrintMonitor 测试结果


twi_start,twi_writebyte,twi_readbyte 三个函数操作后均返回TWI 状态寄存器值,在实际应用中应当检测这样的值,处理出现错误的情况。


单片机教程,五系列(55讲)电子书全集下载

论坛精选:
单片机教程,MCS51单片机从零开始 第一讲:初识单片机
http://bbs.huazhoucn.com/Topic.aspx?id=2539
单片机教程,MCS51单片机从零开始 第二讲:MCS-51单片机简述
http://bbs.huazhoucn.com/Topic.aspx?id=2540
单片机教程,MCS51单片机从零开始 第三讲:单片机相关常用名词解释
http://bbs.huazhoucn.com/Topic.aspx?id=2541
单片机教程,MCS51单片机从零开始 第四讲:计算机中数的表示及运算
http://bbs.huazhoucn.com/Topic.aspx?id=2542
单片机教程,MCS51单片机从零开始 第五讲:常用逻辑电路
http://bbs.huazhoucn.com/Topic.aspx?id=2544
单片机教程,MCS51单片机从零开始 第六讲:51单片机的结构及其组成
http://bbs.huazhoucn.com/Topic.aspx?id=2545
单片机教程,MCS51单片机从零开始 第七讲:51单片机的引脚
http://bbs.huazhoucn.com/Topic.aspx?id=2546
单片机教程,MCS51单片机从零开始 第八讲:8051单片机I/O引脚工作原理
http://bbs.huazhoucn.com/Topic.aspx?id=2547
单片机教程,MCS51单片机从零开始 第九讲:8051单片机的存储器结构
http://bbs.huazhoucn.com/Topic.aspx?id=2548
单片机教程,MCS51单片机从零开始 第十讲:编码及译码器工作原理
http://bbs.huazhoucn.com/Topic.aspx?id=2549
单片机教程,MCS51单片机从零开始 第十一讲:存储器的存储原理
http://bbs.huazhoucn.com/Topic.aspx?id=2550
单片机教程,MCS51单片机从零开始 第十二讲:51单片机的特殊功能寄存器
http://bbs.huazhoucn.com/Topic.aspx?id=2551
单片机教程,MCS51单片机从零开始 第十三讲:51单片机CPU的内部结构
http://bbs.huazhoucn.com/Topic.aspx?id=2552
单片机教程,MCS51单片机从零开始 第十四讲:定时器/计数器的基本结构及工作原理
http://bbs.huazhoucn.com/Topic.aspx?id=2553
单片机教程,MCS51单片机从零开始 第十五讲:51单片机的中断系统
http://bbs.huazhoucn.com/Topic.aspx?id=2554
单片机教程,MCS51单片机从零开始 第十六讲:51单片机的复位
http://bbs.huazhoucn.com/Topic.aspx?id=2555
单片机教程,MCS51单片机从零开始 第十七讲:51单片机执行指令的过程
http://bbs.huazhoucn.com/Topic.aspx?id=2556
单片机教程,MCS51单片机从零开始 第十八讲:51单片机的延时及时序分析
http://bbs.huazhoucn.com/Topic.aspx?id=2557
单片机教程,MCS51单片机从零开始 第十九讲:汇编语言基础
http://bbs.huazhoucn.com/Topic.aspx?id=2558
单片机教程,MCS51单片机从零开始 第二十讲:汇编语言及汇编过程
http://bbs.huazhoucn.com/Topic.aspx?id=2556
单片机教程,MCS51单片机从零开始 第二十一讲:汇编程序的基本结构
http://bbs.huazhoucn.com/Topic.aspx?id=2560
单片机教程,MCS51单片机从零开始 第二十二讲:51单片机的寻址方式
http://bbs.huazhoucn.com/Topic.aspx?id=2561
单片机教程,MCS51单片机从零开始 第二十三讲:数据传送类指令分析
http://bbs.huazhoucn.com/Topic.aspx?id=2562
单片机教程,MCS51单片机从零开始 第二十四讲:算术运算类指令分析
http://bbs.huazhoucn.com/Topic.aspx?id=2563
单片机教程,MCS51单片机从零开始 第二十五讲:逻辑运算及移位指令分析
http://bbs.huazhoucn.com/Topic.aspx?id=2564
单片机教程,MCS51单片机从零开始 第二十六讲:控制转移类指令分析
http://bbs.huazhoucn.com/Topic.aspx?id=2565
单片机教程,MCS51单片机从零开始 第二十七讲:布尔变量操作指令分析
http://bbs.huazhoucn.com/Topic.aspx?id=2566
单片机教程,MCS51单片机从零开始 第二十八讲:伪指令分析
http://bbs.huazhoucn.com/Topic.aspx?id=2567

1 楼  提交者:耕牛 在 2008-6-13 9:24:18
好资料  顶个
2 楼  提交者:Guest 在 2008-7-10 20:42:58
3 楼  提交者:Guest 在 2008-7-13 14:26:17
4 楼  提交者:Guest 在 2008-7-13 22:29:35
5 楼  提交者:Guest 在 2008-7-17 11:26:57
vb
6 楼  提交者:Guest 在 2008-7-17 20:35:38
7 楼  提交者:Guest 在 2008-7-17 20:59:23
h
8 楼  提交者:Guest 在 2008-7-21 15:58:45
是对的
9 楼  提交者:Guest 在 2008-7-24 15:23:12
ding ding 
10 楼  提交者:Guest 在 2008-7-25 18:50:27
去我
11 楼  提交者:Guest 在 2008-7-26 9:27:53
好的
12 楼  提交者:Guest 在 2008-7-28 13:52:44
好资料
13 楼  提交者:fightsqlee 在 2008-7-30 9:01:33
还是谢谢你
14 楼  提交者:Guest 在 2008-8-2 15:26:19
先谢过,看看是否有用
15 楼  提交者:Guest 在 2008-8-6 8:35:20
gdsfgfds
16 楼  提交者:Guest 在 2008-8-7 10:59:34
谢谢
17 楼  提交者:qsjit 在 2008-8-7 20:03:04
18 楼  提交者:Guest 在 2008-8-12 14:08:55
嘿嘿
19 楼  提交者:Guest 在 2008-8-13 22:53:22
good
20 楼  提交者:Guest 在 2008-8-14 23:28:07
sdfdsfgsfdgfgdxfg
21 楼  提交者:Guest 在 2008-8-15 21:43:11
好东西
22 楼  提交者:Guest 在 2008-8-15 21:43:11
好东西
23 楼  提交者:李得中 在 2008-8-16 11:41:55
24 楼  提交者:Guest 在 2008-8-17 14:08:20
看不了????????????????
25 楼  提交者:Guest 在 2008-8-20 13:20:43
26 楼  提交者:Guest 在 2008-8-21 16:02:04
学习
27 楼  提交者:Guest 在 2008-8-23 11:18:38
fddddddddddddddddddddd
28 楼  提交者:Guest 在 2008-8-29 19:28:27
看了再评论
29 楼  提交者:要飞的鸟 在 2008-8-31 8:14:48
good
30 楼  提交者:Guest 在 2008-9-2 10:09:37
nm>M<"KL';'
上一篇 下一篇
当前第〖1〗页 共有4页 转到第 1 2 3 4