交通信号灯控制

1实验原理:

   通过设计,培养自己综合运用所学知识、独立分析和解决实际问题的能力,培养创新意识和创新能力,并获得科学研究的基础训练,加深对ARM芯片的了解;熟悉ARM芯片各个引脚的功能,工作方式,计数/定时,I/O口,中断等相关原理,巩固学习嵌入式的相关内容知识。

利用ARM芯片模拟实现交通灯控制,自行选择所需ARM芯片,查阅相关文献资料,熟悉所选ARM芯片,了解所选ARM芯片各个引脚功能,工作方式,计数/定时,I/O口,中断等相关原理,通过软硬件设计实现利用ARM芯片完成交通灯的模拟控制。

2.实验设计思路

A.利用STM32F103芯片实现十字路交通灯的控制:

实现红、绿、黄灯的循环控制。使用红、黄、绿三种不同状态的灯实现此功能,主干道南北方方向的指示灯,编号为5,接在单片机的PB4的引脚上;东西方向的指示灯,编号为3,接在单片机的PA4的引脚上;

B.定时器功能

定时器功能主要就是用的Tim3定时器

C.按键中断

按键中断功能主要用的就是3个按键,一个控制交通灯是否可编辑,一个负责时间+1,一个负责时间-1

3.实验设计:

a.本设计先是从普通三色灯的指示开始进行设计,用P1,p7口作为输出。程序的初始化是东西方向红灯亮,南北方向的绿灯亮。

b.南北方向绿灯亮20秒后变为黄灯闪亮5秒,东西方向红灯亮25秒黄灯闪亮5秒后南北方向红灯亮,东西方向绿灯亮。

c.重复执行。

d.倒计时用到定时器T0,用P2口作为LED的显示。

e.用一模拟开关作为中断信号。实际中可以接其它可以产生中断信号的信号源。

f.在板子上的k键可以调绿灯黄灯时间,红灯就是绿灯黄灯的和,k3键按一次调节绿灯,按两次调节黄灯,按三次显示红灯时间,k1键加时间,k2键减时间,

4.实验步骤:

定时器控制原理:定时器对外设时钟Fpclk周期进行计数,根据4个匹配寄存器的设定可设置为匹配(即达到匹配寄存器指定的定时值)时产生中断或执行其他操作。

设置P0、P1口为GPIO输出状态,初始化定时器,选定定时器0中断为向量IRQ,对VICIntEnable、VICIntSelect、VICvectCntl进行设置,初始化SPI接口,根据设计要求编写软件程序。

根据事先画好的程序流程图,用C语言编写程序,在主程序中对需要用到的I/O口进行定义,并设置相应的I/O口,比如要求P1。18~P1。25引脚为GPIO功能,则通过对引脚功能选择 寄存器PINSEL1将对应的引脚设置为GPIO方式并设置GPIO方向,在GPIO方向寄存器IO1DIR里设置,之后对定时器0进行初始化,并开相应的中断。然后进入大循环进行倒计时显示、控制蜂鸣器的蜂鸣与否并判断flag是否加到设定值,对flag加到设定值后进行清零,让flag重新计数。中断服务程序的设计,每隔一秒钟定时器中断一次,每中断一次flag加1根据LED点亮的先后顺序以及点亮的时间,分别编写相应的程序。

5.总体设计框架:

用ARM系列芯片STM32F103作为系统的主控芯片,控制交通灯的循环点亮并显示灯亮时间(采用倒计时显示),当定时时间到的时候通过灯的状态来提醒人们注意红绿灯的状态。

6.ARM模拟交通灯控制程序流程图:

7.软件设计流程图:

8.实验代码

1.main.c

/******************************定时中断实现数字钟************************
* 数字钟
* 通过控制定时中断实现数字钟
******************************************************************/

#include "sys.h"
#include "delay.h"	
#include "led.h" 
#include "key.h"
#include "exti.h"	 	
#include "timer.h"

u8 second12 = 0, second67 =30; //红灯(南北) 与 绿色(东西)
u8 mode=0;
u8 flag=1,count=0;
u8 i;

