摘要:提出了一种利用I2C总线实现ATmega88微控制器在应用编程的方法,详述了Bootloader程序及与其相应的上位机程序设计,以及利用PC机串口握手信号模拟I2C总线的方法。实践证明,该方法可成功实现I2C总线上多个ATmega88微控制器的在线调试与升级,也可用于其他AVR系列微控制器的在应用编程。
关键词:I2C总线;ATmega88微控制器;在应用编程;PC串口
引言
随着嵌入式系统技术的发展,电可擦除的Flash存储器由于具有容量大、成本低、编程方便等优点,在微控制器领域得到了广泛的应用Flash微控制器在正常运行前必须将Flash写入用户应用程序,目前对微控制器的Flash程序存储器进行编程的方法主要有出厂固化、编程器编程、在系统编程(In System Programming,ISP)和在应用编程(In Application programming,IAP)4种。
其中,出厂固化和编程器编程方法都要求微控制器在焊接前将程序写入,这显然不满足开发阶段的调试和日后升级的需要。目前比较普及的是在板可编程的ISP和IAP方法。ISP是通过微控制器的串行编程写入应用程序,需要少量的外部电路辅助实现;IAP将Flash映射为用户程序和Bootloader两个存储区,Bootloader可通过系统已有的USB、串口、SPI、I2C总线等各种通信接口,对用户程序进行更新而不需要外部电路辅助,实现更加灵活,可方便地实现程序的在线及远程升级。
在利用ATmega88微控制器开发四旋翼飞行器的无感无刷直流电机驱动器时,由于定时器PWM输出口与SPI接口存在引脚共用问题,用SPI口进行ISP编程时会使MOS管误导通而烧毁。由于驱动器中的4个ATmega88微控制器是通过I2C总线通信的,为了调试和升级方便,提出并实现了通过I2C总线对AVR微控制器进行在应用编程的方法,包括Bootloader程序、I2C总线的PC机串口模拟、上位机程序及相关的通信协议。实践证明,该方法可成功实现I2C总线上多个ATmega88微控制器的在线升级。
1 ATmega88微控制器的Bootloader设计
ATmega88是一款基于AVR增强RISC体系结构的CMOS低功耗8位微处理器,它通过执行强大的单周期指令,达到接近1 MIPS/MHz的运算效率。ATmega88的Flash被分为128个大小为64字节的页面,Flash的编程操作都是以页面为单位进行的。为了用户程序的安全性,以及用户的ISP和IAP编程需要,ATmega88的Flash存储空间被分为引导程序区(Bootloader Section)和应用程序区(Application Program Section)两部分。
引导程序区为非同时读写区,应用程序区为同时读写区。在非同时读写区内执行的代码可以对同时读写区内的页面进行编程操作,根据这一机制我们可以编制Bootloader程序并将其存储于引导程序区内,以实现应用程序区代码的在线与远程升级。
由于ATmega88分配给引导程序区的空间大小有限(最大2 KB),Bootloader程序一定要简洁而高效,图1给出了以I2C总线为通信接口的Bootloader程序流程图。
为了能够执行Bootloader程序,ATmega88熔丝位中的BOOTRST应设为零,这样在系统上电或应用程序接收到升级命令利用看门狗复位后,系统就能从引导程序区运行Bootloader程序。Bootloader程序中维持了一个溢出时间为2 s的定时器,该定时器利用TIM1以查询的方式实现。没有程序更新或程序更新完毕,程序在2 s内没有从I2C总线接收到数据帧时则利用(*((void(*)(void))(0x0000)))()函数跳转到应用程序区执行应用程序,在2s内接收到数据帧后,则将定时器重置,以继续接收数据帧更新应用程序。
在Bootloader实现中,ATmega88的I2C总线工作在从模式,上位机的I2C总线工作在主模式。上位机发送的数据帧由2字节的Flash页面地址、64字节的页面数据、1字节的密码和1字节的异或校验和构成。Bootloader接收到数据帧后会用数据长度、密码、异或校验和对数据帧进行校验,校验正确的话则根据数据帧中Flash的页面地址和数据相应的Flash页面进行编程,并将flag置1;校验错误的话,则丢弃数据帧等待重发的数据帧。
上位机在发送数据帧后读取flag,并根据其状态重发数据帧或发送下一页面的数据帧。flag被读取后Bootloader程序将其清零,这样就形成了一个简洁而有效的差错控制机制。Flash中页面的编程由页擦除和页编程两个过程组成,页擦除由AVR库函数中的boot_page_erase(addr)函数实现,addr为相应页面中的字节地址。
ATmega88的Flash是以页为单位进行擦除和写入操作的,因此在进行Flash页面写入前,要多次调用boot_page_fill(addr,data)函数将整页的程序代码写入临时缓冲区,其中addr为指令所要写入的字节地址,data为相应的由2个字节构成的16位程序指令。Flash页面的写入由boot_page_write(addr)函数实现,addr为相应页面中的字节地址。
为了程序安全,Flash执行页擦除和编程操作时要求CPU处于等待状态,因此Flash的页擦除和写入函数执行后都要调用boot_spm_busy_ wait()函数等待操作完成。此时,I2C总线也处于挂起状态,等所有操作完成后才能将总线释放。上位机软件在发送完数据帧后就一直监听总线状态,等总线释放后再读取flag状态,并决定是重发还是发送下一帧,这样就实现了有效的通信流量控制。
整个Bootloader程序编译完成后,大小为506字节。因此,熔丝位中的BOOTSZI和BOOTSZ0应设为1和0,将引导程序区设置为地址0x1E00~0x1FFF、大小为512字节的区域。为了使Bootloader程序能正确写入到该区域,程序编译时要将程序起始地址设定在0x1E00,在WinAVR中可以通过在Makefile中添加“LDFLAGS+=-W1,——section—start=.text=0x1E00”实现。编译完成的Bootloader可以在ATmega88确定PCB前,通过编程器或ISP写入到Flash中。
2 PC端编程软件设计
2.1 Intel Hex文件格式
ATmega88微控制器的目标程序代码是采用IntelHex文件格式保存的。Intel Hex文件包含了目标代码及相应的地址信息,这些实现在应用编程必需的信息由PC机端的上位机程序提取,并重新以页面为单位装帧后发送给Bootloader便可实现Flash的编程。
Intel Hex文件格式将二进制的目标机器代码以ASCII码的文本形式记录,在文件中,每一行都是一个由十六进制机器码或数据常量组成的Hex记录。记录格式如表1所列。
每条Hex记录都是以“:”开头的,表1中所述的每个字节在记录中是由两个ASCII码表示的,这样的两个十六进制数为一个字节;长度表示的是记录中数据项的长度;地址为数据项在Flash中的起始地址;记录的类型总共有6种,分别为数据记录(00)、文件结束记录(01)、扩展段地址记录(01)、开始段地址记录(03)、扩展线性地址记录(04)、开始线性地址记录(05)。数据是与记录类型相对应的可变长度的数据组。校验字节计算如下:首先,将每条记录中除记录头和校验外所有ASCII码以2个ASCII码转换为1个字节的形式转换为二进制。然后计算上述二进制字节的累加和,最后将累加和的低字节取反加1即为校验字节。
ATmega88目标代码的Hex文件由数据记录和文件结束记录两种类型的记录构成。上位机程序在解析过程中以行为单位读取文件中的记录,并根据上述记录的格式进行解析,获得Flash每一页面的地址和相应的数据,遇到文件结束记录后则停止解析。
2.2 I2C总线的PC机串口模拟
PC机端的编程软件是通过I2C总线与ATmega88的Bootloader通信的,I2C总线在微控制器中是广泛存在的,一般的微控制器都集成了I2C总线控制模块。但PC机基本没有I2C总线接口,需要专用的USB转I2C总线协议芯片或其他接口的I2C总线模块才能实现PC机与微控制器之间的I2C总线通信。这种方法成本高且实现麻烦,本文给出了一种利用PC机串口的握手信号模拟I2C总线的方法,相比专用协议转换芯片或模块的方法,这种方法更加简单、高效。为了实现PC机RS232串口与微控制器I2C总线的电平匹配,设计了如图2所示的接口电路。
图2中,PC机串口的RTS输出用来模拟I2C总线的SCI,时钟信号;DTR输出模拟I2C总线的SDA输出数据信号;CTS输入用于接收SDA输入数据。PC机串口的RS232的高电平为15 V,低电平为-15 V;I2C总线的高电平为+5 V,低电平为0 V。因此,将PC串口的握手信号转换成I2C总线信号时需要进行相应的电平转换,PC机串口RS232电平与I2C总线FTL电平之间的转换是由电阻R1、R2和5.1V稳压管D1、D2实现的。
当RTS输出+15 V高电平时,由于电阻和稳压管的作用,SCL端电平被稳定在+5.1 V;而当RTS输出-15 V低电平时,由于二极管D2导通,SCL电平被钳位在-0.7 V。这样,便实现了±15 V的RS232电平到0~5 V CMOS电平的转换;电阻同时也起着限流作用。DTR输出到SDA信号的电平转换也是同样的原理,而当SDA处于输入状态时,由于CMOS电平可以满足RS232电平的输入容限,因此无需进行电平转换。
由于用PC机串口模拟I2C总线时仅仅用到了串口的握手信号,而没有用到串口的波特率、数据长度、奇偶校验等设置功能及输入/输出缓冲区的管理功能,本文直接采用Windows提供的API函数实现串口编程。串口的打开和关闭分别采用CreateFile函数和CloseHandle函数实现。RTS和DTR信号高低电平的控制由EscapeCommFunction函数将串口作为文件操作实现,调用该函数后程序要有一定时间的延时以实现通信波特率的控制。CTS的电平状态则由GetCommModemStatus函数查询得到。
在实现了RTS、DTR的电平控制与CTS电平状态的获取后,借鉴单片机用I/O口模拟I2C总线的方法,可以通过控制RTS、DTR电平与查询CTS状态来模拟I2C总线。在总线的时序处理与读写操作方面,两种方法的唯一不同在于;用单片机I/O口模拟I2C总线时,I2C总线的SDA信号由输出模式转换到输入模式是通过将单片机I/O口从输出转换为输入实现的;由于串口握手信号无法实现双向通信,因此,SDA信号的输入功能是通过将DTR置高电平后读取CTS状态实现的,之所以将DTR置高电平是因为微控制器端的I2C总线的集电极开漏输出结构需要DTR置高后才能输出高电平,这类似于I2C总线上拉电阻的功能。
2.3 上位机程序设计
PC端上位机程序的主要功能为:解析应用程序的Hex格式文件,并从中提取Flash中每一页面的地址与数据信息;设置串口号与所需升级的ATmega88的I2C总线地址,利用串口的握手信号模拟I2C总线通信,将Hex文件中的程序代码准确无误地发送给相应地址的Bootloader以实现应用程序的在线更新。
根据上述功能设计了如图3所示的上位机程序界面,开发环境采用Borland C++builder 5.0,串口操作通过Windows API接口函数实现。
I2C总线通信的波特率设置为10 kbps,这是通过每次EscapeCommFunction函数调用后运行相应时间的延时函数实现的,这样也可以使RTS和DTR信号在改变电平后有足够的稳定时间。点击“烧录程序”按钮后,上位机程序通过I2C总线向相应地址的ATmega88发送复位命令,然后循环发送Flash第一页的数据帧;ATmega88接收到复位命令利用看门狗复位或人工上电复位后,跳转运行Bootloader程序,开始接收数据帧并对相应的Flash页面进行编程。Bootloader接收到一帧数据后将I2C总线拉低,使总线处于忙状态,此时上位机一直查询SDA状态直到SDA恢复高电平后再操作I2C总线,这样便实现了有效的通信流量控制。Bootloader对数据帧进行校验后对flag进行标记,上位机发送完数据帧等I2C总线空闲后,读取flag并根据其状态重发数据帧或发送下一帧数据,这样便实现了通信的差错控制,保证写入程序代码的正确性。所有Flash页面编程完毕后,上位机程序关闭串口,Bootloader在2 s内接收不到数据帧后,就能跳转去运行升级后的应用程序。
结语
本文给出了一套完整的利用I2C总线实现ATmega88微控制器在应用编程的方法,包括Bootloader程序、I2C总线的PC串口模拟、上位机程序及相关的通信协议。该方法应用于四旋翼飞行器的无感无刷直流电机驱动板,成功通过I2C总线实现了4个ATmega88微控制器的在线调试与升级。该方法经过少量针对具体微控制器的代码修改后也可用于其他AVR系列微控制器的在应用编程。