Saturday, October 10, 2009

POSIX termios interface for canonical/non-canonical mode switching


前言
POSIX termios interface,提供一個介面讓我們可以做Termios Programming然而,Termios Programming是一個很大的主題,並且有許多的應用我們可以透過它來控制terminal在處理字元上的一些設定此外Serial Programming也是需要透過它來做一些設定,例如:設定Baud Rate或Parity Checking。甚至,它可以用來存取RS-232的serial port,並且做client-server終端控制的一些應用。而在這篇我想介紹的是利用termios來切換終端機接收字元的標準/非標準模式(canonical/non-canonical mode

Termios簡介
Termios是一個符合POSIX的介面有定義一些函式或巨集除了控制I/O; 它還可以控制硬體,例如決定使用多少位元來傳送跟接收字元; 並且,它也可以控制終端機的特性,例如決定終端機接收字元的模式(接下來本篇文章將以此來舉例); 最後,它也針對一些字元提供特殊的控制

標準/非標準模式(canonical/non-canonical mode)
不管在MS-DOS或是LINUX的終端機相信大家較常使用的是標準模式,而簡單的說,它就是允許使用者在按下Enter鍵前仍然可以修改輸入的字元(例如使用Backspace鍵來修改),所以按下Enter鍵,程式才會讀取字元。相對來說,非標準模式會讀取使用者輸入的任何字元,當然,我們可以利用termios來控制這些輸入,例如我們可以決定在非標準模式下,當使用者輸入多少個字元時即把這些字元傳送給終端機

如何利用termios來切換標準與非標準模式
底下就開始介紹如何使用termios來切換標準與非標準模式
1) 首先,要使用termios我們當然需要先把它include進來

#include <termios.h>

2) 接下來,宣告兩個termios結構(需要宣告兩個是因為,程式在做完任何只有程式本身所需的設定後,在程式結束前必須負責切換回終端機原本的設定,以免影響其它使用)

struct termios initial_settings, new_settings;

3) 取得目前終端機初始設定,並將設定存到initial_settings

tcgetattr(fileno(stdin),&initial_settings);

4) initial_settings複製給new_settings

new_settings = initial_settings;

5) 利用new_settings來做我們想要的設定,以我們的例子來講,我們需要切換成非標準模式

new_settings.c_lflag &= ~ICANON
6) 復原終端機原本的設定

tcsetattr(fileno(stdin),TCSANOW,&initial_settings);

如此,便可以切換成非標準模式,此外,在非標準模式下termios還有很多功能例如使用VTIMEVMIN來控制輸入舉個例子,每當使用者輸入兩個字元,終端機就自動接收這兩個字元,可以這麼寫

new_settings.c_cc[VMIN] = 2;
new_settings.c_cc[VTIME] = 0;

VMIN代表的是非canonical模式讀的最小字元數目。而VTIME代表讀取字元跟字元中間的延遲時間,設定成0代表不可慮VTIME。然而VMIN跟VTIME有許多不同組合,各代表著不同的意義,這就不是本篇文章討論範圍了

結論
termios允許我們去跟一些硬體做溝通,例如透過serial port跟其他設備溝通,目前又以嵌入式系統比較常需要使用,然而在PC上,由於各種傳輸規格日益增進,因此serial port的方法也慢慢被取代了

[Reference]
Beginning Linux Programming 4/e, Richard Stones and Neil Matthew


Wednesday, October 7, 2009

[AVR] Storing/Retrieving Data in the Program Space (Flash Memory)

AVR Atmel ATmega328P
微處理器 - AVR Atmel ATmega328P提供了32KB的Flash Memory(Program Memory),以及2KB的RAM(Data Memory)。而由於AVR是採用Harvard architecture,因此會利用Flash Memory來儲存program;並利用RAM儲存data,且有各自的address space。 


Harvard architecture V.S. Von Neumann architecture
因為Data Memory容量比較有限,因此我們希望能將一些constant data存放在空間較大的Flash Memory。加上,C語言是for Von Neumann architecture,也就是code跟data皆存在同一個address space,所以,針對AVR(Harvard architecture)所設計的compiler必須符合code跟data存放在不同address space這個設計標準。


AVR GCC __attribute__
要達到Harvard architecture的特性有許多方法
而AVR則利用類似GCC的__attribute__這個tag來實作,簡單講,__attribute__可以附加在函式變數的宣告,就是在編譯時會告訴compiler要特別對該函式變數做一些處理。AVR提供一些特殊用途的attribute,例如它提供__attribute__((__progmem__))這個attribute讓programmer可以將constant data儲存在Flash Mem.(Program Mem.)
In AVR GCC, there is a special attribute called progmem. This attribute is use on data declarations, and tells the compiler to place the data in the Program Memory (Flash). ~ avr-libc user manual 1.6.6
PROGMEM是AVR-Libc自行定義的一個macro,它被定義成類似GCC的attribute syntax,以下是<avr/pgmspace.h>這個標頭檔對於特殊attribute定義的節錄