//红绿蓝765

void EXTI0_IRQHandler(void)
{
	
	delay_ms(10);//消抖
	if(KEY3==0)	 //按键3
	{ 
		mode++;
	}		 
	EXTI->PR=1<<0;  //清除LINE0上的中断标志位  
}

//外部中断1服务程序
void EXTI1_IRQHandler(void)
{
	delay_ms(10);//消抖
	if(KEY2==0&&flag==0)	 //按键2
	{
		if(mode==1){
		   second12++;
			 second67--;
		}else if(mode==2){
			 second12--;
			 second67++;
		}
		
  		if(second12 > 29)
			{
				second12=0;
				count++;
			}
			
			if(second67 < 1)
			{
				second67=30;
			}
		
	}		 
	EXTI->PR=1<<1;  //清除LINE1上的中断标志位  
}


//外部中断2服务程序
void EXTI2_IRQHandler(void)
{
	delay_ms(10);//消抖
	if(KEY1==0&&flag==0)	 //按键1
	{
		if(mode==1){
		 second12--;
		 second67++;
		}else if(mode==2){
			second12++;
			second67--;
		}
		
			if(second12 < 1)
			{
				count++;
				second12=30;
			}
			
			if(second67 > 29)
			{
				second67=0;
			}
			
		
	}		 
	EXTI->PR=1<<2;  //清除LINE2上的中断标志位  
}


// 定时中断函数
void TIM3_IRQHandler( void )
{
    if( TIM3->SR & 0x0001) // 定时器溢出中断标志位
    {
        if(flag)
        {   
            second12++; 
            second67--; 
            if(second12 > 29)
            {
                second12 = 0;
                count++;

            }
            
            if(second67 < 1)
            {
                second67 = 30; 
            }
        }
    }
    TIM3->SR &= ~(1<<0); //清除定时器溢出中断标志位
}



void DisplayDigitalClock(void)//显示数字时钟
{
	
	//从左到右第1,2号数码管(表示南北秒数)一个横线表示通行
	SetLed(0, second12/10);
	delay_ms(1);
	SetLed(1, second12%10);
	delay_ms(1);



    
	//从左到右第6,7号数码管(表示东西秒数) 两个竖线表示通行
    

	SetLed(6, second67/10);
	delay_ms(1);
	SetLed(7, second67%10);
	delay_ms(1);
    
    if(count%2==0)
	{
        SetLed(5, 13);
		delay_ms(1);
        SetLed(2, 12);
        delay_ms(1);
	}else{
        SetLed(2, 13);
		delay_ms(1);
        SetLed(5, 12);
        delay_ms(1);
	}

    
	
	if(mode>2) {mode=0;flag=1;}
		
        switch (mode) {
            case 1:
                flag = 0;
                SetLed(3, 0);
                delay_ms(1);
                SetLed(4, 1);
                delay_ms(1);
                break;
            case 2:
                flag = 0;
                SetLed(3, 0);
                delay_ms(1);
                SetLed(4, 2);
                delay_ms(1);
                break;
        }
	for(i=0;i<10;i++)
	{
		LED_SEL=1;
		/*switch(count%2)
		{
			case 0: LedValue(129);delay_ms(1);break; //01显示红,67显示绿
			case 1: LedValue(66);delay_ms(1);break;  //01显示绿,67显示红
		}*/
		
		if(count%2==0)
		{
			 LedValue(129);

             delay_ms(1);
		}else{
			 LedValue(66);

             delay_ms(1);
		}
		
	}
	
}


/***************************主函数*****************************/
int main()
{
	Stm32_Clock_Init( 6 ); 
	delay_init( 72 );
	TimerxInit( 9999,7199 ); 10Khz的计数频率,计数到10000表示1s,10表示1ms
	LED_Init();
	EXTIX_Init();		 //外部中断初始化
	
  
	while(1)
	{
		LED_SEL = 0;
		DisplayDigitalClock();
	}	
}

2.delay.h

#include <stm32f10x_map.h>
#include <stm32f10x_nvic.h>
#include "delay.h"
 
