stm32 嵌入式系统之 LED状态灯服务

       自从接受linux的熏陶来,编程思想已经发生极大转变(提高)。下面是由request gpio,及注册式驱动启发,在freertos上实现led灯服务。该服务可提供:led控制常关方式,常开方式,闪烁方式。每个控制任务都可单独设定时间,周期频率,还有各自优先级。

       指导思想:开一个周期性任务,该任务周期是20ms计(鉴于人的视觉残留是20ms)。该任务中统计led控制请求中最高优先级控制任务中GPIO的状态,完成后,再对相应的GPIO进行操作。

       具体实施:首先两个链表,分别维护led控制任务列表,led请求的gpio列表。

typedef struct LEDServerTypeDef
{
	struct list_head entry; //链表节点
	char* name; //控制任务名称
	GPIOConf_t gpioConf; //控制任务GPIO的配置
	LEDLightTypeDef type; //显示方式
	uint32_t ticks; //控制任务生存时间
	uint32_t cycle; //半周期(闪烁方式有效)
	uint32_t cc; //半周期内的计数变量
	uint32_t bltime; //半周期(亮)内闪烁次数
	uint32_t blcount; //半周期(亮)内的计数变量
	uint32_t blactive; //半周期(亮)闪烁使能
	xSemaphoreHandle lock;//访问锁
	GPIOServer_t *gpio;//请求的gpio handle
}LEDServer_t;
typedef struct GPIOServerTypeDef{
	struct list_head entry; //链表节点
	uint16_t link; //请求连接数
	GPIO_TypeDef *GPIOx; //GPIOA/B/C/D/E/F
	uint16_t GPIO_Pin; //GPIO Pin脚
	PriorityTypeDef curMaxPrio; //当前有效的最高优先级
	GPIO_PinState set; //设置值
	GPIO_PinState state; //当前状态值
}GPIOServer_t;

结构中其他定义:

typedef enum{
	PRIORITY_LOW,
	PRIORITY_NORMAL,
	PRIORITY_HIGH,
}PriorityTypeDef;//优先级
typedef enum{

	LED_STATE_ON,
	LED_STATE_OFF,
}LEDStateTypeDef;//LED状态
typedef enum{
	LED_ON,
	LED_OFF,
	LED_BLINK,
}LEDLightTypeDef;//LED控制方式
typedef struct GPIOConfigTypeDef{
	PriorityTypeDef priority; //GPIO优先级
	GPIO_TypeDef *GPIOx; //GPIOA/B/C/D/E/F
	uint16_t GPIO_Pin; //GPIO Pin脚
	GPIO_PinState gpio_led_on; //gpio 设置该值时led灯亮
}GPIOConf_t;//GPIO的配置

GPIO链表的创建和释放:

static LIST_HEAD(gGPIOListHead);

GPIOServer_t* requestGPIO(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
	struct list_head *pos;
	GPIOServer_t *sGpio;
	
	list_for_each(pos, &gGPIOListHead){
		sGpio = list_entry(pos, GPIOServer_t, entry);
		if(GPIOx == sGpio->GPIOx &&
				GPIO_Pin == sGpio->GPIO_Pin){
			sGpio->link ++;
			return sGpio;
		}
	}
	
	sGpio = pvPortMalloc(sizeof(GPIOServer_t));
	if(sGpio == NULL){
		return NULL;
	}
	sGpio->GPIOx  = GPIOx;
	sGpio->GPIO_Pin = GPIO_Pin;
	sGpio->link ++;
	sGpio->set = GPIO_PIN_RESET;
	sGpio->state = HAL_GPIO_ReadPin(GPIOx, GPIO_Pin);
	sGpio->curMaxPrio = PRIORITY_LOW;
	list_add(&sGpio->entry, &gGPIOListHead);
	return sGpio;
}

void freeGPIO(GPIOServer_t *gpio)
{
	struct list_head *pos, *n;
	GPIOServer_t *sGpio;
	
	if(gpio == NULL)
		return;
	
	if(list_empty(&gGPIOListHead))
		return;
	
	list_for_each_safe(pos, n, &gGPIOListHead){
		sGpio = list_entry(pos, GPIOServer_t, entry);
		if(sGpio == gpio){
			sGpio->link--;
			if(sGpio->link == 0){
				list_del(&sGpio->entry);
				vPortFree(sGpio);
				return;
			}
		}
	}
	//no regisist gpio, it does not suppose to be here;
	vPortFree(sGpio);
}

GPIO根据优先级设定控制值

LEDStateTypeDef getLEDState(GPIOServer_t *gpio, GPIO_PinState gpio_led_on)
{
	return gpio->state == gpio_led_on? LED_STATE_ON: LED_STATE_OFF;
}

