/****************************************************************************
* 文件名称:main.c
* 功 能:3路抢答器
* 说 明:KEY1为开始/停止,KEY2为正确,KEY3为错误,KEY4-KEY6为选手抢答按钮
数码管显示抢答者编号,音乐1为正确结果音乐,音乐2为错误
****************************************************************************/
#include "config.h"
#define HC595_CS (1 << 29) // P0.29口为74HC595的片选
#define LEDS (0xFF << 18) // P1[18:25]控制LED1-LED8,低电平点亮
#define BEEP (1 << 7) // P0.7 控制蜂鸣器
#define KEY2 (1 << 17) // P0.17 连接KEY2
#define KEY3 (1 << 18) // P0.18 连接KEY3
#define KEY4 (1 << 19) // P0.19 连接KEY4
#define KEY5 (1 << 20) // P0.20 连接KEY5
#define KEY6 (1 << 21) // P0.21 连接KEY6
/* 全局变量 */
int start = 0; // 抢答开始标志,0表示停止,1表示开始
int press = 0; // 按下抢答键标志,0表示未抢答,1表示已抢答
int num = 0; // 抢答队伍编号
int countDown = 30; // 倒计时设置时间
/* 此表为LED0~9的字模 */
uint8 const DISP_TAB[] = {
// 0 1 2 3 4 5 6 7 8 9 P
0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8, 0x80,0x90,0x8C};
/* 已抢答LED 灯花样,低电平点亮 */
const uint32 LED_TBL[] = {
0x00, 0xFF, // 全部熄灭后,再全部点亮
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, // 依次逐个点亮
0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF, // 依次逐个叠加
0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, // 依次逐个递减
0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81, // 两个靠拢后分开
0x81, 0xC3, 0xE7, 0xFF, 0xFF, 0xE7, 0xC3, 0x81 // 从两边叠加后递减
};
/* 定时器开始LED 灯花样,低电平点亮 */
const uint32 LED_TIMER[] = {
0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, // 依次逐个叠加
0x0F, 0x07, 0x03, 0x01, 0x00, // 依次逐个递减
};
/****************************************************************************
* 名 称:DelayNS()
* 功 能:长软件延时
* 入口参数:dly---延时参数,值越大,延时越久,单位为s
* 出口参数:无
****************************************************************************/
void DelayNS(uint32 dly)
{ uint32 i;
for(; dly>0; dly--)
for(i=0; i<500000; i++);
}
/****************************************************************************
* 名 称:IRQ_EINT0()
* 功 能:外中断服务程序。
* 入口参数:无
* 出口参数:无
****************************************************************************/
void __irq IRQ_Eint0(void)
{
start = (start + 1) % 2; // 开始标志模2加1
if(start == 1)
T0TCR = 0x01; // 启动定时器
press = 0; // 未有人抢答
num = 5; // 队伍编号未赋值
countDown = 30; // 倒计时设置为30秒
IO0CLR = BEEP; // 打开BEEP
DelayNS(1);
IO0SET = BEEP; // 关闭BEEP
while((EXTINT & 0x01) != 0)
EXTINT = 0x01; // 清除EINT0中断标志
VICVectAddr = 0x00; // 通知VIC 中断处理结束
}
/******************************************************************************
** 函数名称:Eint_Init()
** 函数功能:初始化外中断。
** 入口参数:无
** 出口参数:无
******************************************************************************/
void Eint_Init(void)
{
EXTMODE = 0x00; // 设置EINT0 为电平触发
EXTPOLAR = 0x00; // 极性寄存器使用默认值0
/* 打开EINT0 中断(使用向量中断) */
VICVectCntl0 = 0x20 | 0x0e; // 分配外部中断0 到向量中断0
VICVectAddr0 = (uint32)IRQ_Eint0; // 设置中断服务程序地址
EXTINT = 0x01; // 清除EINT0 中断标志
VICIntEnable = 1 << 0x0e; // 使能EINT0 中断
}
/******************************************************************************
** 函数名称:DisplayLED()
** 函数功能:流水灯显示。
** 入口参数:无
** 出口参数:无
******************************************************************************/
void DisplayLED(void)
{
uint32 i;
if(start == 0)
{
for(i=10; i<26; i++) // 逐个点亮,逐个熄灭
{
IO1SET = ~((LED_TBL[i]) << 18);
DelayNS(1);
IO1CLR = ((LED_TBL[i]) << 18);
DelayNS(1);
}
}
else
{
for(i=0; i<42; i++) // 花式灯
{
IO1SET = ~((LED_TBL[i]) << 18);
DelayNS(1);
IO1CLR = ((LED_TBL[i]) << 18);
DelayNS(1);
}
}
IO1SET = (0xFF << 18); // 熄灭所有灯
}
/******************************************************************************
** 函数名称:TwinkleLED()
** 函数功能:LED灯闪烁。
** 入口参数:无
** 出口参数:无
******************************************************************************/
void TwinkleLED(void)
{
uint32 i;
for(i=0; i<5; i++)
{
IO1SET = ~((0x00) << 18);
DelayNS(2);
IO1CLR = ((0x00) << 18); // 全部熄灭
DelayNS(2);
IO1SET = ~((0xFF) << 18);
DelayNS(2);
IO1CLR = ((0xFF) << 18); // 全部点亮
DelayNS(2);
}
IO1SET = (0xFF << 18); // 熄灭所有灯
}
/******************************************************************************
** 函数名称:MSPI_Init()
** 函数功能:初始化SPI 接口,设置为主机。
** 入口参数:无
** 出口参数:无
******************************************************************************/
void MSPI_Init(void)
{
PINSEL0 = PINSEL0 & (~(0xFF << 8)) | (0x15 << 8); // 设置管脚连接SPI
SPCCR = 0x52; // 设置SPI 时钟分频
SPCR = (0 << 3) | // CPHA = 0, 数据在SCK 的第一个时钟沿采样
(1 << 4) | // CPOL = 1, SCK 为低有效
(1 << 5) | // MSTR = 1, SPI 处于主模式
(0 << 6) | // LSBF = 0, SPI 数据传输MSB (位7)在先
(0 << 7); // SPIE = 0, SPI 中断被禁止
}
/******************************************************************************
** 函数名称:MSPI_SendData()
** 函数功能:向SPI总线发送数据。
** 入口参数:data 待发送的数据
** 出口参数:返回值为读取的数据
******************************************************************************/
uint8 MSPI_SendData(uint8 data)
{
IO0CLR = HC595_CS; // 片选74HC595
SPI_SPDR = data;
while(0 == (SPI_SPSR & 0x80)); // 等待SPIF置位,即等待数据发送完毕
IO0SET = HC595_CS;
return(SPI_SPDR);
}
/******************************************************************************
** 函数名称:Display()
** 函数功能:数码管显示。
** 入口参数:i 待显示的数据
** 出口参数:无
******************************************************************************/
void Display(int i)
{
uint8 data;
data = MSPI_SendData(DISP_TAB[i]);
}
/*******************************************************************************************
** 函数名称:IRQ_Timer0()
** 函数功能:定时器 0 中断服务程序,BEEP控制。
** 入口参数:无
** 出口参数:无
*******************************************************************************************/
void __irq IRQ_Timer0 (void)
{
countDown--; // 倒计时减1
if(countDown <= 5 && countDown > 0)
{
IO0CLR = BEEP; // 打开BEEP
DelayNS(1);
IO0SET = BEEP; // 关闭BEEP
}
else if(countDown == 0)
{
TwinkleLED(); // LED 灯闪烁
T0TCR = 0x00; // 停止定时器
T0TC = 0; // 定时器设置为0
start = 0;
}
IO1SET = ~((LED_TIMER[(30-countDown)%11]) << 18);
DelayNS(1);
IO1CLR = ((LED_TIMER[(30-countDown)%11]) << 18);
T0IR = 0x01; // 清除中断标志
VICVectAddr = 0x00; // 通知VIC 中断处理结束
}
/******************************************************************************
** 函数名称:Timer_Init()
** 函数功能:初始化定时器。
** 入口参数:无
** 出口参数:无
******************************************************************************/
void Timer_Init(void)
{
T0TC = 0; // 定时器设置为0
T0PR = 0; // 时钟不分频
T0MCR = 0x03; // 设置T0MR0 匹配后复位T0TC,并产生中断标志
T0MR0 = Fpclk; // 1秒钟定时
/* 打开定时器T0 中断(使用向量中断) */
VICVectCntl1 = 0x20 | 0x04; // 设置定时器0 中断通道分配第二优先级
VICVectAddr1 = (uint32)IRQ_Timer0; // 设置中断服务程序地址
VICIntEnable = 1 << 0x04; // 使能定时器0 中断
}
/*******************************************************************************************
** 函数名称:main()
** 函数功能:初始化外部中断为向量中断,低电平触发,然后等待中断。
** 调试说明:
****