static u8  fac_us=0;//us延时倍乘数
static u16 fac_ms=0;//ms延时倍乘数

//初始化延迟函数
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
void delay_init(u8 SYSCLK)
{
	SysTick->CTRL&=0xfffffffb;//bit2清空,选择外部时钟  HCLK/8
	fac_us=SYSCLK/8;		    
	fac_ms=(u16)fac_us*1000;
}	

//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864 
void delay_ms(u16 nms)
{	 		  	  
	u32 temp;		   
	SysTick->LOAD=(u32)nms*fac_ms;//时间加载(SysTick->LOAD为24bit)
	SysTick->VAL =0x00;           //清空计数器
	SysTick->CTRL=0x01 ;          //开始倒数  
	do
	{
		temp=SysTick->CTRL;
	}
	while(temp&0x01&&!(temp&(1<<16)));//等待时间到达   
	SysTick->CTRL=0x00;       //关闭计数器
	SysTick->VAL =0X00;       //清空计数器	  	    
}

//延时nus
//nus为要延时的us数.		    								   
void delay_us(u32 nus)
{		
	u32 temp;	    	 
	SysTick->LOAD=nus*fac_us; //时间加载	  		 
	SysTick->VAL=0x00;        //清空计数器
	SysTick->CTRL=0x01 ;      //开始倒数 	 
	do
	{
		temp=SysTick->CTRL;
	}
	while(temp&0x01&&!(temp&(1<<16)));//等待时间到达   
	SysTick->CTRL=0x00;       //关闭计数器
	SysTick->VAL =0X00;       //清空计数器	 
}

3.sys.h

#include "sys.h"


//设置向量表偏移地址
//NVIC_VectTab:基址
//Offset:偏移量
//CHECK OK
//091207
void MY_NVIC_SetVectorTable(u32 NVIC_VectTab, u32 Offset)	 
{ 
  	//检查参数合法性
	assert_param(IS_NVIC_VECTTAB(NVIC_VectTab));
	assert_param(IS_NVIC_OFFSET(Offset));  	 
	SCB->VTOR = NVIC_VectTab|(Offset & (u32)0x1FFFFF80);//设置NVIC的向量表偏移寄存器
	//用于标识向量表是在CODE区还是在RAM区
}

//设置NVIC分组
//NVIC_Group:NVIC分组 0~4 总共5组 
//CHECK OK
//091209
void MY_NVIC_PriorityGroupConfig(u8 NVIC_Group)	 
{ 
	u32 temp,temp1;	  
	temp1=(~NVIC_Group)&0x07;//取后三位
	temp1<<=8;
	temp=SCB->AIRCR;  //读取先前的设置
	temp&=0X0000F8FF; //清空先前分组
	temp|=0X05FA0000; //写入钥匙
	temp|=temp1;	   
	SCB->AIRCR=temp;  //设置分组	    	  				   
}

//设置NVIC 
//NVIC_PreemptionPriority:抢占优先级
//NVIC_SubPriority       :响应优先级
//NVIC_Channel           :中断编号
//NVIC_Group             :中断分组 0~4
//注意优先级不能超过设定的组的范围!否则会有意想不到的错误
//组划分:
//组0:0位抢占优先级,4位响应优先级
//组1:1位抢占优先级,3位响应优先级
//组2:2位抢占优先级,2位响应优先级
//组3:3位抢占优先级,1位响应优先级
//组4:4位抢占优先级,0位响应优先级
//NVIC_SubPriority和NVIC_PreemptionPriority的原则是,数值越小,越优先
//CHECK OK
//100329
void MY_NVIC_Init(u8 NVIC_PreemptionPriority,u8 NVIC_SubPriority,u8 NVIC_Channel,u8 NVIC_Group)	 
{ 
	u32 temp;	
	u8 IPRADDR=NVIC_Channel/4;  //每组只能存4个,得到组地址 
	u8 IPROFFSET=NVIC_Channel%4;//在组内的偏移
	IPROFFSET=IPROFFSET*8+4;    //得到偏移的确切位置
	MY_NVIC_PriorityGroupConfig(NVIC_Group);//设置分组
	temp=NVIC_PreemptionPriority<<(4-NVIC_Group);	  
	temp|=NVIC_SubPriority&(0x0f>>NVIC_Group);
	temp&=0xf;//取低四位

	if(NVIC_Channel<32)NVIC->ISER[0]|=1<<NVIC_Channel;//使能中断位(要清除的话,相反操作就OK)
	else NVIC->ISER[1]|=1<<(NVIC_Channel-32);    
	NVIC->IPR[IPRADDR]|=temp<<IPROFFSET;//设置响应优先级和抢断优先级   	    	  				   
}

