⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sleep_main.c

📁 令AVR ATMEGA16进入睡眠状态及唤醒的例程(转贴)
💻 C
字号:
/***********************************************
****      AVR  电源管理_睡眠模式范例            ***
****  	                                     ***
**** 作者:  HJJourAVR                        ***
**** 编译器:WINAVR20050214                   ***
****                                         ***
****  	www.OurAVR.com	 2005.9.26           ***
***********************************************/
/*
本程序简单的示范了如何令AVR ATMEGA16进入睡眠状态及唤醒
		电源管理及睡眠模式的介绍
		进入最低耗电的掉电模式
		关闭各种模块
		外部中断唤醒

 M16掉电模式的耗电情况(看门狗关闭),时钟为内部RC 1MHz
     0.9uA@Vcc=5.0V  [手册的图表约为1.1uA]
     0.3uA@Vcc=3.3V  [手册的图表约为0.4uA]
     //测量的数字万用表是FLUKE 15B,分辨率0.1uA

这个程序需要MCU进入休眠状态,为实现最低功耗,JTAG接口会被关闭,只能通过LED的变化来观察程序的运行。
这个实验里面,用STK500(AVRISP) ISP下载线来烧录更方便。

熔丝位设置 
1 关断BOD功能 BODEN=1 
2 如果用ISP方式烧录,就可以完全关闭JTAG口了  OCEEN=1,JTAGEN=1  

*/

#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <avr/delay.h>
//时钟定为内部RC 1MHz,F_CPU=1000000 也可以采用其他时钟
#include <avr/sleep.h>
/*
sleep.h里面定义的常数,对应各种睡眠模式
#define SLEEP_MODE_IDLE         0
空闲模式
#define SLEEP_MODE_ADC          _BV(SM0)
ADC 噪声抑制模式
#define SLEEP_MODE_PWR_DOWN     _BV(SM1)
掉电模式
#define SLEEP_MODE_PWR_SAVE     (_BV(SM0) | _BV(SM1))
省电模式
#define SLEEP_MODE_STANDBY      (_BV(SM1) | _BV(SM2))
Standby 模式
#define SLEEP_MODE_EXT_STANDBY  (_BV(SM0) | _BV(SM1) | _BV(SM2))
扩展Standby模式

函数
void set_sleep_mode (uint8_t mode);
设定睡眠模式
void sleep_mode (void);
进入睡眠状态
*/

//管脚定义
#define LED			0   //PB0 驱动LED,低电平有效
#define KEY_INT2	0   //PB3 按键,   低电平有效

void delay_10ms(unsigned int t)
{
/*
	由于内部函数_delay_ms() 最高延时较短
 	262.144mS@1MHz / 32.768ms@8MHz / 16.384ms@16MHz
	故编写了这条函数,实现更长的延时,并能令程序能适应各种时钟频率
*/
	while(t--)
		_delay_ms(10);
}

