本帖最后由 wgsxsm 于 2015-10-28 23:44 编辑
离交作业的日子越来越近,最近刚好也空闲了一些,赶紧补一下,要不然真的要烂尾了。当时申请板子的理由是做一个简易的门禁系统,按部就班,功能要一点点加入进来了。
这两天加入了如下的AD按键模块和用来提醒解锁成功的蜂鸣器模块。
接下来可能要加入EEPROM用来存储密码,时间充足或者加入无线(蓝牙)和RTC时钟等等。
~~~~~~~~~~~~~~~~~~~~~~~~分割线~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1.硬件部分
最近花了半天的时间做了下面的这个硬件,按键码与下图的编码一一对应:
实物图与硬件电路如下,AD测量使用的前面帖子里提到的P07/ANI0,但是要割断板子上的S4跳线处
加之之前焊接的无源蜂鸣器模块,实物图以及电路图如下,蜂鸣器引脚为P40,因为该引脚刚好有蜂鸣器输出控制功能。
顺便提一下,具有蜂鸣器输出控制功能的还有一个P10口(被OLED占用了)。
2.软件部分
AD按键部分主要还是移植应用笔记中的:R01AN2007CC0110-A/D 按键输入配合 4 位 8 段数码管显示部分代码。
- const uint16_t AD_Key_Data[13] = {
- 983, /* 13/13*1023=1023 */
- 904, /* 12/13*1023=944 */
- 825, /* 11/13*1023=865 */
- 747, /* 10/13*1023=786 */
- 668, /* 9/13*1023=708 */
- 589, /* 8/13*1023=629 */
- 511, /* 7/13*1023=550 */
- 432, /* 6/13*1023=472 */
- 353, /* 5/13*1023=393 */
- 275, /* 4/13*1023=314 */
- 196, /* 3/13*1023=236 */
- 117, /* 2/13*1023=157 */
- 39 /* 1/13*1023=78 */
- };
复制代码
static void AD_Init(void)
{
ADCEN = 1; /* SFR used by A/D converter can be read/written */
ADM0 = 0x00U; /* disable AD conversion and clear ADM0 register */
ADMK = 1U; /* disable INTAD interrupt */
ADIF = 0U; /* clear INTAD interrupt flag */
/* Set INTAD low priority */
ADPR1 = 1U;
ADPR0 = 1U;
ADM0 = 0; /* Select Mode, fCLK/8 */
ADM2 = 0; /* 10-bit resolution */
ADS = 0; /* ANI0 */
}
void ADKey_Init(void)
{
ADKey_IO_Init();
AD_Init();
}
static void AD_In(void)
{
uint16_t i, ADValue;
static uint16_t s_ADCnt=0; /* A/D conversion counter */
static uint16_t s_AD_Sum; /* Summation of A/D conversion results */
static uint16_t s_AD_Max; /* Maximum value of A/D conversion result */
static uint16_t s_AD_Min; /* Minimum value of A/D conversion result */
ADCE = 1;
for(i = 0; i < 10; i++); /* 0.1us waiting is needed */
ADCS = 1;
for(i = 0; i < 3; i++);
/* Waiting A/D conversion */
while(ADCS != 0); /* A/D conversion completed? */
ADValue = (((uint16_t)(ADCRL >> 6)) | ((uint16_t)ADCRH << 2));
/* Refresh a max and min ad value */
if (s_ADCnt == 0) /* If it is the first conversion */
{
s_AD_Max = ADValue; /* Saves the maximum value */
s_AD_Min = ADValue; /* Saves the minimum value */
}
else
{
if (ADValue > s_AD_Max)
s_AD_Max = ADValue; /* Renews the maximum value */
if (ADValue < s_AD_Min)
s_AD_Min = ADValue; /* Renews the minimum value */
}
/* Up a count of ad sampling count */
s_ADCnt += 1; /* Conversion counter +1 */
s_AD_Sum += ADValue; /* Adds A/D conversion result */
/* Set a fixed ad value */
if (s_ADCnt == 6) /* If converted counter is 6 */
{
s_ADCnt = 0; /* Clears A/D conversion counter */
s_AD_Sum -= s_AD_Max; /* Omits the maximum value */
s_AD_Sum -= s_AD_Min; /* Omits the minimum value */
g_AD_Fix = s_AD_Sum >> 2; /* A/D value fixed */
s_AD_Sum = 0; /* Clears summation data */
g_Flag_AD_Fix = 1; /* Sets A/D value fixed flag */
}
}
static void AD_Key_In(void)
{
uint8_t i;
static uint8_t s_Last_AD_Key; /* Fixed key code last time */
/* Check a fix of ad data */
if (g_Flag_AD_Fix == 1) /* A/D value fixed ? */
{
g_Flag_AD_Fix = 0; /* A/D value fixed flag off */
/* Decode ad-value to ad-keycode */
for(i = 0;i < 13 ;i++)
{
if (g_AD_Fix >= AD_Key_Data
)
break;
}
/* Check the last key data */
if (s_Last_AD_Key == i) /* Is it the same key as the last one ? */
g_AD_Key_Code = i; /* g_AD_Key_Code fixed */
else
s_Last_AD_Key = i; /* Save the key code as the last one */
}
}
static void Key_Process(void)
{
if(g_Last_AD_Key_Code != g_AD_Key_Code) /* Current key update? */
{
switch(g_AD_Key_Code)
{
case 1:
OLED_DrawChar1608(0,4,'1');
break;
case 2:
OLED_DrawChar1608(0,4,'2');
break;
case 3:
OLED_DrawChar1608(0,4,'3');
break;
case 4:
OLED_DrawChar1608(0,4,'4');
break;
case 5:
OLED_DrawChar1608(0,4,'5');
break;
case 6:
OLED_DrawChar1608(0,4,'6');
break;
case 7:
OLED_DrawChar1608(0,4,'7');
break;
case 8:
OLED_DrawChar1608(0,4,'8');
break;
case 9:
OLED_DrawChar1608(0,4,'9');
break;
case 10:
OLED_DrawChar1608(0,4,'*');
break;
case 11:
OLED_DrawChar1608(0,4,'0');
break;
case 12:
OLED_DrawChar1608(0,4,'#');
break;
default:
break;
}
g_Last_AD_Key_Code = g_AD_Key_Code;
}
}
void Key_Handle(void)
{
AD_In(); /* Gets A/D fixed value */
AD_Key_In(); /* Confirms A/D key code */
Key_Process(); /* Inputted key function handle */
}
蜂鸣器部分主要是根据用户手册:第8章--时钟输出 / 蜂鸣器输出控制电路进行一步步设置
但是值得说明的是,仿真时,用到了P40口,所以为了使蜂鸣器发声不受影响的话还是需要拔掉仿真器的.
蜂鸣器的频率曲线如下,因此为了使发声更大,要选择在4KHz附近值。
而P40作为 蜂鸣器输出控制功能时,其输出时钟最接近于4KHz的值为4.88KHz:
- void BUZ_Init(void)
- {
- P4 &= 0xfe; /* P40 bit set 0*/
- PM4 &= 0xfe; /* Output mode */
- PIOR |= 0x01; /* PCLBUZ0 Sel P4.0 */
- // PMC1 &= 0xfe;
- // P1 &= 0xfe; /* P10 bit set 0*/
- // PM1 &= 0xfe; /* Output mode */
- // PIOR &= 0xfe; /* PCLBUZ0 Sel P1.0 */
-
- CKS0=0x06; /* f=4.88kHz */
- }
- void BUZ_Off(void)
- {
- CKS0 &= 0x7f;
- }
- void BUZ_On(void)
- {
- CKS0 |= 0x80;
- }
复制代码
3.操作演示
蜂鸣器的发声就先不做了,以后有机会上视频时才有声音。