void controlGPIO(GPIOServer_t *gpio, PriorityTypeDef prio, GPIO_PinState set)
{
	if(prio >= gpio->curMaxPrio){
		gpio->set = set;
		gpio->curMaxPrio = prio;
	}
}

void controlLED(GPIOServer_t *gpio, GPIOConf_t *conf, LEDStateTypeDef set_led)
{
	GPIO_PinState set_gpio;
	set_gpio = (set_led == LED_STATE_ON)? conf->gpio_led_on: (GPIO_PinState)!conf->gpio_led_on;
	controlGPIO(gpio, conf->priority, set_gpio);		
}
LED控制链表的创建和释放
static LIST_HEAD(gLEDServerListHead);/* 获取控制任务 */
LEDServer_t *getLED(char* name)
{
	struct list_head *pos;
	LEDServer_t *sLed;
	
	if(name == NULL)
		return NULL;
	
	list_for_each(pos, &gLEDServerListHead){
		sLed = list_entry(pos, LEDServer_t, entry);
		if(!strcmp(sLed->name, name))
			return sLed;
	}
	return NULL;
}
/* 请求or设置控制任务 */
int setLED(LEDServer_t *led)
{
	struct list_head *pos;
	LEDServer_t *sLed;
	
	if(led == NULL)
		return -1;
	
	list_for_each(pos, &gLEDServerListHead){
		sLed = list_entry(pos, LEDServer_t, entry);
		if(!strcmp(sLed->name, led->name)){
			xSemaphoreTake(sLed->lock, portMAX_DELAY);
			if(sLed->gpioConf.GPIOx != led->gpioConf.GPIOx  //如果设置的led控制gpio不同
					|| sLed->gpioConf.GPIO_Pin != led->gpioConf.GPIO_Pin){
				GPIOServer_t *gpio = requestGPIO(sLed->gpioConf.GPIOx, sLed->gpioConf.GPIO_Pin);//请求gpio
				if(gpio){//
					freeGPIO(sLed->gpio);
					sLed->gpio = gpio;    //重新设定gpio
					sLed->gpioConf.GPIOx = led->gpioConf.GPIOx;
					sLed->gpioConf.GPIO_Pin = led->gpioConf.GPIO_Pin;
				}
			}
			sLed->gpioConf.priority = led->gpioConf.priority; //设置其他参数
			sLed->gpioConf.gpio_led_on = led->gpioConf.gpio_led_on;
			sLed->gpioConf = led->gpioConf;
			sLed->type = led->type;
			sLed->ticks = led->ticks;
			sLed->cycle = led->cycle;
			sLed->bltime = led->bltime;
			sLed->blcount = led->blcount;
			sLed->blactive = led->blactive;
			
			xSemaphoreGive(sLed->lock);
			return 0;
		}
	}
	//新的led控制任务
	sLed = pvPortMalloc(sizeof(LEDServer_t));
	if(sLed == NULL){
		return -1;
	}
	memcpy(sLed, led, sizeof(LEDServer_t));
	sLed->lock = xSemaphoreCreateMutex();
	if(sLed->lock == NULL){
		vPortFree(sLed);
		return -1;
	}
	//请求gpio
	sLed->gpio = requestGPIO(sLed->gpioConf.GPIOx, sLed->gpioConf.GPIO_Pin);
	if(sLed->gpio == NULL){
		vPortFree(sLed);
		return -1;
	}
	list_add(&sLed->entry, &gLEDServerListHead);
	return 0;
}
/* 取消led控制任务 */
void unsetLED(char* name)
{
	struct list_head *pos, *n;
	LEDServer_t *sLed;
	
	if(name == NULL)
		return;
	
	if(list_empty(&gLEDServerListHead))
		return;
	
	list_for_each_safe(pos, n, &gLEDServerListHead){
		sLed = list_entry(pos, LEDServer_t, entry);
		if(!strcmp(sLed->name, name)){
			list_del(&sLed->entry);
			freeGPIO(sLed->gpio);
			vSemaphoreDelete(sLed->lock);
		  vPortFree(sLed);
		}
	}
}

周期性控制任务