int main(void)
{
	unsigned char i;
    //上电默认DDRx=0x00,PORTx=0x00 输入,无上拉电阻
    PORTA=0xFF;								//不用的管脚使能内部上拉电阻。
    PORTC=0xFF;
    PORTD=0xFF;

    PORTB=0xFF;
    DDRB =(1<<LED);							//PB0设为输出高电平,灯灭   
    /*
    端口引脚
    进入休眠模式时,所有的端口引脚都应该配置为只消耗最小的功耗。
    最重要的是避免驱动电阻性负载。
    在休眠模式下I/O 时钟clkI/O 和ADC 时钟clkADC 都被停止了,输入缓冲器也禁止了,从而保证输入电路不会消耗电流。
    在某些情况下输入逻辑是使能的,用来检测唤醒条件。用于此功能的具体引脚请参见“ 数字输入使能和休眠模式” 。
     如果输入缓冲器是使能的,此时输入不能悬空,信号电平也不应该接近VCC/2,否则输入缓冲器会消耗额外的电流。
     
     IO作输出(DDR=1)时,维持状态不变
  	*/
  	
	/*
	看门狗定时器(上电默认是关闭的)
	如果系统无需利用看门狗,这个模块也可以关闭。
	若使能,则在任何休眠模式下都持续工作,从而消耗电流。
	在深层次的睡眠模式下,这个电流将占总电流的很大比重。
	
	假设看门狗定时器使能了,关闭程式如下
		1. 在同一个指令内对WDTOE 和WDE 写"1“,即使WDE 已经为"1“
		2. 在紧接的4 个时钟周期之内对WDE 写"0”
	*/	
	WDTCR=(1<<WDTOE)|(1<<WDE);
	WDTCR=(0<<WDE);
	//或使用wdt.h里面的wdt_disable()函数

  	/*
  	模数转换器(上电默认是关闭的)
  	使能时, ADC在睡眠模式下继续工作。
  	为了降低功耗,在进入睡眠模式之前需要禁止ADC。
  	重新启动后的第一次转换为扩展的转换。
  	
	假设模数转换器使能了,关闭程式如下  	
  	*/
  	 ADCSRA=(0<<ADEN); 
    /*
    模拟比较器(上电默认是打开的,需要手工关闭)
    在空闲模式时,如果没有使用模拟比较器,可以将其关闭。在ADC 噪声抑制模式下也是如此。
    在其他睡眠模式模拟比较器是自动关闭的。
    如果模拟比较器使用了内部电压基准源,则不论在什么睡眠模式下都需要关闭它。否则内部电压基准源将一直使能。
    
	关闭程式如下  	
	*/
	ACSR=(1<<ACD);
	/*
	掉电检测BOD (由熔丝位BODEN控制)
	如果系统没有利用掉电检测器BOD,这个模块也可以关闭。
	如果熔丝位BODEN 被编程,从而使能了BOD 功能,它将在各种休眠模式下继续工作。
	在深层次的休眠模式下,这个电流将占总电流的很大比重。
	
	设置熔丝位BODEN=1 关断BOD功能
	*/
	
	/*
	片内基准电压
	使用BOD、模拟比较器和ADC 时可能需要内部电压基准源。
	若这些模块都禁止了,则基准源也可以禁止。
	重新使能后用户必须等待基准源稳定之后才可以使用它。
	如果基准源在休眠过程中是使能的,其输出立即可以使用。
	
	当BOD、模拟比较器和ADC都禁止了,则基准源也自动禁止了。
	*/
	
	/*
	JTAG 接口与片上调试系统
	如果通过熔丝位OCDEN使能了片上调试系统,当芯片进入掉电或省电模式时主时钟保持运行。
	在休眠模式中这个电流占总电流的很大比重。
	下面有三种替代方法:
	1 不编程OCDEN
    2 不编程JTAGEN
    3 置位MCUCSR 的JTD
	当JTAG 接口使能而JTAG TAP 控制器没有进行数据交换时,引脚TDO 将悬空。
	如果与TDO 引脚连接的硬件电路没有上拉电阻,功耗将增加。
	器件的引脚TDI 包含一个上拉电阻,因此在扫描链中无需为下一个芯片的TDO 引脚设置上拉电阻。
	通过置位MCUCSR寄存器的JTD 或不对JTAG 熔丝位编程可以禁止JTAG 接口。
	
	JTD: 禁止JTAG 接口(MCU控制与状态寄存器MCUCSR Bit7)
	此位为0 时,如果JTAGEN熔丝位被编程则JTAG 接口使能。
	如果这位为1, JTAG接口禁止。
	为了避免无意的禁止或使能JTAG接口,必须通过一个时间序列来改变JTD 位。
		应用软件必须在四个时钟周期内将期望的数值两次写入JTD。
	如果JTAG 接口没有与其他JTAG电路连接, JTD应该置位。这样做的原因是为了避免JTAG接口TDO引脚的静态电流。
	
	在软件中关闭JTAG接口的方法     
	*/
	MCUCSR=(1<<JTD);
	MCUCSR=(1<<JTD);	
	
	/*
	掉电模式
	当SM2..0 为010 时, SLEEP 指令将使MCU 进入掉电模式。
	在此模式下,外部晶体停振,而外部中断、两线接口地址匹配及看门狗(如果使能的话)继续工作。
	只有外部复位、看门狗复位、BOD 复位、两线接口地址匹配中断、外部电平中断INT0 或INT1,或外部中断INT2 可以使MCU 脱离掉电模式。
	这个睡眠模式停止了所有的时钟,只有异步模块可以继续工作。
	当使用外部电平中断方式将MCU 从掉电模式唤醒时,必须保持外部电平一定的时间。
	从施加掉电唤醒条件到真正唤醒有一个延迟时间,此时间用于时钟重新启动并稳定下来。
	唤醒周期与由熔丝位CKSEL 定义的复位周期是一样的。
	
	如果在睡眠过程中发生了复位,则MCU 唤醒后从中断向量开始执行
	使能的中断可以将进入睡眠模式的MCU 唤醒,	经过启动时间,外加4个时钟周期后,MCU就可以运行中断例程了。然后返回到SLEEP 的下一条指令。
	*/
	MCUCSR=(0<<ISC2);		//INT2 的下降沿激活中断(默认的,这句话可以不写)
	GICR=(1<<INT2); 		//使能外部中断INT2
	sei();					//使能全局中断
	while(1)
	{
		for (i=0;i<10;i++)  //LED闪动10次后进入掉电模式的睡眠状态
		{
			delay_10ms(30);
			PORTB&=~(1<<LED);   //点亮LED
			delay_10ms(30);			
			PORTB|=(1<<LED);	//熄灭LED
		}
		
		set_sleep_mode(SLEEP_MODE_PWR_DOWN);	//设定为掉电模式
		sleep_mode();							//进入睡眠状态
		/*
		也可以自行编写
		MCUCR=(0<<SM2)|(1<<SM1)|(0<<SM0);   	//设定为掉电模式
		asm volatile(“sleep” : : );				//进入睡眠状态
		*/		
	}
}