//外部中断配置函数
//只针对GPIOA~G;不包括PVD,RTC和USB唤醒这三个
//参数:GPIOx:0~6,代表GPIOA~G;BITx:需要使能的位;TRIM:触发模式,1,下升沿;2,上降沿;3,任意电平触发
//该函数一次只能配置1个IO口,多个IO口,需多次调用
//该函数会自动开启对应中断,以及屏蔽线   
//待测试...
void Ex_NVIC_Config(u8 GPIOx,u8 BITx,u8 TRIM) 
{
	u8 EXTADDR;
	u8 EXTOFFSET;
	EXTADDR=BITx/4;//得到中断寄存器组的编号
	EXTOFFSET=(BITx%4)*4;

	RCC->APB2ENR|=0x01;//使能io复用时钟

	AFIO->EXTICR[EXTADDR]&=~(0x000F<<EXTOFFSET);//清除原来设置!!!
	AFIO->EXTICR[EXTADDR]|=GPIOx<<EXTOFFSET;//EXTI.BITx映射到GPIOx.BITx
	
	//自动设置
	EXTI->IMR|=1<<BITx;//  开启line BITx上的中断
	//EXTI->EMR|=1<<BITx;//不屏蔽line BITx上的事件 (如果不屏蔽这句,在硬件上是可以的,但是在软件仿真的时候无法进入中断!)
 	if(TRIM&0x01)EXTI->FTSR|=1<<BITx;//line BITx上事件下降沿触发
	if(TRIM&0x02)EXTI->RTSR|=1<<BITx;//line BITx上事件上升降沿触发
} 


//不能在这里执行所有外设复位!否则至少引起串口不工作.		    
//把所有时钟寄存器复位
//CHECK OK
//091209
void MYRCC_DeInit(void)
{										  					   
	RCC->APB1RSTR = 0x00000000;//复位结束			 
	RCC->APB2RSTR = 0x00000000; 
	  
  	RCC->AHBENR = 0x00000014;  //睡眠模式闪存和SRAM时钟使能.其他关闭.	  
  	RCC->APB2ENR = 0x00000000; //外设时钟关闭.			   
  	RCC->APB1ENR = 0x00000000;   
	RCC->CR |= 0x00000001;     //使能内部高速时钟HSION	 															 
	RCC->CFGR &= 0xF8FF0000;   //复位SW[1:0],HPRE[3:0],PPRE1[2:0],PPRE2[2:0],ADCPRE[1:0],MCO[2:0]					 
	RCC->CR &= 0xFEF6FFFF;     //复位HSEON,CSSON,PLLON
	RCC->CR &= 0xFFFBFFFF;     //复位HSEBYP	   	  
	RCC->CFGR &= 0xFF80FFFF;   //复位PLLSRC, PLLXTPRE, PLLMUL[3:0] and USBPRE 
	RCC->CIR = 0x00000000;     //关闭所有中断
	//配置向量表				  
#ifdef  VECT_TAB_RAM
	MY_NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
#else   
	MY_NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
#endif
}

//THUMB指令不支持汇编内联
//采用如下方法实现执行汇编指令WFI
//CHECK OK
//091209
__asm void WFI_SET(void)
{
	WFI;    
}

//进入待机模式	 
//check ok 
//091202
void Sys_Standby(void)
{
	SCB->SCR|=1<<2;//使能SLEEPDEEP位 (SYS->CTRL)	   
  	RCC->APB1ENR|=1<<28;     //使能电源时钟	    
 	PWR->CSR|=1<<8;          //设置WKUP用于唤醒
	PWR->CR|=1<<2;           //清除Wake-up 标志
	PWR->CR|=1<<1;           //PDDS置位		  
	WFI_SET();				 //执行WFI指令		 
}	 

