基於ATmega8的測速計設計

finhaz發表於2020-10-03

這是曾經本科生時期的製作,一個用atmega8a晶片做的測速計。當時選用atmega8a的原因很簡單,學生時代做東西,都是撿到啥晶片就用啥,只要效能還能過得去就可以了。這款微控制器效能一般,但是比at89c51強多了(最主要是剩下的多),就是燒寫程式時候需要配置熔絲位,一時間不太習慣。整個工程是用iar for avr寫的,因為那時候一直用msp430,習慣了iar的介面。
首先是選標頭檔案,iom8.h定義了相關的暫存器,裝好iar就能用了。

//這裡是iar裡面的
#include <iom8.h>
#include<intrinsics.h>
//後面是自己的
#include"bianliangshengming.h"
#include"hanshushengming.h"
#include"hanshudingyi.h"
#include"lcd.h"
#include"int.h"

主函式就這麼寫了,主要是一堆初始化,最後一個while(1),主要程式都是在中斷裡面,因為檢測只有在外界觸發了感測器才需要進行。

void main(void)
{
	init_devices();
	timer1_init();
	lcd();
	timer0_init();
	while(count2<10000)
	{
		if(anjian==0)
		{
			DelayNms(3);
			if(anjian==0);
			{
				key=1;
				while(anjian==0);
				break;
			}
		}
	}
	count2=0;
	TCCR0=0x00;
	lcd1(); 
	timer0_init();
	while(count2<30000)
	{
	}
	TCCR0=0x00;
	
	TCCR1B=0X00;
	led0=0;
	led1=0;
	count2=0;
	timer2_init();
	if(!key)//選擇模式,當時有兩個檢測模式,演算法不太一樣,具體不記得了
	{     
		LcdWriteCom(0x01);
		LCD_write_string(0,1,"MODE1 Running.....");
	}
	else
	{    
		LcdWriteCom(0x01);
		LCD_write_string(0,1,"MODE2 Running....."); 
	}
	timer0_init();
	while(count2<5000)
	{
	}
	TCCR0=0x00;
	int_init();
	// DelayNms(5000);
	timer1_init();
	while(1)
	{
		__no_operation();
	}
}

一系列的外設初始化寫在另一個檔案,用了1602作為顯示,這些初始化程式碼專門寫一個檔案。

#ifndef __LCD_H_
#define __LCD_H_
/**********************************
當使用的是4位資料傳輸的時候定義,
使用8位取消這個定義
**********************************/
#define LCD1602_4PINS 1

/**********************************
包含標頭檔案
**********************************/

//---重定義關鍵詞---//
#ifndef uchar
#define uchar unsigned char
#endif

#ifndef uint 
#define uint unsigned int
#endif

/**********************************
PIN口定義
**********************************/
#define LCD1602_DATAPINS PORTC
#define LCD1602_E   PORTC_Bit4
#define LCD1602_RW PORTD_Bit5
#define LCD1602_RS PORTB_Bit0

				  

#endif
/*void Lcd1602_Delay1ms(unsigned int ms)
{
  unsigned int i; 
  for(;ms>0;ms--)
    for(i=0;i<2*4*143;i++);
}*/


