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、在编译和调试的过程中,由进一步加强了自己编程和改错的能力,通过研究这方面的
课题,可以更深刻地理解交通信号灯的整个过程,同时也加强了嵌入式学习的技巧。由
于时间的关系,只能做到这些功能,我知道想要实现功能更强大的交通信号灯,还有大
量的工作要做,希望以后还可以进一步接触此类设计。