AVR单片机学习的点点滴滴,下来很大决心才开始学AVR单片机,在网络上找了很多书籍、例程,也购买了前段时间比较热门的书籍《深入浅出AVR单片机》,也许是自己天生不如人吧,还是不得要领。只能自己厚着薄脸请教同事。方得点点。<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
根据其意。就是要程序模块化设计,将一个大的程序按功能分割成一些小模块, 特点: 各模块相对独立,功能单一,结构清晰,接口简单 控制了程序设计的复杂性 提高元件的可靠性 缩短开发周期 避免程序开发的重复劳动 易于维护和功能扩充 开发方法: 自上向下,逐步分解。
以MEGA32、ICCV7 for AVR为工具,并把学习的过程记录下来。MEGA32是一款比较常用的AVR单片机。
例子一: 建立总模板
打开ICCV7 for AVR,新建工程。在工程里添加main.c,main.h两个总工程文件。其中main.c为所有程序汇总。main.h汇总总工程的头文件。在.h头文件中加入#ifndef __systen_h和 #define __systen_h是为了避免重复编译。
如下:
///////////////////////////////////////////////////////////
Main.c
/////////////////////////////////////////////////////////////
#include "main.h" //包含总头文件
void main(void)
{
init_devices(); //AVR硬件初始化
while (1) //循环
{
led_light();
led_disp( );
}
}
////////////////////////////////////////////////////////////////
main.h
/////////////////////////////////////////////////////////////////
#ifndef __systen_h
#define __systen_h
/*********************************************/
#define M8 1
#define M16 2
#define M32 3
#define M64 4
#define M128 5
/*********************************************/
#define CPU_TYPE M32
//定义MCU时钟频率
//#define F_CPU 14745600
#define F_CPU 7372800
//**************************************************
//包含系统头文件,请根据实际需要进行裁减
//**************************************************
//#pragma REGPARMS
#if CPU_TYPE == M128
#include <iom128v.h>
#endif
#if CPU_TYPE == M64
#include <iom64v.h>
#endif
#if CPU_TYPE == M32
#include <iom32v.h>
#endif
#if CPU_TYPE == M16
#include <iom16v.h>
#endif
#if CPU_TYPE == M8
#include <iom8v.h>
#endif
//#include <intrins.h>
//#include <absacc.h>
//#include <string.h>
//#include <FLOAT.H>
//#include <math.h>
//#include <stdlib.h>
#include <macros.h>
//#include <eeprom.h>
//#define const code
//**************************************************
//系统数据类型定义
//**************************************************
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef NULL
#define NULL 0
#endif
#define MIN(a,b) ((a<b)?(a)b))
#define MAX(a,b) ((a>b)?(a)b))
#define ABS(x) ((x>0)?(x)-x))
typedef unsigned char uint8; /* 定义可移植的无符号8位整数关键字 */
typedef signed char int8; /* 定义可移植的有符号8位整数关键字 */
typedef unsigned int uint16; /* 定义可移植的无符号16位整数关键字 */
typedef signed int int16; /* 定义可移植的有符号16位整数关键字 */
typedef unsigned long uint32; /* 定义可移植的无符号32位整数关键字 */
typedef signed long int32; /* 定义可移植的有符号32位整数关键字 */
//**************************************************
//包含工程头文件,请根据需要进行裁减
//**************************************************
#include "devices.h"
#include "led.h"
//**************************************************
//一下为工程变量、端口定义
//**************************************************
/********************************/
/* "以下为工程配置" */
/********************************/
#endif
/**********************end****************************************/
例子二 软件延时
建立软件延时delay.c,delay.h并加到工程中。
///////////////////////////////////////////////////////////////////////////////////////
delay.c
///////////////////////////////////////////////////////////////////////////////////////
#include "main.h"
//1us
extern void delay_us(uint8 u)
{
uint8 i;
for (i=177;u!=0;u--)
{
while (i--);
}
}
//1ms
extern void delay_ms(uint<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />16 m)
{
for (;m!=0;m--)
{
delay_us(10);
}
}
//1s
extern void delay_s(uint16 s)
{
s=s*40;
for (;s!=0;s--)
{
delay_ms(100);
}
}
////////////////////////////////////////////////////////////////////////////////////////////
delay.h
/////////////////////////////////////////////////////////////////////////////////////////////
#ifndef __delay_h
#define __delay_h
//1us
extern void delay_us(uint8 u);
//1ms
extern void delay_ms(uint16 m);
//1s
extern void delay_s(uint16 s);
#endif
例子三 7段共阳数码管的显示
7段共阳数码管的显示例子是学习AVR单片机的端口操作。
AVR端口是真正的双向端口,不像51伪双向。这也是AVR的一项优势,只是操作时大家注意DDRn就可以了。真正双向端口在模拟时序方面不如伪双向的方便。
DDRn PORTn PINn 解释:n为端口号:ABCDE
DDRn:控制端口是输入还是输出,0为输入,1为输出。个人记忆方法:一比零大所以往外挤,即1为输出,0为输入。
PORTn:从引脚输出信号,当DDRn为1时,可以通过PORTn=x等端口操作语句给引脚输出赋值。
PINn:从引脚读输入信号,无论DDRn为何值,都可以通过x=PINn获得端口n的外部电平。
当引脚配置为输入时,若PORTxn 为"1“,上拉电阻将使能。内部上拉电阻的使用在键盘扫描的时候还要说到。
端口更详细功能及介绍以及端口第二功能请参考数据手册。
端口引脚配置
DDxn PORTxn PUD (in SFIOR) I/O 上拉电阻说明
0 0 X 输入 No 高阻态 (Hi-Z)
0 1 0 输入 Yes被外部电路拉低时将输出电流
0 1 1 输入 No高阻态(Hi-Z)
1 0 X 输出 No输出低电平 ( 漏电流)
1 1 X 输出 No输出高电平 ( 源电流)
如果有引脚未被使用,建议给这些引脚赋予一个确定电平。最简单的保证未用引脚具有确定电平的方法是使能内部上拉电阻。但要注意的是复位时上拉电阻将被禁用。如果复位时的功耗也有严格要求则建议使用外部上拉或下拉电阻。不推荐直接将未用引脚与VCC 或GND 连接,因为这样可能会在引脚偶然作为输出时出现冲击电流。
下面我们来看例子:
void port_init(void)
{
PORTA = 0x03;
DDRA = 0x03;
PORTB = 0x00;
DDRB = 0x01;
PORTC = 0x00;
DDRC = 0x00;
PORTD = 0x00;
DDRD = 0x00;// 建议赋值为零
}
PORTA = 0x03;DDRA = 0x03;这两句使PA口的PA1和PA0处于输出状态,PA7—PA2处于输入状态,因为先定义了PORTA=0x30,PA1和PA0的内部上拉电阻也使能了。这里的0x03即二进制的00000011,从左到右对应于Pn7--Pn0八个IO口。
通过跑马灯程序来深入理解IO口的操作:
//ICC-AVR application builder : 2006-11-21 9:20:57
// Target : M32
// Crystal: 7.3728Mhz
#include
#include
void _delay(unsigned char n) //延时函数定义
{
unsigned char i,j;
for(;n!=0;n--) //n*10ms
{
for(j=100;j!=0;j--) //100us*100=10ms
{
for(i=147;i!=0;i--) //delay 100us
;
}
}
}
int main(void)
{
unsigned char i,j,k; //
PORTA=0xFF; //PA口设为输出高电平,灯灭
DDRA=0xFF; //PA?ú?aê?3??£ê?
while(1)
{
i="1";
for (j=0;j<8;j++) //循环8次,即PA0~~PA7轮流闪亮
{
PORTA=~i; //反相输出,低电平有效,对应的灯亮
for (k=0;k<10;k++) _delay(100); //延时 100*10=1秒,可自行调节 i="i"<<1; //左移一位,I的值将向下面的列表那样变化
// 0b00000001 PA0
// 0b00000010 PA1
// 0b00000100 PA2
// 0b00001000 PA3
// 0b00010000 PA4
// 0b00100000 PA5
// 0b01000000 PA6
// 0b10000000 PA7
}
}
}
其他IO口操作指令:
void main(void)
{
PORTA=0xff;
DDRA=0xff; //输出 模式 ,IO口上拉电阻有效,1为输出,0为输入。
PORTA=0xf0; //等
以下三条指令只对操作符号右边的数字位是一的位操作。
PORTA&=~0xf0; //清零 0xf0为 01110000 ,即把654三位清零,其余数位不变。
PORTA|=0x77; //置一 0xf0为 01110111 ,即把654210六位清零,其余数位不变。
PORTA^=0x70; //翻转 如果是零变成1,是一变成0。
(P & 0x80)==0x80; //按位与 判断p的第七位是否是一,是则成立
}
关于1< ADIF是一个寄存器变量,可以堪称数字4, 跟手册中的定义,包含芯片头文件的定义是一样的。
(1< ADCSR=(1< ADCSR|=(1< ADCSR&=~(1< while(ADCSR&(1<
while(1)
{
while(ADCSR&(1< {
程序......
}
例子:
建立led.c,led.h并添加到工程:
//////////////////////////////////////////////////////////////////////
led.C
///////////////////////////////////////////////////////////////////////
#include "main.h"
/*0,1,2,3,4,5,6,7,8,9,A,b,C,d,E,F,*/
extern const uint8 led_seg[]=
{
0xC0,//0
0xF9,//1
0xA4,//2
0xB0,//3
0x99,//4
0x92,//5
0x82,//6
0xF8,//7
0x80,//8
0x90,//9
0x88,//a
0x83,//b
0xC6,//c
0xA1,//d
0x86,//e
0x8E,//f
};
void led_light()
{
uint8 i;
for (i=0;i<=7;i++)
{
led_port^=BIT(i);//输出低电平
delay_ms(200);
}
for (i=0;i<=7;i++)
{
led_port^=BIT(i);//输出低电平
delay_ms(200);
}
}
void led_disp(void)
{
uint8 i;
for (i=0;i<=16;i++)
{
led_disp_port=led_seg;//输出低电平
delay_ms(200);
}
}
//////////////////////////////////////////////////////////////////////
led.h
///////////////////////////////////////////////////////////////////////
#ifndef __led_h
#define __led_h
#define led_port PORTA
#define led_ddr DDRA
#define led_pin PINA
#define led_disp_port PORTB
extern void led_light(void);
extern void led_disp(void);
#endif
其中使用单片机的PB口为数码管数据口
1、使用“数码管计算”小软件自动生成16进制(0~9的数组)如下:
16进制: led_disp[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,};
2、把数组定义到FLASH中,使用const unsinged char led_disp[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,};
https://static.assets-stash.eet-china.com/album/old-resources/2009/6/4/6ecbc6d3-a458-4012-9914-52da01b2a9e7.rar
文章评论(0条评论)
登录后参与讨论