"); //-->
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(®.data[1],0x30);//fill a byte to the command
add2send(®); //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(®.data[1],loc); //location
add2send(®);//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(®.data[1],data[n]); //data
add2send(®);
}
}
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问题出现。
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。