自從接受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