新闻  |   论坛  |   博客  |   在线研讨会
原代码:ZRTOS的任务篇LCD
a181633697 | 2009-07-31 22:40:45    阅读:800   发布文章

1、问题综述:
在嵌入式系统中有许多LCD装置,信息的显示往往是由控制芯片向LCD写入byte stream来实现的,如果一次写得太多,LCD装置的buffer放不下,同时也会影响系统的实时性能。合理地选用适当的优先级的任务来对LCD编程,可以收到很好的效果。在下面的程序中,当嵌入式系统需要LCD显示信息时,那些任务将要显示的信息先写入一个ring buffer(lcd_data),然后由另一个任务(lcd_send)将这些数据显示在LCD屏幕上。以下是部分原代码:

#define _LCD_DATA_TO_SENT (3)

typedef struct {
  U8 data[_LCD_DATA_TO_SENT];  //3 bytes are the basic data unit for
} SYN_SCI_SD_SMALL_TYPE;           //a LCD (汉字图形点阵液晶显示模块)

#define LCD_BUFFER_SIZE 64 //32 is too small!    

typedef struct {
  U8 head;
  U8 tail;    
  SYN_SCI_SD_SMALL_TYPE lcd_rcv[LCD_BUFFER_SIZE]; 
} LCD_DATA_TYPE; 

static LCD_DATA_TYPE lcd_data;    //ring buffer to hold all data/byte stream
static RTOS_SEM_ID semrenew;      //semaphone to wake up task lcd_send()
static long sm_timeout;           //time out or period of task lcd_send()

void lcd_Init(void) //initialization of LCD
{
  SYN_SCI_SD_SMALL_TYPE reg; //initialized command to LCD

  semrenew = rtos_semBCreate(0, SEM_EMPTY);//create a semaphone
  sm_timeout = 5;//wait 5 tick or 50 ms;

  lcd_data.head = lcd_data.tail = 0; //clear the ring buffer
  
  rtos_taskSpawn(RTOS_PEND_FOREVER_PRI+1, (RTOS_FUNCPTR) lcd_send, 0);
  //spawn a task (lcd_send) to handle LCD data sending
 
  reg.data[0] = LCD_WRITE_CMD;//this is a LCD write command
 
  fill_a_byte(&reg.data[1],0x30);//fill a byte to the command
  add2send(&reg); //fill the command into the ring buffer
  //other command here ...  
}

static void add2send(SYN_SCI_SD_SMALL_TYPE * reg)//subroutine to put data
{                                                //into the ring buffer
  U8 m = lcd_data.tail+1;
  int n;
 
  if (m == LCD_BUFFER_SIZE) {//the buffer ring over
    m = 0;
  } 
 
  for(n = 0; n < _LCD_DATA_TO_SENT; n++) //always allow new data in
    lcd_data.lcd_rcv[lcd_data.tail].data[n] = reg->data[n];

  if (m == lcd_data.head) { //the ring buffer is full, push the old one out
    if (++lcd_data.head == LCD_BUFFER_SIZE) lcd_data.head = 0;
  } //

  lcd_data.tail = m; //all "in" data shall result in tail growing or
                     //all "producing" is about to go to tail in the
                     //comsumer/product relationship

  if (sm_timeout == (long) WAIT_FOREVER) {//lcd_send task in idle
    sm_timeout = 1; //set the period of lcd_send task, 1 tick or 10 ms
    rtos_semGive(semrenew);//wake up the lcd_send
  }
 
}

static void send_a_byte(U8 byte) //synchronous serial communication for a LCD
{                                //which does not comply with SPI interface
  U8 n;
  U8 bit = 0x80;
 
  for(n = 0; n < 8; n++) {
    LCD_SID(byte&bit); //synchronous serial sending a bit
    bit >>= 1;
  }
}

static void lcd_send(void) //task to handle LCD data sending
{
  U8 n;
  long results = rtos_semTake(semrenew, sm_timeout);//may pended here till time out
    
  if (lcd_data.head == lcd_data.tail || results == (long) OK) {
    return;//the ring buffer is empty or it is not a time out
  }        
  sm_timeout = 1;//set the period of lcd_send task, 1 tick or 10 ms

  LCD_CE_ON(1); //CE enable
  LCD_SCLK(0);
   
  for(n = 0; n < _LCD_DATA_TO_SENT; n++) { //send byte by byte
    send_a_byte(lcd_data.lcd_rcv[lcd_data.head].data[n]);
  }
 
  LCD_CE_ON(0); //CE disable
  lcd_data.head++;
   
  if (lcd_data.head == LCD_BUFFER_SIZE) {
    lcd_data.head = 0;
  }
   
   
  if (lcd_data.head == lcd_data.tail) {
    //continue to send till the buffer is empty
    sm_timeout = WAIT_FOREVER;//pended the task
  }
}

void displaySpeedInLCD(U8 speed) //displacy vehicle speed value on
{                                //LCD screen
  U8 speed_display[] = {//unicode chinese character "speed"
    0xCB,0xD9,0xB6,0xC8,0x3A,
    0,0,0
  };
 
  fill_a_few_100(&speed_display[5],speed); //fill the speed value into storage
                                           //speed_display[]
  fill_block(speed_display,0x93,8);    //fill the speed_display[] into screen
}

static void fill_block(U8* data, U8 loc, U8 size) //fill a block data into
{                                                 //LCD screen
  SYN_SCI_SD_SMALL_TYPE reg;//LCD command form
  U8 n; //index
 
  reg.data[0] = LCD_WRITE_CMD; //LCD write command command
  fill_a_byte(&reg.data[1],loc); //location
  add2send(&reg);//put data into ring buffer/product queue

  reg.data[0] = LCD_WRITE_DATA;//LCD write data command
 
  for(n = 0; n < size; n++) {//fill all data
    fill_a_byte(&reg.data[1],data[n]); //data
    add2send(&reg);
  }
}


2、结论
从上述的例子我们可以看到,在嵌入式系统中,其实是可以用很简单的方法来达到很令人满意的结果的。在教科书中,本例是个经典的生产者-消费者(Comsumer - productor) 问题,有大量的因素(race conditions等)要考虑。在vxWorks等RTOS中通常用MessageQueue类的LinkList的方式来对应。但这样做有如下问题:i)因为嵌入式系统的资源很少,一但LinkList出问题,就会造成严重后果,(这也解释为什么大多数工程师在编程时,将需要的RAM事先安排好);ii)嵌入式系统所面对的是各式各样的硬件设备,MessageQueue类的东东很难有效甚至不能成功地完成任务。比如,在本例中,对LCD而言,10毫秒的间隔是个不错的间隔,但对其他设备可能就不合适;间隔太小会造成硬件的操作失败,间隔太大又造成实时性能差和数据丢失。
在本例中,定义了一个环形数组(ring buffer)所有的products(产品)go to “tail”,而所有的comsumer(消费者)using "head", 当tail将要等于head时,ring buffer is full,此时设计者有以下三种选择:i)把最老的数据推出ring buffer(就像本例中做的那样);ii)拒绝新的数据;iii)接收或拒绝数据并同时通知caller with a return value;
另外,由于使用的是ZRTOS,本例中完全没有race condition问题出现。

*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
推荐文章
最近访客