//后备寄存器写入操作
//reg:寄存器编号
//reg:要写入的数值 
check ok
091202
//void BKP_Write(u8 reg,u16 dat)
//{
//  RCC->APB1ENR|=1<<28;     //使能电源时钟	    
//	RCC->APB1ENR|=1<<27;     //使能备份时钟	    
//	PWR->CR|=1<<8;           //取消备份区写保护 
//	switch(reg)
//	{
//		case 1:
//			BKP->DR1=dat;
//			break;
//		case 2:
//			BKP->DR2=dat;
//			break;
//		case 3:
//			BKP->DR3=dat;
//			break; 
//		case 4:
//			BKP->DR4=dat;
//			break;
//		case 5:
//			BKP->DR5=dat;
//			break;
//		case 6:
//			BKP->DR6=dat;
//			break;
//		case 7:
//			BKP->DR7=dat;
//			break;
//		case 8:
//			BKP->DR8=dat;
//			break;
//		case 9:
//			BKP->DR9=dat;
//			break;
//		case 10:
//			BKP->DR10=dat;
//			break;
//	} 
//}	    
//系统软复位
//CHECK OK
//091209
void Sys_Soft_Reset(void)
{   
	SCB->AIRCR =0X05FA0000|(u32)0x04;	  
} 

//JTAG模式设置,用于设置JTAG的模式
//mode:jtag,swd模式设置;00,全使能;01,使能SWD;10,全关闭;
//CHECK OK	
//100818		  
void JTAG_Set(u8 mode)
{
	u32 temp;
	temp=mode;
	temp<<=25;
	RCC->APB2ENR|=1<<0;     //开启辅助时钟	   
	AFIO->MAPR&=0XF8FFFFFF; //清除MAPR的[26:24]
	AFIO->MAPR|=temp;       //设置jtag模式
} 

//系统时钟初始化函数
//pll:选择的倍频数,从2开始,最大值为16	
//CHECK OK
//091209
void Stm32_Clock_Init(u8 PLL)
{
	unsigned char temp=0;   
	MYRCC_DeInit();		  //复位并配置向量表
	RCC->CR|=0x00010000;  //外部高速时钟使能HSEON
	while(!(RCC->CR>>17));//等待外部时钟就绪
	RCC->CFGR=0X00000400; //APB1=DIV2;APB2=DIV1;AHB=DIV1;
	PLL-=2;//抵消2个单位
	RCC->CFGR|=PLL<<18;   //设置PLL值 2~16
	RCC->CFGR|=1<<16;	  //PLLSRC ON 
	FLASH->ACR|=0x32;	  //FLASH 2个延时周期

	RCC->CR|=0x01000000;  //PLLON
	while(!(RCC->CR>>25));//等待PLL锁定
	RCC->CFGR|=0x00000002;//PLL作为系统时钟	 
	while(temp!=0x02)     //等待PLL作为系统时钟设置成功
	{   
		temp=RCC->CFGR>>2;
		temp&=0x03;
	}    
}		    

4.usart.h

#include "sys.h"
#include "usart.h"

//********************************************************************************
//支持适应不同频率下的串口波特率设置.
//加入了对printf的支持
//增加了串口接收命令功能.
//修正了printf第一个字符丢失的bug
// 	  
 