/*******************************************************************************
* 函 數 名         : LcdWriteCom
* 函式功能		   : 向LCD寫入一個位元組的命令
* 輸    入         : com
* 輸    出         : 無
*******************************************************************************/
#ifndef 	LCD1602_4PINS	 //當沒有定義這個LCD1602_4PINS時
void LcdWriteCom(uchar com)	  //寫入命令
{
	LCD1602_E = 0;     //使能
	LCD1602_RS = 0;	   //選擇傳送命令
	//LCD1602_RW = 0;	   //選擇寫入
	
	LCD1602_DATAPINS = com;     //放入命令
	DelayNms(1);		//等待資料穩定

	LCD1602_E = 1;	          //寫入時序
	DelayNms(5);	  //保持時間
	LCD1602_E = 0;
}
#else 
void LcdWriteCom(uchar com)	  //寫入命令
{
	LCD1602_E = 0;	 //使能清零
	LCD1602_RS = 0;	 //選擇寫入命令
	//LCD1602_RW = 0;	 //選擇寫入

	LCD1602_DATAPINS = com>>4;	//由於4位的接線是接到P0口的低四位,所以傳送高四位用改
	DelayNms(1);

	LCD1602_E = 1;	 //寫入時序
	DelayNms(5);
	LCD1602_E = 0;

//	Lcd1602_Delay1ms(1);
	LCD1602_DATAPINS = com ; //傳送低四位
	DelayNms(1);

	LCD1602_E = 1;	 //寫入時序
	DelayNms(5);
	LCD1602_E = 0;
}
#endif
/*******************************************************************************
* 函 數 名         : LcdWriteData
* 函式功能		   : 向LCD寫入一個位元組的資料
* 輸    入         : dat
* 輸    出         : 無
*******************************************************************************/		   
#ifndef 	LCD1602_4PINS		   
void LcdWriteData(uchar dat)			//寫入資料
{
	LCD1602_E = 0;	//使能清零
	LCD1602_RS = 1;	//選擇輸入資料
	LCD1602_RW = 0;	//選擇寫入

	LCD1602_DATAPINS = dat; //寫入資料
	DelayNms(1);

	LCD1602_E = 1;   //寫入時序
	DelayNms(5);   //保持時間
	LCD1602_E = 0;
}
#else
void LcdWriteData(uchar dat)			//寫入資料
{
	LCD1602_E = 0;	  //使能清零
	LCD1602_RS = 1;	  //選擇寫入資料
	//LCD1602_RW = 0;	  //選擇寫入

	LCD1602_DATAPINS = dat>>4;	//由於4位的接線是接到P0口的低四位,所以傳送高四位用改
	DelayNms(1);

	LCD1602_E = 1;	  //寫入時序
	DelayNms(5);
	LCD1602_E = 0;

	LCD1602_DATAPINS = dat ; //寫入低四位
	DelayNms(1);

	LCD1602_E = 1;	  //寫入時序
	DelayNms(5);
	LCD1602_E = 0;
}
#endif
/*******************************************************************************
* 函 數 名       : LcdInit()
* 函式功能		 : 初始化LCD屏
* 輸    入       : 無
* 輸    出       : 無
*******************************************************************************/		   
#ifndef		LCD1602_4PINS
void LcdInit()						  //LCD初始化子程式
{
 	LcdWriteCom(0x38);  //開顯示
	LcdWriteCom(0x0c);  //開顯示不顯示游標
	LcdWriteCom(0x06);  //寫一個指標加1
	LcdWriteCom(0x01);  //清屏
	LcdWriteCom(0x80);  //設定資料指標起點
}
#else
void LcdInit()						  //LCD初始化子程式
{
	LcdWriteCom(0x32);	 //將8位匯流排轉為4位匯流排
	LcdWriteCom(0x28);	 //在四位線下的初始化
	LcdWriteCom(0x0c);  //開顯示不顯示游標
	LcdWriteCom(0x06);  //寫一個指標加1
	LcdWriteCom(0x01);  //清屏
	LcdWriteCom(0x80);  //設定資料指標起點
}
#endif



void LCD_set_xy( unsigned char x, unsigned char y ) //寫地址函式
{
    unsigned char address;
    if (y == 0) address = 0x80 + x; // 最高位是1,另七位才是地址 故有+0x80
    else if(y==1)  address = 0xc0 + x;
   else if(y==2)  address=0x80+x+0x14;
    else address=0xc0+x+0x14;
    LcdWriteCom( address);
}

void LCD_write_string(unsigned char X,unsigned char Y,unsigned char *s) //列x=0~15,行y=0,1
{
    LCD_set_xy( X, Y ); //寫地址    
    while (*s) // 寫顯示字元
    {
      LcdWriteData( *s );
      s ++;
    }
      
}




