器件
Atmega8,Atmega88 169 32
MEGA8焊接成MEGA88了,虽然Mega88和Mega8 Pin-pin 兼容,但和Mega8的寄存器略有不同,还得重新为Mega88编译代码。3V下LCD亮了,但是2.3V下LCD已经
mega88则是在兼容、继承、发展meng8的基础上设计的
开发环境
AVR开发环境:
- AVR studio
- WinAVR
- IAR
SL 烧写器
IAR /GCC 编译器
IAR在51,AVR,ARM的C上都是非常优秀的,它针对不同的单片机都有不同的C版本.唯一一点遗憾的是IAR的价格是个人和小公司难以承受的.当然网上有很多破解,现在的最新版4.20A也有了破解.
用了IAR,才知不是一般的好……
我于一年前就转到IAR571182
不过原因并不是因为GCC的性能不好,只因为IAR编译出来的固件比GCC的小很多。
程序存储空间用完
IAR压缩
超过容量问题
WinAVR-20071221-install 编译Program: 7616 bytes (93.0% Full)
WinAVR(GCC)-20081205-install 104%,还没有2007版本编译效率高
WinAVR-20090313-install Program: 8424 bytes (102.8% Full)
past is past,past 反映在现在
关于移植:
#include<avr/io.h> 等去掉
中断定义方式不一样
全部将inc文件复制过来,或者
头文件,条件编译
8K程序存储空间
hex文件21K多但是还没有超出,说明不是这样对应的
AVR特点
一,需要通过逻辑运算实现位操作
二,上拉配置 芯片内部可设置上拉电阻
三,熔丝
通过置1来清零
输入输出方向设置和PIC相反
中断声明随编译器变化
AVR也需要对IO口方向进行编程
1是output(read ),0是写 input(写)
这正好和pic相反!!!
---第一,端口驱动
用LED显示或者量电平
状态要延时一定时间才能显示
发现问题 :端口单个位操作不方便
发现问题:
高低电平都正常了,为什么闪烁就不行了?
肯定端口输出有问题
不是延时的问题
单个位如何处理
位操作
AVR系列单片机最大的缺陷莫过于其对 IO寄存器的位操作上。
但AVR的位操作功能确实不如MCS51、PIC,但绝不影响控制功能的实现。另外AVR的SRAM比较多,不妨改一下观念,不要始终拿MCS51和PIC的习惯写AVR的程序,如在MCS51、PIC中用一个BIT来作标志位,而在AVR中不妨用一个字节来作标志。
还分PIN,我操
<<
事物存在总有它的道理,不要因为用惯了51就拿51跟它比, 51与AVR根本不是一个档次.
1,位操作: AVR中的位操作只需要一个周期就可以完成,不管是置位还是清零,因此,根本不需要象楼主所说的要关闭中断,而标准的51需要12个周期.AVR的位操作在书写格式上也很方便, 学习过其它CPU构架的人就会体会这点,比如ARM, XSCALE, 都是采用这种方式控制位操作的,当然,只学过51的人是不会体会到的
2,ADC的基准源: 基准源最主要的指标是温度飘移,而不是初始精度,AVR中的基准源以及ADC, 是设计用来诸如电压检测等对精度要求不高的场合,我曾经也将它用于触摸屏控制,几乎不花一分钱(只外加了一个CD4051)就实现了高可靠的,高精度的触摸屏控制, 关键还在于你如何设计,如何利用软件校准误差,并尽量在休眠下使用ADC,不是拿来就用,是要下一点小功夫的,多学点是有好处,其它CPU也有类似的情况,况且,AVR以如此低的价格(M8仅7元左右)提供了多通道的10位ADC和基准源,还有什么好指责的?
3,关于IIC与TWI, 这个问题是这样的, IIC总线是飞利普的专利,所有使用IIC总线技术的厂商都要交专利费,但很多公司自行开发了兼容于IIC的总线技术,例如AVR中的TWI, 如果你见识足够的话,还会在其它芯片的DATASHEET中看到类似的例子.其结果是,由于省去了专利费,芯片价格下来了,而且又能与IIC兼容, 最终受惠的是我们这些消费者,
4,关于熔丝设定: 这是为了器件保密,以及器件安全所需,仔细体会AVR关于熔丝位及保密位的设置初衷时,你就会佩服AVR的设计师,另外,在程序中修改熔丝位是可以的,仔细阅读原文手册, 不要过于浮燥
最后说一点:不要过于浮燥,不要把AVR设计师看得过于幼稚.AVR不是普通51所能比拟的,当然,对使用者的要求也要高一些.
>>
位操作
与一个数进行逻辑运算获得
首先要明白移位操作:
tmp<<=1 表示tmp字节左移一位
1<<3 则表示1向左移动了三位!
置1
PB0=1
->PORTB = 0X01;
PB1=1
->PORTB =0X02;
保证PORTB得到一个1,其他位则保持原状态
清零
PB0=0
->PORTB &=0XFE;
即:PORTB &=0X01;(1<<(bitn)))
获得某位
别的都清零
PORTB0
PORTB&(1<<X)
理解
AVR的编译器中,只有CVAVR支持位操作,可以 PORTA.1=0; 这样用
其余的编译器都不支持位操作
由于AVR不支持位操作,所以必须通过软件来实现。下面对我所知道的两种方法进行一个简单的比较。
1、位域方式。先定义一个位域,
typedef struct _bit_struct
{
unsigned char bit0 : 1 ;
unsigned char bit1 : 1 ;
unsigned char bit2 : 1 ;
unsigned char bit3 : 1 ;
unsigned char bit4 : 1 ;
unsigned char bit5 : 1 ;
unsigned char bit7 : 1 ;
unsigned char bit6 : 1 ;
}bit_field;
再用一个宏 ,来指向要操作的位。
#define LED GET_BITFIELD(PORTB).bit0
#define BUTTON GET_BITFIELD(PINB).bit7
使用时只需要直接赋值即可:如LED = 0 ,LED = 1, 或者直接判断 LED==0 , LED ==1.
这种方法类似C51中的位操作。直接。
2、位移宏方式。主要有三个.
#define Set_Bit(val, bitn) (val =(1<<(bitn)))
#define Clr_Bit(val, bitn) (val&=
#define Get_Bit(val, bitn) (val &(1<<(bitn)) )
三个分别用来设置某一位,清除某一位,取某一位的值.
使用方法为.Set_Bit(PORTA,3); Clr_Bit(PORTB,2); Get_Bit(val,5);
// testled.c 测试AVR的位操作.
// 这是gcc;如是其它编译器,请修改。
#include <avr/io.h>
// 定义一个寄存器(Register)或端口(Port)的八个位
typedef struct _bit_struct
{
unsigned char bit0 : 1 ;
unsigned char bit1 : 1 ;
unsigned char bit2 : 1 ;
unsigned char bit3 : 1 ;
unsigned char bit4 : 1 ;
unsigned char bit5 : 1 ;
unsigned char bit7 : 1 ;
unsigned char bit6 : 1 ;
}bit_field;
//定义一个宏,用来得到每一位的值
#define GET_BITFIELD(addr) (*((volatile bit_field *) (addr)))
//定义每一个位
#define LED GET_BITFIELD(PORTB).bit0
#define BUTTON GET_BITFIELD(PINB).bit7
int main( void )
{
DDRB = 0x41; //配置PB0为输出,PB7为输入
if ( BUTTON==0 ) LED = 1; else LED = 0;
while(1);
}
中断
在ICCAVR下,用以下语句可定义中断程序
#pragma interrupt_handler 函数名:中断向量
要注意不同的AVR芯片的中断向量是不同的,因此在ICCAVR下编译,除了要在程序上注明芯片的类型外,还要在PROJECT的OPTION页选择正确的芯片,否则有可能导致程序运行不正常;而GCC只要在MFILE里面选定芯片型号就可以,GCC在这点上的使用比ICCAVR的方便。
附:定时器1溢出中断例子,每溢出一次就令i自加1,然后在LCD1602上显示出来
-———————————————————————————————–
#include <iom16v.h>
#include <macros.h>
#pragma interrupt_handler ANA_COMP:10 //定时器1溢出中断
#define uchar unsigned char
#define uint unsigned int
#define LcdBus PORTA
#define rs 2 //LCD端口定义
#define rw 3
#define en 4
void LcdIni(void);
void WrOp(uchar dat);
void WrDat(uchar dat);
void ChkBusy(void);
void DisText(uchar addr,uchar *text);
void DisTextConst(uchar addr,uchar const *text);
void ShowNum(uchar addr,uint num); //在addr处显示数字num
int AdcVal(uchar n);
void delayms(uint n);
int AdcValPro(uchar n);
void ANA_COMP(void);
char BCD[6]; //十位二进制的显示码分别是千百十个位的显示
int Adc[3]; //存放Adc转换后的值
int i=0;
void main(void)
{
LcdIni();
TIMSK=0X04;
TCNT1H=0XFF;
TCNT1L=0X00;
TCCR1A=0X00;
TCCR1B=0X01;
SEI(); //允许全局中断
while(1);
}
void ANA_COMP(void)
{//中断处理程序,溢出时就令i增加1
TCNT1H=0XFF;
TCNT1L=0X00;
ShowNum(0x82,i++);
}
int AdcVal(uchar n)
{//软件滤波
uchar i,k;
uint tmp=0;
for(i=0;i<20;i++)
tmp=(tmp+AdcValPro(n))/2;
k=tmp>Adc[n] ? (tmp-Adc[n]):(Adc[n]-tmp);
Adc[n]=k>0 ? tmp:Adc[n];
return Adc[n];
}
int AdcValPro(uchar n)
{//返回第N个ADC的值从0到7
uchar dat1,dat0;
int val;
DDRA &=BIT(n); //设置对应的ADC口为输入BIT(n);
PORTA &=
ADMUX=0x40+n; //选择AVCC,选择第N个ADC
ADCSRA=0xc0; //允许转换ADEN,ADSC
while(ADCSRA & BIT(ADSC)); //
dat1=ADCH;
dat0=ADCL;
val=ADCH*256+ADCL;
return val;
}
void ShowNum(uchar addr,uint num) //在addr处显示数字num
{//将num转化成五个BCD码存放在全局数组BCD[5]中
uchar i;
for(i=5;i>0;i–) //将NUM数据转化成ASCII码,如521会转化为00521
{
BCD[i-1]=(uchar)(num%10+0x30); //取出最低位
num/=10; //去掉最低位
}
i=0;
while(BCD[i] ==0x30 && i<4) BCD[i++]=’ ‘; //NUM转换成数组存放,但还没有加上小数点
BCD[5]=’\0’;
DisText(addr,BCD+1);
}
void DisText(uchar addr,uchar *p)
{
WrOp(addr);
while(*p !=’\0’)WrDat(*(p++));
}
void DisTextConst(uchar addr,uchar const *p)
{
WrOp(addr);
while(*p !=’\0’)WrDat(*(p++));
}
void LcdIni()
{
uint i;
DDRD=0XFF; //设置PA输出
PORTD=0XFF; //全部加上上拉电阻
DDRC=0XFF; //设置PC为输出
PORTC=0XFF; //全部加上上拉电阻
WrOp(0x38);
WrOp(0x06); //光标加1
WrOp(0x0c); //开显示
WrOp(0x01);
for(i=0;i<5000;i++);
}
void WrOp(uchar dat)
{
uchar i;
ChkBusy();
PORTD &=BIT(rs); //RS=0BIT(rw); //RW=0
PORTD &=
PORTD &=BIT(en); //EN=0BIT(en); //EN=0
PORTC =dat; //送数据
PORTD =BIT(en); //EN=1
for(i=1;i;i++); //延时
PORTD &=
}
void WrDat(uchar dat)
{
uchar i;
ChkBusy();
PORTD =BIT(rs); //rs=1
PORTD &=BIT(rw); //rw=0BIT(en); //en=0
PORTD &=
PORTC=dat; //送数据
PORTD =BIT(en); //en=1
for(i=1;i;i++); //延时
PORTD &=BIT(en); //en=0BIT(rs); //RS=0
}
void ChkBusy()
{
DDRC=0X00; //设置为输入
PORTC=0X00; //不设置上拉电阻
PORTD &=
PORTD =BIT(rw); //RW=1
PORTD =BIT(en); //EN=1
while(PINC & 0x80); //送数据
PORTD &=~BIT(en); //en=0
DDRC=0xff; //设置为输出
}
void delayms(uint n)
{
uchar j;
uint i;
for(i=0;i<n;i++)
for(j=0;j<250;j++);
}