//
//加入以下代码,支持printf函数,而不需要选择use MicroLIB	  
#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE 
{ 
	int handle; 
	/* Whatever you require here. If the only file you are using is */ 
	/* standard output using printf() for debugging, no file handling */ 
	/* is required. */ 
}; 
/* FILE is typedef’ d in stdio.h. */ 
FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
_sys_exit(int x) 
{ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{      
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
	USART1->DR = (u8) ch;      
	return ch;
}
#endif 
//end
//

#ifdef EN_USART1_RX   //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误   	
u8 USART_RX_BUF[64];     //接收缓冲,最大64个字节.
//接收状态
//bit7,接收完成标志
//bit6,接收到0x0d
//bit5~0,接收到的有效字节数目
u8 USART_RX_STA=0;       //接收状态标记	  
  
void USART1_IRQHandler(void)
{
	u8 res;	    
	if(USART1->SR&(1<<5))//接收到数据
	{	 
		res=USART1->DR; 
		if((USART_RX_STA&0x80)==0)//接收未完成
		{
			if(USART_RX_STA&0x40)//接收到了0x0d
			{
				if(res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
				else USART_RX_STA|=0x80;	//接收完成了 
			}else //还没收到0X0D
			{	
				if(res==0x0d)USART_RX_STA|=0x40;
				else
				{
					USART_RX_BUF[USART_RX_STA&0X3F]=res;
					USART_RX_STA++;
					if(USART_RX_STA>63)USART_RX_STA=0;//接收数据错误,重新开始接收	  
				}		 
			}
		}  		 									     
	}  											 
} 
#endif										 
//初始化IO 串口1
//pclk2:PCLK2时钟频率(Mhz)
//bound:波特率
//CHECK OK
//091209
void uart_init(u32 pclk2,u32 bound)
{  	 
	float temp;
	u16 mantissa;
	u16 fraction;	   
	temp=(float)(pclk2*1000000)/(bound*16);//得到USARTDIV
	mantissa=temp;				 //得到整数部分
	fraction=(temp-mantissa)*16; //得到小数部分	 
    mantissa<<=4;
	mantissa+=fraction; 
	RCC->APB2ENR|=1<<2;   //使能PORTA口时钟  
	RCC->APB2ENR|=1<<14;  //使能串口时钟 
	GPIOA->CRH&=0XFFFFF00F; 
	GPIOA->CRH|=0X000008B0;//IO状态设置
		  
	RCC->APB2RSTR|=1<<14;   //复位串口1
	RCC->APB2RSTR&=~(1<<14);//停止复位	   	   
	//波特率设置
 	USART1->BRR=mantissa; // 波特率设置	 
	USART1->CR1|=0X200C;  //1位停止,无校验位.
#ifdef EN_USART1_RX		  //如果使能了接收
	//使能接收中断
	USART1->CR1|=1<<8;    //PE中断使能
	USART1->CR1|=1<<5;    //接收缓冲区非空中断使能	    	
	MY_NVIC_Init(3,3,USART1_IRQChannel,2);//组2,最低优先级 
#endif
}

5.exit.c

#include "exti.h"
#include "led.h"
#include "key.h"
#include "delay.h"
#include "timer.h"
//	 
//外部中断 驱动代码			   
// 	  

//外部中断0服务程序
/*
void EXTI0_IRQHandler(void)
{
	
	delay_ms(10);//消抖
	if(KEY3==0)	 //按键3
	{ 
		
		
	}		 
	EXTI->PR=1<<0;  //清除LINE0上的中断标志位  
}

//外部中断1服务程序
void EXTI1_IRQHandler(void)
{
	delay_ms(10);//消抖
	if(KEY2==0)	 //按键2
	{
		LED_SEL = 0;
		LED5=!LED5;
	}		 
	EXTI->PR=1<<1;  //清除LINE1上的中断标志位  
}

//外部中断2服务程序
void EXTI2_IRQHandler(void)
{
	delay_ms(10);//消抖
	if(KEY1==0)	 //按键1
	{
		LED_SEL = 0;
		LED0=!LED0;
	}		 
	EXTI->PR=1<<2;  //清除LINE2上的中断标志位  
}
*/

//外部中断初始化程序
//初始化PC0-2为中断输入.
void EXTIX_Init(void)
{
	RCC->APB2ENR|=1<<4;     //使能PORTC时钟
	GPIOC->CRL&=0XFFFFF000;//PC0-2设置成输入	  
	GPIOC->CRL|=0X00000888;
	
	Ex_NVIC_Config(GPIO_C,0,FTIR);//下降沿触发
	Ex_NVIC_Config(GPIO_C,1,FTIR);//下降沿触发
	Ex_NVIC_Config(GPIO_C,2,FTIR);//下降沿触发

	MY_NVIC_Init(2,2,EXTI0_IRQChannel,2);//抢占2,子优先级2,组2
	MY_NVIC_Init(2,1,EXTI1_IRQChannel,2);//抢占2,子优先级1,组2	   
	MY_NVIC_Init(2,0,EXTI2_IRQChannel,2);//抢占2,子优先级1,组2
}

6.key.c

#include "key.h"
#include "delay.h"
//按键输入 驱动代码		   
//PC0.1.2 设置成输入
void KEY_Init(void)
{
	RCC->APB2ENR|=1<<4;     //使能PORTC时钟
	GPIOC->CRL&=0XFFFFF000;//PC0-2设置成输入	  
	GPIOC->CRL|=0X00000888;   
} 
//按键处理函数
//返回按键值
//0,没有任何按键按下
//1,KEY1按下
//2,KEY2按下
//3,KEY3按下
//注意此函数有响应优先级,KEY1>KEY2>KEY3!!
u8 KEY_Scan(void)
{	 
	static u8 key_up=1;//按键按松开标志
	
	if(key_up && (KEY1==0 || KEY2==0 || KEY3==0))
	{
		delay_ms(10);//去抖动 
		key_up=0;
		if(KEY1==0)
		{
			return 1;
		}
		else if(KEY2==0)
		{
			return 2;
		}
		else if(KEY3==0)
		{
			return 3;
		}
	}
	else if(KEY1==1 && KEY2==1 && KEY3==1)
		key_up=1;
	
	return 0;// 无按键按下
}

 7.led.c

/****************LED灯有关实现函数********************
* 
******************************************************************/

#include "led.h"

/***************************数码管段选***************************/
u8 segTable[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x40,0x39,0x09,0x36};
u8 segTablePortation[] = {0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};

/****************************************************
* 初始化二极管LED灯的引脚端口
******************************************************/
void LED_Init()
{
	RCC->APB2ENR|=1<<0;	//使能AFIO
	RCC->APB2ENR|=1<<3;  //使能PORTB时钟
	RCC->APB2ENR|=1<<6;	//使能PORTE时钟	 
	
	AFIO->MAPR |= 0x02000000; //复用口设置	设置PB.3为I/O口可用,且可以SW仿真
	   	 
	GPIOB->CRL &= 0xFFFF0000;
	GPIOB->CRL |= 0x00003333; //PB.3推挽输出
	GPIOB->ODR |= 0x000000FF; //PB.3输出高
											  
	GPIOE->CRH&=0X00000000;
	GPIOE->CRH|=0X33333333; //PE.8-15推挽输出
	GPIOE->ODR|=0x0000FF00; //PE.8-15输出高 	
}

/***************************************
* 流水灯选择,或数码管段选
* value:显示的数值对应的段选二进制值
****************************************/
void LedValue(u8 value)
{
	GPIOE->ODR &= ~(0xff<<8);
	GPIOE->ODR |= value<<8;
// 	  LED0 = (value&0x01)?1:0;
// 		LED1 = (value&0x02)?1:0;
// 		LED2 = (value&0x04)?1:0;
// 		LED3 = (value&0x08)?1:0;
// 		LED4 = (value&0x10)?1:0;
// 		LED5 = (value&0x20)?1:0;
// 		LED6 = (value&0x40)?1:0;
// 		LED7 = (value&0x80)?1:0;	
}

/***************************************
* 数码管显示不带小数点的数值
* 参数 w:显示的位置,即位选,左-右:0-7
*      value:要显示的数值
****************************************/
void SetLed(u8 w, u8 value)
{
	SEL0 = w%2;
	SEL1 = w/2%2;
	SEL2 = w/4;
	LedValue(segTable[value]);
}

/***************************************
* 数码管显示带小数点的数值
* 参数 w:显示的位置,即位选,左-右:0-7
*      value:要显示的数值
****************************************/
void PortationDisplay(u8 w, u8 value)
{
	SEL0 = w%2;
	SEL1 = w/2%2;
	SEL2 = w/4;
	LedValue( segTablePortation[value] );
}

8.timer.c

#include "timer.h"

//数字钟的时,分、秒
//u8 hour = 0, minute = 0, second = 0;


/****************普通按键初始化函数********************
* 通用定时器中断初始化
* 这里时钟选择为APB1的2倍,而APB1为36M
* arr:自动重装值。
* psc:时钟预分频数
* 这里使用的是定时器3!
******************************************************/
void TimerxInit(u16 arr, u16 psc)
{
	RCC->APB1ENR |= 1<<1; //TIM3时钟使能
	TIM3->ARR = arr; //设定计数器自动重装值,10为1ms
	TIM3->PSC = psc; //预分频器7200,得到10KHZ的计数时钟
	
	TIM3->DIER |= 1<<0; //允许更新中断
	TIM3->CR1 |= 0x01; //使能定时器3
	
	MY_NVIC_Init(1, 3, TIM3_IRQChannel, 2); //抢占1,子优先级3,组2
}

/****************定时器3的中断函数********************
* 定时器3的中断函数
* 每次中断,second加一
******************************************************/

/*
void TIM3_IRQHandler( void )
{
	if( TIM3->SR & 0x0001) //溢出中断
	{
		second12++;
		second67--;
		if(second12 > 59)
		{
			second12=0;
		}
		
		if(second67 < 1)
		{
			second67=60;
		}
	}
	TIM3->SR &= ~(1<<0); //清除中断标志位
}
*/

/*****************************************************
* 数字钟显示函数
******************************************************/

/*
void DisplayDigitalClock(void)
{
	
	switch(mode)
	{
		case 0: SetLed(3, 0); delay_ms(1); SetLed(4, 0); delay_ms(1);break; 
		case 1: SetLed(3, 0); delay_ms(1); SetLed(4, 1); delay_ms(1);break;
		case 2: SetLed(3, 0); delay_ms(1); SetLed(4, 2); delay_ms(1);break;
		case 3: SetLed(3, 0); delay_ms(1); SetLed(4, 3); delay_ms(1);break; //退出
	}
	
	//从左到右第1,2号数码管(表示南北秒数)
	SetLed(0, second12/10);
	delay_ms(1);
	SetLed(1, second12%10);
	delay_ms(1);
	
	
	//从左到右第6,7号数码管(表示东西秒数)
	SetLed(6, second67/10);
	delay_ms(1);
	SetLed(7, second67%10);
	delay_ms(1);
}
*/

 

9.实验结果与分析:

东西绿灯,南北红灯

绿灯结束,3秒黄灯

转换,南北绿灯,东西红灯

中断控制,绿灯设置时间

黄灯设置

红灯时间为绿灯+黄灯时间

 

10.设计心得体会及总结

1、因为设计中所用的一些工具,平时虽然接触过,但研究不够深入,所以此次课程设计

在选好题目之后对设计所需资料进行了系统查询和整理,其中包括了我们学习过程中用

到的课本和网上搜索的期刊文献等;

2、此次嵌入式实验是比较全面的一个实验,所涉及的内容十分广泛,包括底层硬件结构

以及上层操作系统、驱动程序等方面,在实验过程中虽然遇到了各种各样的问题,但解

决问题的同时也锻炼了我们分析问题的能力以及动手能力,做完后感觉获益匪浅;

3、回顾此次ARM课程设计,我感慨很多,在这几星期里,可以说苦多于甜,但是确实

又学到了很多东西,不仅巩固了以前所学的知识,而且学到了很多书本上没有的新的知

识。在设计调试的过程中遇到了各种各样的问题,同时也发现了自己的不足之处;

4、在编译和调试的过程中,由进一步加强了自己编程和改错的能力,通过研究这方面的

课题,可以更深刻地理解交通信号灯的整个过程,同时也加强了嵌入式学习的技巧。由

于时间的关系,只能做到这些功能,我知道想要实现功能更强大的交通信号灯,还有大

量的工作要做,希望以后还可以进一步接触此类设计。

  • 31
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值