void LCD_write_mode(unsigned char X,unsigned char Y,unsigned long int count)
{
  uchar D[7];
  count/=100;
  D[6]=0;
  D[5]='s';
  D[4]=count%10+48;              
  D[2]=(count%100)/10+48;        
  D[1]=(count%1000)/100+48;      
  D[0]=(count%10000)/1000+48;
  D[3]='.';
  LCD_write_string(X,Y,D);
}

相關的定時器等配置如下

#include<intrinsics.h>
void lcd(void)
{
  DelayNms(50);
  LCD_write_string(0,0,"10s choose mode");
}
void lcd1(void)
{
  DelayNms(50);
  LCD_write_string(0,0,"Speedtest will start");
 // DelayNms(20);
  LCD_write_string(0,1," after 30 seconds ");
  //DelayNms(2000);
}

/**************延時Nms*************************/	
void DelayNms(unsigned int ms)
{
  unsigned int i; 
  for(;ms>0;ms--)
    for(i=0;i<4*143;i++);
}

//定時器T0初始化,計時0.5ms
void timer0_init(void)
{
 TCCR0  = 0x00;//停止定時器
 TCNT0  = 0x83;//初始值
 TIMSK |= 0x01;//中斷允許
 TCCR0  = 0x03;//啟動定時器
}

//外中斷初始化
/*void int_init(void)
{
 MCUCR |= 0x0A;//int0 int1 下降沿中斷
 MCUCSR|= 0x00;
 PORTD|=(1<<PD2)|(1<<PD3);
 GICR  |= 0xC0;//使能int_0 int_1中斷
}
*/
void int_init(void)
{
 MCUCR |= 0x02;//int0 下降沿中斷
 MCUCSR|= 0x00;
 PORTD|=(1<<PD2);
 GICR  |= 0x40;//使能int_0 中斷
}
void init_devices(void)
{
 __disable_interrupt(); //禁止所有中斷
 MCUCR  = 0x00;
 MCUCSR = 0x80;//禁止JTAG
 GICR   = 0x00;
 Portinit();
 LcdInit();
 __enable_interrupt();
}//開全域性中斷


void Portinit(void)
{
  DDRC=0XFF;
  DDRB=0XFF;
  PORTC=0XFF;
  DDRD=0Xff;
  PORTD=0XFF;
  
}
void timer1_init(void)
{
	DDRB=0XFF;
        TCCR1A=0XF2;
        TCCR1B=0X19;
	OCR1A= 0x32;//匹配A值
	OCR1B = 0x32;//匹配B值
	ICR1 = 0x64;//輸入捕捉匹配值
}

void timer2_init(void)
{
	TCCR2  = 0x00;//停止定時器
	ASSR   = 0x00;//非同步時鐘模式
	TCNT2  = 0x83;//初始值
	TIMSK |= 0x40;//中斷允許
	TCCR2  = 0x03;//啟動定時器
        
}






void end(void)
{
    TCCR0  = 0x00;
    LcdWriteCom(0x01);
    GICR_Bit6=0;
}

中斷程式用來計時

#pragma vector=INT0_vect 
__interrupt void INT0_Server(void)//  感測器停止計時
{
  if(key){
  if(!s)
  {  
    TCNT0 = 0x83;   
    count2=count1;
    s=1;
  }
  else
  { 
   end();
   unsigned long int count3;
   count3=count1+5*count1-5*count2;
   LCD_write_string(0,0,"T1:");
   LCD_write_mode(5,0,count2);
   LCD_write_string(0,1,"T2:");
   LCD_write_mode(5,1,count1);
   LCD_write_string(0,2,"Time:");
   LCD_write_mode(0,3,count3);
  }
  }
  else
  {
    end();
    LCD_write_string(0,0,"Time:");
    LCD_write_mode(0,1,count1);
  }
}


#pragma vector=TIMER0_OVF_vect
__interrupt void TIMER0_OVF_Server(void)
{
  TCNT0 = 0x83;
  count2+=2;
}

#pragma vector=TIMER2_OVF_vect
__interrupt void TIMER2_OVF_Server(void)
{
  TCNT2 = 0x83;
  count1+=1;
}

這個功能還是很簡單的。

相關文章