下面给大家带来一个非常简单的32小项目:
一、要求
通过按键按下改变流水灯的方向,按下时从左到右闪烁,抬起时从右到左闪烁,任意取一个按键即可。
在这个项目中,灯光不止要从右到左,也需要从左到右闪烁,这是一大构思难点。
灯光的闪烁过程,可以想象成指针在圆环上顺逆时针运动,指针指向的灯光亮起。
C-51-32课后小项目需求&实现思路&答案 - Feishu Docs
二、方案构思
在《数据结构》中我们学过循环队列的实现,其中队列的指针是通过取模运算来实现移动循环的。在嵌入式C语言中,我们也可以借用这样的思想来实现双向控制。
首先,我们需要一个变量circle,让(circle%3)的值对应每一个LED的点亮条件。
circle模3==0,则点LED1
circle模3==1,则点LED2
......
在数论中,对于给定的正整数m,所有整数可以按照除以m的余数进行分类,这些分类被称为模m的同余类或剩余类。例如,在模3的情况下,整数被分为三类:余数为0的一类,余数为1的一类,余数为2的一类。
我们可以发现,随着circle的增加(减少),模的值是一直处在0-2循环状态的。
此外,我们需要一个控制灯流向的全局变量direct,#define LEFT 1,#define RIGHT 0。通过0和1传值给while(1)循环中的条件判断,来断定此次循环之前circle是增还是减,以控制灯向左流还是向右流。
最后,程序结构设计上讲,我们可以用双触发的外部中断响应来处理按键的按下(松开)状态变化,并在中断响应函数中对全局变量direct进行取反操作。而在主函数中,我们集中处理指针加减和模运算的分支控制。
三、程序设计
芯片型号:STM32F103
编译器:MDK
全局变量定义:
主函数:
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{ if(direct){
circle++;
}
else{
circle--;
}
switch(circle%3){
case 0: HAL_GPIO_WritePin(LED3_GPIO_Port,LED3_Pin,GPIO_PIN_RESET);
HAL_Delay(1000);
HAL_GPIO_WritePin(LED3_GPIO_Port,LED3_Pin,GPIO_PIN_SET);
break;
case 1: HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,GPIO_PIN_RESET);
HAL_Delay(1000);
HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,GPIO_PIN_SET);
break;
case 2: HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_RESET);
HAL_Delay(1000);
HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_SET);
break;
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
中断处理:
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
if(GPIO_Pin==KEY1_Pin){
for(long i =1; i<72000;i++) //消抖
{}
direct=!direct;
}
}