static void LEDServerTask(void const * argument)
{
	uint32_t freq = 20;
	struct list_head *pos;
	LEDServer_t *sLed;
	LEDStateTypeDef led_state;
	GPIOServer_t *sGpio;
	
	while(1){
		//led 控制任务循环检测
		list_for_each(pos, &gLEDServerListHead){
			sLed = list_entry(pos, LEDServer_t, entry);
			xSemaphoreTake(sLed->lock, portMAX_DELAY);
			switch(sLed->type)
			{
				case LED_ON:
					if(sLed->ticks > 0){
					    	controlLED(sLed->gpio, &sLed->gpioConf, LED_STATE_ON);
					}
					break;
				case LED_OFF:
					controlLED(sLed->gpio, &sLed->gpioConf, LED_STATE_OFF);
					break;
				case LED_BLINK:
 					if(sLed->ticks > 0){
						if(sLed->bltime > 1 && sLed->blactive && sLed->cc < sLed->cycle){//半周期(亮)闪烁控制
							if(sLed->blcount > sLed->cycle/(sLed->bltime*2-1)){
								led_state = getLEDState(sLed->gpio, sLed->gpioConf.gpio_led_on);
								controlLED(sLed->gpio, &sLed->gpioConf, (LEDStateTypeDef)!led_state);
								sLed->blcount = 0;
							}
							sLed->blcount += freq;
						}
						if(sLed->cc >= sLed->cycle){
							led_state = getLEDState(sLed->gpio, sLed->gpioConf.gpio_led_on);
							controlLED(sLed->gpio, &sLed->gpioConf, (LEDStateTypeDef)!led_state);//翻转
							sLed->cc = 0;
							sLed->blcount = 0;
							if(led_state == LED_STATE_OFF)//翻转前状态
								sLed->blactive = 1;//半周期(亮)状态设置
							else
								sLed->blactive = 0;
						}						
						sLed->cc += freq;
					}
					break;
				default :
					break;
			}
			if(sLed->ticks != portMAX_DELAY){
				if(sLed->ticks != 0){
					if(sLed->ticks > freq){
						sLed->ticks -= freq;//控制任务生存计时
					}
					else{
						sLed->ticks = 0;//结束
						sLed->cc = 0;
						controlLED(sLed->gpio, &sLed->gpioConf, LED_STATE_OFF);//shout down
					}
				}
			}
			xSemaphoreGive(sLed->lock);
		}
		//gpio 控制操作
		list_for_each(pos, &gGPIOListHead){
			sGpio = list_entry(pos, GPIOServer_t, entry);
			if(sGpio->set != sGpio->state){//避免频繁操作,不同状态才可实际行动
				HAL_GPIO_WritePin(sGpio->GPIOx, sGpio->GPIO_Pin, sGpio->set);
				sGpio->state = sGpio->set;
				sGpio->curMaxPrio = PRIORITY_LOW;//复位优先级,为下次做准备
			}
		}
		
		osDelay(freq);
	}
}

实例

static void led_running_blink()
{
	LEDServer_t led;
	led.name = "Run";
	led.gpioConf.GPIOx = GPIOC;
	led.gpioConf.GPIO_Pin = GPIO_PIN_5;
	led.gpioConf.gpio_led_on = GPIO_PIN_RESET;
	led.gpioConf.priority = PRIORITY_LOW;
	led.type = LED_BLINK;
	led.ticks = portMAX_DELAY;
	led.cycle = 1000;
	led.cc = 0;
	led.bltime = 0;
	led.blcount = 0;
	led.blactive = 0;
	
	setLED(&led);
}

以上还可以扩展很多led的控制方式。

下次介绍注册式驱动的实现,其实也和上面思想一致,从注册的驱动链表中,适配适合的硬件ID然后返回驱动Handle


  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32是一款广泛应用于嵌入式系统的微控制器,通过定时器可以方便地实现LED的闪烁功能。 首先,我们需要配置一个定时器来控制LED的闪烁间隔。定时器可以选择使用内部的系统时钟作为时基,也可以通过外部时钟源提供时基。在本例中,我们选择使用内部时钟作为时基。 接下来,我们需要配置GPIO引脚来控制LEDSTM32有多个GPIO引脚可以用来控制LED的亮灭状态。我们将一个GPIO引脚配置为输出模式,并连接到LED。 然后,我们需要编写代码来配置定时器和GPIO引脚,并实现LED的闪烁。 首先,我们使用CubeMX来配置STM32的时钟源和GPIO引脚。我们选择一个合适的时钟源作为定时器的时基,同时将一个GPIO引脚配置为输出模式,并连接到LED。 然后,我们打开Keil或其他编程软件,编写C语言代码来实现LED的闪烁。在代码中,我们使用定时器的中断功能来控制LED的亮灭状态。 首先,我们初始化定时器并配置定时器的参数,如定时器的预分频值和计数器的重载值。然后,我们使能定时器的中断,并编写一个中断服务函数来处理定时器中断。 在中断服务函数中,我们使用一个计数变量来记录中断的次数。当计数变量达到我们需要的闪烁间隔时,我们改变LED状态,即改变GPIO引脚的电平状态,使LED闪烁。 最后,我们在主函数中启动定时器,并进入一个无限循环来保持程序的运行。在无限循环中,我们不需要做任何操作,因为LED的闪烁将由定时器中断来实现。 通过上述步骤,我们成功实现了STM32通过定时器来控制LED的闪烁功能。这样,每当定时器中断触发时,LED状态就会改变,从而实现了LED的闪烁效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值