先说矩阵LED,如图:
下面以行几、列几的表达方法说明
三极管可以当做开关使用
由图可知,如果想让一个LED亮,那么它对应的行和列的三极管都要导通。如果这个矩阵每次只亮一个LED,那么正常写程序没问题。
如果我们想让LED1和LED6亮,其他灯不亮,则需要开启行1、行2、列1、列2,随之带来的问题就是LED2和LED5也会跟着亮起来。
解决方法类似于扫描,因为单片机运行速度很快,可以在很短的时间内开关数次而肉眼看不到。所以我们可以每次只亮一列(排)灯,亮单位时间后关掉同时开启另外一列(排),如此四次一循环就可以让该亮的灯亮起来,不该亮的灯关闭。下附源码(用的STM32F103芯片)
//这个任务放在时间片里跑
//将打开(关闭)一行(一列)封装成了四个小函数
//A[][]是个4X4的数组,对应矩阵16个LED
void TASK_LED(void)
{
static uint8_t flag=0;
static uint8_t H=0;
int a=0;
TIM_ClearFlag(TIM3, TIM_FLAG_Update); //清除中断标志位
switch (flag) //用switch方法实现轮询
{
case 0:
{
for(a=0;a<4;a++)
{
if(A[H][a])
{
line_on(a); //打开一列
row_on(H); //打开一行 这两个函数的定义在后面
}
}
flag=1; //把标志位改成下一个,类似于指针,下一次进switch会进入下一种情况进行运行
break;
}
case 1:
{
for(a=0;a<4;a++)
{
if(A[H][a])
{
line_off(a);
row_off(H);
}
}
flag=2;
H=1;
break;
}
case 2:
{
for(a=0;a<4;a++)
{
if(A[H][a])
{
line_on(a);
row_on(H);
}
}
flag=3;
break;
}
case 3:
{
for(a=0;a<4;a++)
{
if(A[H][a])
{
line_off(a);
row_off(H);
}
}
flag=4;
H=2;
break;
}
case 4:
{
for(a=0;a<4;a++)
{
if(A[H][a])
{
line_on(a);
row_on(H);
}
}
flag=5;
break;
}
case 5:
{
for(a=0;a<4;a++)
{
if(A[H][a])
{
line_off(a);
row_off(H);
}
}
flag=6;
H=3;
break;
}
case 6:
{
for(a=0;a<4;a++)
{
if(A[H][a])
{
line_on(a);
row_on(H);
}
}
flag=7;
break;
}
case 7:
{
for(a=0;a<4;a++)
{
if(A[H][a])
{
line_off(a);
row_off(H);
}
}
flag=0;
H=0;
break;
}
}
}
void line_on (uint8_t line)
{
switch (line) //用switch查找拉低引脚的位置
{
case 0:
{
GPIO_ResetBits(GPIOA,LEDL1_GPIO);
break;
}
case 1:
{
GPIO_ResetBits(GPIOA,LEDL2_GPIO);
break;
}
case 2:
{
GPIO_ResetBits(GPIOA,LEDL3_GPIO);
break;
}
case 3:
{
GPIO_ResetBits(GPIOA,LEDL4_GPIO);
break;
}
}
}
void line_off (uint8_t line)
{
switch (line) //用switch查找拉高引脚的位置
{
case 0:
{
GPIO_SetBits(GPIOA,LEDL1_GPIO);
break;
}
case 1:
{
GPIO_SetBits(GPIOA,LEDL2_GPIO);
break;
}
case 2:
{
GPIO_SetBits(GPIOA,LEDL3_GPIO);
break;
}
case 3:
{
GPIO_SetBits(GPIOA,LEDL4_GPIO);
break;
}
}
}
void row_on (uint8_t row)
{
switch (row) //用switch查找拉高引脚的位置
{
case 0:
{
GPIO_SetBits(GPIOA,LEDH1_GPIO);
break;
}
case 1:
{
GPIO_SetBits(GPIOA,LEDH2_GPIO);
break;
}
case 2:
{
GPIO_SetBits(GPIOB,LEDH3_GPIO);
break;
}
case 3:
{
GPIO_SetBits(GPIOB,LEDH4_GPIO);
break;
}
}
}
void row_off (uint8_t row)
{
switch (row) //用switch查找拉低引脚的位置
{
case 0:
{
GPIO_ResetBits(GPIOA,LEDH1_GPIO);
break;
}
case 1:
{
GPIO_ResetBits(GPIOA,LEDH2_GPIO);
break;
}
case 2:
{
GPIO_ResetBits(GPIOB,LEDH3_GPIO);
break;
}
case 3:
{
GPIO_ResetBits(GPIOB,LEDH4_GPIO);
break;
}
}
}
矩阵键盘也类似
可以知道当KEYA为高电平时,KEy1检测的就是高电平,为低电平时,KEY1检测的就是低电平。
也会出现之前所讲过的对角线问题,解决方法依然是轮询
每次打开一列,检测三行,然后关闭这一列打开另一列,如此往复三次为一个循环。
下附源码(也是在时间片里):
//KEY[][]对应一个3X3的数组,对应九个按键
void Key_Scan(uint8_t line) //实现键盘扫描后标志位置一
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)==1)
{
KEY[line-1][0]=1;
}
else
{
KEY[line-1][0]=0;
}
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==1)
{
KEY[line-1][1]=1;
}
else
{
KEY[line-1][1]=0;
}
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_15)==1)
{
KEY[line-1][2]=1;
}
else
{
KEY[line-1][2]=0;
}
}
void Key_Task(void) //实现键盘的扫描
{
switch(Flag_key)
{
case 0:
{
GPIO_SetBits(GPIOB,GPIO_Pin_10);
Key_Scan(1);
GPIO_ResetBits(GPIOB,GPIO_Pin_10);
Flag_key=1;
break;
}
case 1:
{
GPIO_SetBits(GPIOB,GPIO_Pin_11);
Key_Scan(2);
GPIO_ResetBits(GPIOB,GPIO_Pin_11);
Flag_key=2;
break;
}
case 2:
{
GPIO_SetBits(GPIOB,GPIO_Pin_12);
Key_Scan(3);
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
Flag_key=0;
break;
}
}
}