AVR C语言的应用.docx
《AVR C语言的应用.docx》由会员分享,可在线阅读,更多相关《AVR C语言的应用.docx(25页珍藏版)》请在冰豆网上搜索。
![AVR C语言的应用.docx](https://file1.bdocx.com/fileroot1/2022-11/23/fa98bcec-9fe0-4963-91a5-cfac9817ec33/fa98bcec-9fe0-4963-91a5-cfac9817ec331.gif)
AVRC语言的应用
第九章AVRC语言的应用
★更详细资料参阅光盘文件<>
9.1AVR–支持C和高级语言编程的结构
高级语言
•提高了MCU的重要性
–上市的时间
–简化维护工作
–轻便
–学习时间
–可重用性
–库
•潜在的缺点
–代码大小
–执行速度
为什么AVR适宜用高级语言编程?
因为它是为高级语言而设计的!
IAR对AVR结构和指令集的影响
•在结构/指令集确定之前,编译器的开发就开始了
•潜在的瓶颈得到确认并消除
•IAR的反馈在硬件设计上得到了反映
•几次循环反复
•修改后的结果从代码当中可看出来
Memorysystem
•32通用寄存器
–数量多
–直接与ALU连接
–可保存变量,指针和之间结果
•线性程序存储空间
–1KBytes-8MBytes
–无需页寻址
–常数区(SPM可修改)
•线性数据存储空间
–16MBytes
–无需页寻址
类似于C的寻址模式
C源代码
unsignedchar*var1,*var2;
*var1++=*--var2;
产生的代码
LDR16,-X
STZ+,R16
带偏移量的间接寻址
•有效访问数组和结构
•Auto(localvariables)放置于软件堆栈之中
–为适应重入的要求,高级语言都基于堆栈结构
四种指针
16和32位支持
•加法指令
–加和减
–寄存器之间
–寄存器和立即数之间
–Zero标志的传播
SUBR16,R24SUBIR16,1
SBCR17,R25SBCIR17,0
所有的跳转都基于最后结果
两个16位数相减
Non-destructivecomparison
CPR16,R24
CPCR17,R25
CPCR18,R26
CPCR19,R27
•带进位比较
•Zero传播
•无需保存结果
•可使用所有形式的跳转
Switch支持
•Switches在CASE语句中经常遇到
•Straightforwardapproach效率低
•间接跳转适合于紧凑的switch结构
•switch由通用库管理
摘要
•AVR结构从一开始就是针对高级语言设计的
•Atmel与IAR在结构和指令调整上的合作
•从而编译器可以产生高效的代码
EfficientC-codingforAVR
减少代码的提示和诀窍
汇编(Assembly)与C比较
汇编:
•可以完全控制资源
•在小应用当中可以产生紧凑的、高速的代码
•在大的应用当中代码效率低
•可读性差(Crypticcode)
•不好维护
•不易移植(Non-portable)
C:
•对资源的控制有限
•在小应中产生的代码量大,执行速度慢
•在大的应用当中代码效率高
•结构化的代码
•容易维护
•容易移植
访问I/O
•读I/O:
temp=PIND;
INR16,LOW(16)
•写I/O:
TCCR0=0x4F;
LDIR16,79
OUTLOW(51),R16
•I/O的位设置与清除
•地址小于0x1F的I/O:
PORTB|=(1<SBILOW(24),LOW
(2)
ADCSR&=~(1<CBILOW(6),LOW(7)
•地址高于0x1F的I/O:
TCCR0&=~(0x80);
INR16,LOW(51)
ANDIR16,LOW(127)
OUTLOW(51),R16
测试I/O的单个位
•等待地址低于0x1F的单个位的清除
while(PIND&(1<SBICLOW(16),LOW(6)
RJMP?
0002
•等待地址高于0x1F的单个位的设置
while(!
(TIFR&(1<INR16,LOW(56)
SBRSR16,LOW(0)
RJMP?
0004
16位变量
•总是使用最小的数据类型
•8位计数器:
charcount8=5;
do{
}while(--count8);
LDIR16,5
DECR16
BRNE?
0004
•Total6bytes
•16位计数器:
intcount16=5;
do{
}while(--count16);
LDIR24,LOW(5)
LDIR25,0
SBIWR24,LWRD
(1)
BRNE?
0004
Total8Bytes
全局和局部变量
•全局变量
–在startup初始化
–存储于SRAM
–必须加载到寄存器堆中
•局部变量
–在函数初期初始化
–存储于寄存器当中直至函数结束
全局变量和局部变量
•局部变量
voidmain(void)
{
charlocal;
local=local-34;
}
SUBIR17,LOW(34)
•Total2bytes
•全局变量
charglobal;
voidmain(void)
{
global=global-45;
}
LDSR16,LWRD(global)
SUBIR16,LOW(45)
STSLWRD(global),R16
Total10Bytes
高效地使用全局变量
•将全局变量收集到一个结构中:
typedefstruct{
intt_count;
charsec;//globalseconds
charmin;//globalminutes
}t;
ttime;
Voidmain(void)
{
t*temp=&time;
temp->sec++;temp->min++;temp->t_count++;
}
带参数的函数调用
•使用参数将数据传递到函数中去
charadd(charnumber1,charnumber2)
{
returnnumber1+number2;
}
函数间参数的传递通过R16-R23来实现
循环
•死循环
for(;;)
{
}
•循环
charcounter=100;
do{
}while(--counter);
预减变量(Pre-decrement)代码效率最高
优化代码的选项
•代码大小优化编译
•使用局部变量
•使用允许的最小数据类型
•将全局变量收集到结构中去
•死循环使用for(;;)
•使用预减的do{}while;
CAVR的程序设计
程序设计的程序设计
内容
•安装必须的工具
9.2C编译的介绍
•练习
边学边做
•用C编程
–设置编译和链接文件
–用C访问外围
–中断处理
–高级调试
•使用不同的AVR外围
–定时器/计数器
–UART
–外部中断
Toolflow
•器件:
AT90S8535
•CCompiler
•AVRstudio——仿真
•STK200——测试代码
•测试程序:
死循环
–读PortD的值(按键,输入口)
–将其值写到PortB(LED,输出口)
测试程序
#include/*定义AT90S8515*/
voidmain(void)
{
charc;
DDRB=0xFF;/*PortBalloutputs*/
for(;;)/*Eternalloop*/
{
c=PIND;/*ReadPortD*/
PORTB=c;/*回写到PortB*/
}
}
安装C编译器
•CompilerdeliveredonCD-ROM
•安装Dongle驱动器(见“DONGLE.TXT”)
设置C编译器
•启动IAREmbeddedWorkbench
可双击快捷图标
•创建新工程
–File->New->Project:
设置编译器选项
Project->Options
设置编译器选项
•设置链接器文件XLINK
设置编译器选项
•选择“Release”
•选择处理器配置及内存模式
设置C编译器
•设定“intel-extended”为输出格式(releaseonly)
设置C编译器
•将文件加入到工程-Project->Files...
设置C编译器
•设定“Debug”为当前配置
•“Make”theprogram:
–Project->Make
–PressF9
–Toolbutton
带调试信息的可执行文件在DEBUG\EXE目录
使用AVRStudio
•启动AVRStudio
•加载调试文件(TestProg.d90)-File->Open
•选择AT90S8515(只需在开始时选择一次)
•加入视图(VIEW)
–I/O(PinB,PortD)
–Processor
–Watch
»c
»PORTB
»PIND
•单步执行,TogglePINDbits
对器件编程
•选择编程窗口
•加载Intel-Hex文件(TestProg.hex)-File->Load
•对器件编程
–Program->Autoprogram
–F5
–Pushbutton
9.3测试应用程序
Main函数
•“main”是所有C程序的入口点
•不要加入参数,也不要返回值
•语法:
voidmain(void)
{
/*代码*/
}
访问外围
•所有I/O寄存器在头文件里都被定义为特殊功能寄存器
•象普通变量一样访问
#include/*定义8515*/
voidmain(void)
{
DDRD=0xFF;/*PortD输出*/
}
9.3.1读/写口
#include/*定义AT90S8515*/
voidmain(void)
{
charc;
DDRB=0xFF;/*PortB输出*/
for(;;)/*死循环*/
{
c=PIND;/*读PortD*/
PORTB=c;/*回写到PortB*/
}
}
9.3.2延时函数
#include/*定义8515*/
voiddelay(unsignedintdelayValue)
{
unsignedinti;
for(i=0;i;/*Donothing*/
}
9.3.2A:
延时函数
voidmain(void)
{
unsignedcharrunner=0x01;
DDRB=0xff;/*PortB输出*/
for(;;)/*死循环*/
{
if(runner)runner<<=1;
elserunner=0x01;
PORTB=runner;/*设置LED*/
delay(100);/*调用延时函数*/
}
}
9.3.3读/写E2PROM
/*利用IAR标准I/O函数来读/写E2PROM*/
#include
#include
voidmain(void)
{
chartemp=0;
_EEPUT(0x10,temp);/*写E2PROM地址:
0x10*/
_EEGET(temp,0x10);/*读E2PROM地址:
0x10*/
9.3.4AVR的PB口变速移位
/*文件名:
SLAVR934.ASM*/
/*位运算符:
~按位取反;<<左移;>>右移;&按位与;∣按位或;^按位异或;
;i++相当于i=i+1;i--相当于i=i-1*/
#include;/*器件配置文件*/
#defineBIT(x)(1<<(x));/*左移*/
voiddelay(void)
{
unsignedchari,j;
for(i=1;i;i++)
for(j=1;j;j++);
}
voidled_pb(void)
{
unsignedchari;
DDRB=0xff;/*设PB口输出*/
for(i=0;i<8;i++)/*硬件设定低电平灯亮,LED的1位亮灯从B口PB0→PB7*/
{
PORTB=~BIT(i);/*LED亮灯1位*/
delay();/*延时*/
}
}
voidmain(void)/*主函数*/
{
while
(1)/*循环*/
led_pb();
}
9.3.54个口LED亮灯变速移位
/*文件名:
SLAVR935.ASM*/
/*请修改程序,改变移位方向,2位或3位或一隔一亮灯移位等*/
#include/*预处理命令,头文件*/
#defineBIT(x)(1<<(x))?
*定义位函数,可修改移位方向*/
voiddelay(unsignedchart);/*延时函数*/
{
unsignedchari;
unsignedcharj;
for(i=0;ifor(j=1;j;j++);
}
voidled_pb(unsignedchart);/*LED移位函数*/
{
unsignedchari;
DDRB=0xff;/*设PB口为输出*/
for(i=0;i<8;i++)/*硬件设定低电平灯亮,LED的1位亮灯从B口PB0→PB7*/
{
PORTB=~BIT(i);/*LED亮灯1位*/
delay(t);/*延时*/
}
PORTB=0xff;/*关PB口*/
}
voidled_pd(unsignedchart);/*LED的1位亮灯从D口PD0→PD7移位函数*/
{
unsignedchari;
DDRD=0xff;
for(i=0;i<8;i++)/*LED的1位亮灯从D口PD0→PD7*/
{
PORTD=~BIT(i);
delay(t);
}
PORTD=0xff;
}
voidled_pc(unsignedchart);/*C口PC0→PC7移位函数*/
{
unsignedchari;
DDRC=0xff;
for(i=0;i<8;i++)/*LED的1位亮灯从C口PC0→PDC*/
{
PORTC=~BIT(i);
delay(t);
}
PORTC=0xff;
}
voidled_pa(unsignedchart);/*A口PA7→PA0移位函数*/
{
unsignedchari;
DDRA=0xff;
for(i=8;i>0;i--)/*LED的1位亮灯从A口PA7→PA0*/
{
PORTA=~BIT(i-1);
delay(t);
}
PORTA=0xff;
}
voidmain(void);/*主函数*/
{
unsignedchardt;
while
(1)/*循环*/
{
for(dt=5;dt<200;dt+=25)
{
led_pb(dt);/*LED发光二极管一亮灯沿四个口移位变速循环*/
led_pd(dt);
led_pc(dt);
led_pa(dt);
}
}
}
9.3.6音符声程序
/*源程序SLAVR936.ASM*/
/*可改变t函数,改变发音快慢*/
#include/*预处理命令*/
#defineucharunsignedchar
#defineuintunsignedint
voiddelay(uchart)
{
uchari,j;
for(i=0;ifor(j=1;j<150;j++);
}
voidsound_pc0(uchart)
{
uinti;
DDRC=0xff;
PORTC=0xff;
for(i=0;i<350-t*t;i++)/*改变发音快慢,另见SLAVR936B.ASM程序*/
{
PORTC^=(1<<0);
delay(t);
}
}
voidmain(void)/*主函数*/
{
uchardt;
for(;;)
for(dt=1;dt<14;dt++)/*改变发音数量*/
sound_pc0(dt);
}
9.3.78字循环移位显示程序
/*源程序SLAVR937.ASM*/
/*在SL-AVR开发实验器LED数码管上,8字符循环移位显示程序*/
#include/*器件配置文件*/
#defineucharunsignedchar/*定义缩写*/
#defineuintunsignedint
voiddelay(uintt)
{
uinti;
for(i=0;i}
voidinit_disp(void)/*B口,D口初始化*/
{
DDRB=0xff;
DDRD=0xff;
PORTB=0x7f;/*B口送8字符,字形可修改*/
}
voidscan(void)/*位选扫描*/
{
uchari,j;
for(i=0;i<6;i++)/*i++可修改为一位隔一位或隔2位显示或改变移位方向*/
{
j=150;/*可改变移位速度*/
do
{
PORTD=~(0x01<
delay(150);/*可改变LED显示亮度*/
PORTD=0xff;
delay(2100);/*可改变LED显示亮度*/
}
while(--j);
}
}
voidmain(void)/*主程序*/
{
init_disp();/*初始化*/
for(;;)
scan();/*位选扫描*/
}
9.3.8按键加1计数显示程序
/*在SL-AVR开发实验器上,用SHIFT键,按1次键加1计数显示程序*/
#include/*头文件*/
#defineucharunsignedchar/*缩写定义*/
#defineuintunsignedint
flashucharDATA_7SEG[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};/*LED字形表*/
ucharled[6];/*显示缓冲*/
uintcount;/*延时子程序*/
voiddelay(uintt)
{
uinti;
for(i=0;i}
voidinit_disp(void)/*初始化B口,D口*/
{
DDRB=0xff;
DDRD=0x7f;
PORTD|=0x80;
}
voiddisp(void)/*键盘显示*/
{
uchari;
for(i=0;i<6;i++)
{
PORTD=~(0x01<
PORTB=DATA_7SEG[led[i]];
delay(1000);
}
PORTB=0x00;
PORTD=0xff;
}
voidbe_pc0(void)/*发出一声响子程序*/
{
uinti;
DDRC|=0x01;
for(i=0;i<350;i++)
{
PORTC^=0x01;
delay(350);
}
}
voidconv(void)/*计数值转换成十进制数*/
{
led[5]=0;
led[4]=count/10000;
led[3]=count/1000%10;
led[2]=count/100%10;
led[1]=count/10%10;
led[0]=count%10;
}
voidmain(void)/*主程序*/
{
init_disp();/*初始化B口,D口*/
count=0;/*开始计数值是零*/
conv();/*转换*/
for(;;)
{
while((PIND&0x80)==0x80)/*没有键按下等待*/
disp();/*显示*/
be_pc0();/*发出一声响*/
count++;/*计数器加1*/
conv();/*转换成十进制数*/
while((PIND&0x80)==0)/*有键按下*/
disp();/*显示*/
}
}