#ifndef __ATTR_PROGMEM__
#define __ATTR_PROGMEM__ __attribute__((__progmem__))
#endif
/**
   \ingroup avr_pgmspace
   \def PROGMEM

   Attribute to use in order to declare an object being located in
   flash ROM.
 */
#define PROGMEM __ATTR_PROGMEM__


Storing/Retrieving Data in the Program Space 
接下來,我們來看如何利用AVR-Libc所提供的macro來存取program space的data:
首先,假設我們有以下的data
unsigned char mydata[2][10] =
{
{0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09},
{0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13}
};

我們可能會利用以下的方式來存取data
byte = mydata[i][j];

但是我們想要把它存放在program space,因此需要用到PROGMEM這個macro來幫助我們達成目的,因為PROGMEM是在<avr/pgmspace.h>定義,所以我們需將它include進來
#include <avr/pgmspace.h>


然後在變數宣告後面加上PROGMEM
unsigned char mydata[2][10] PROGMEM =
{
{0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09},
{0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13}
};

經過以上幾個步驟,就可以將data存在program space,接下來介紹如何從program space取出data。需要注意的是,我們已經將data搬到了program space了,因此我們沒辦法使用一般的方法來取出data,因為data space的data已被搬到program space。為此,AVR-Libc提供一個pgm_read_* macro讓我們可以取出program space的data,我們只需將data的位址當成pgm_read_* macro的參數即可,用法如下
byte = pgm_read_byte(&(mydata[i][j]));


結論
The macros and functions used to retrieve data from the Program Space have to generate some extra code in order to actually load the data from the Program Space. This incurs some extra overhead in terms of code space (extra opcodes) and execution time. ~ avr-libc user manual 1.6.6
另外,雖然user manual中也提到,從program space中取出data所花費的時間小於把data存入program space的時間,但是,如果程式在呼叫pgm_read_* macro上可以最佳化,當然是至上之選了 : ) 

[Reference]
AVR-Libc user manual (v1.6.6)



Saturday, October 3, 2009

如何將Pololu AVR Library加入自己的Project

前言
Pololu是一家由三個MIT的學生在2000年所創立(http://www.pololu.com/),Pololu主要是生產自家Robot,並且提供一些Robot的週邊零件及相關Library,至於Pololu如何發音?是這樣發的:polo-lu,polo跟俗稱的polo衫發音是一樣的。底下是該公司在Las Vegas的辦公室一景










Pololu AVR Library
這是Pololu公司針對AVR系列的微處理器所提供的Library

如何使用Pololu AVR Library
  1. 首先,根據自己所使用的產品來include所需的標頭檔,例如你購買了Pololu的3pi且想要使用C來coding,則需加入

    #include <pololu/3pi.h>
    

    另外
    ,假如是想使用C++,則加入

    #include <pololu/Pololu3pi.h>
    

  2. 加入所屬微處理器型號的靜態函式庫的連結,例如libpololu_atmega328p.a
  3. 基本上以上兩點已經可以成功編譯,但是為了減少code的大小有個指令可以讓程式在編譯時忽略不必要的function,即-Wl,-gc-sections (for linker option)
下面圖中顯示的是加入Step3前的memory使用狀況


      















而這是加入後的使用狀況
 
可見得把不必要的function拿掉後,程式明顯變小許多

[Reference]
Pololu AVR C/C++ Library User's Guide

Thursday, October 1, 2009

Windows Programming - A Short Introduction

使用環境:Visual C++ 2008


至今在Visual C++中,想要建立一個可以跟使用者互動的應用程式大概有三種方式,
分別是使用Windows API,MFC以及Windows Forms。以下將分別簡介這三種方法:


(1) Windows API:透過Windows API的函式,我們所寫的應用程式就可以跟作業系統做溝通。而利用Windows API來撰寫一個Windows程式至少需要透過兩個函式:WinMain(),它負責程式視窗的初始化;以及WindowProc(),負責處理應用程式的訊息,當有訊息產生,Windows會自動呼叫此函式。當然,這兩個函式都是透過Windows提供的API來跟Windows做溝通。我們來看一下利用這兩個函式所建立的視窗樣貌:


















(2) MFC:MFC是一個封裝Windows API之類別的集合,它簡化了利用Windows API來開發程式,也就是說,我們可以經由一些簡單的類別繼承,加上一些簡單的程式碼,就可以產生我們想要的結果。例如,下圖是繼承CWinAppCFrameWnd,且透過這兩個類別的成員來幫我們產生的視窗圖示:















(3) Windows Forms: Windows Forms應用程式需在CLR環境下執行,它比前面兩種分法更方便建立視窗,programmer完全不需要寫任何code就可以產生如下圖的視窗,並且自動產生code:

















結論:
就以寫GUI為目的來講的話,透過Windows API當然是最吃力的方法,至於剩下來的兩種方法的話,只能說看應用來決定要使用哪種方法,兩種方法各有所長。

[Reference]
Ivor Horton's Beginning Visual C++ 2005, Ivor Horton