SIGNAL(SIG_INTERRUPT2)  //外部中断2服务程序 唤醒源
{
	PORTB&=~(1<<LED);   //点亮LED
	delay_10ms(500);
	PORTB|=(1<<LED);	//熄灭LED
	delay_10ms(100);

	/*LED长亮5秒钟,熄灭1秒钟后,退出中断服务程序,然后返回到SLEEP 的下一条指令*/	
}


/*
   程序运行效果
     万用表打到直流电流的最小档位(uA分辨率),接到开关的两头
     烧录后要把STK500拔出,否则无法测得正确的电流数据。
   
     上电后LED闪动10次后进入掉电模式的睡眠状态
		 此时可断开开关
		 看看万用表的读数
		 然后接通开关
     按下 INT2按键,将会发现LED长亮5秒钟,熄灭1秒钟后,退回主程序,LED闪动10次后进入掉电模式的睡眠状态
     如果按下复位按键,马上复位。

     网友可以编写其他睡眠模式/开关各种模块/其他时钟源/其他电源电压来测试电流消耗情况
*/


/*
电源管理及睡眠模式
	睡眠模式可以使应用程序关闭MCU中没有使用的模块,从而降低功耗。
	AVR 具有不同的睡眠模式,允许用户根据自己的应用要求实施剪裁。
	进入睡眠模式的条件是置位寄存器MCUCR的SE,然后执行SLEEP 指令。
	具体哪一种模式( 空闲模式、ADC 噪声抑制模式、掉电模式、省电模式、Standby 模式和扩展Standby模式) 由MCUCR 的SM2、SM1 和SM0 决定。
	使能的中断可以将进入睡眠模式的MCU 唤醒。
	经过启动时间,外加4个时钟周期后,MCU就可以运行中断例程了。然后返回到SLEEP 的下一条指令。
	唤醒时不会改变寄存器文件和SRAM的内容。
	如果在睡眠过程中发生了复位,则MCU 唤醒后从中断向量开始执行
	
	需要了解AVR芯片内部不同的时钟系统及其分布,在选择合适的睡眠模式时非常有用。
	
MCU控制寄存器-MCUCR 
	MCU控制寄存器包含了电源管理的控制位。
	  	Bits 7, 5, 4 – SM2..0: 休眠模式选择位 2、1 和0
		这些位用于选择具体的休眠模式。
		SM2 SM1 SM0 休眠模式
		 0   0   0  空闲模式
		 0   0   1  ADC 噪声抑制模式
		 0   1   0  掉电模式
		 0   1   1  省电模式
		 1   0   0  保留
		 1   0   1  保留
		 1   1   0  Standby模式(1) 
		 1   1   1  扩展Standby模式(1) 
		Note:1 仅在使用外部晶体或谐振器时Standby 模式与扩展Standby 模式才可用。
		
	  	Bit 6 – SE: 休眠使能
		为了使MCU 在执行SLEEP 指令后进入休眠模式, SE必须置位。
		为了确保进入休眠模式是程序员的有意行为,建议仅在SLEEP 指令的前一条指令置位SE。
		MCU 一旦唤醒立即清除SE。

关于各种睡眠模式的特点与唤醒要求,内容繁多,请参考数据手册
*/

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -