天天看點

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