2015年9月10日 星期四

Raspberry Pi mini UART


mini UART 規格如下:

The mini Uart has the following features:
• 7 or 8 bit operation.
• 1 start and 1 stop bit.
• No parities.
• Break generation.
8 symbols deep FIFOs for receive and transmit.
• SW controlled RTS, SW readable CTS.
• Auto flow control with programmable FIFO level.
• 16550 like registers.
• Baudrate derived from system clock.


mini UART 暫存器控制整理如下: 


AUX BaseAddr 0x7E210000

0x7E21 5000 AUX_IRQ Auxiliary Interrupt status
0x7E21 5004 AUX_ENABLES Auxiliary enables
0x7E21 5040 AUX_MU_IO_REG Mini Uart I/O Data
0x7E21 5044 AUX_MU_IER_REG Mini Uart Interrupt Enable
0x7E21 5048 AUX_MU_IIR_REG Mini Uart Interrupt Identify
0x7E21 504C AUX_MU_LCR_REG Mini Uart Line Control
0x7E21 5050 AUX_MU_MCR_REG Mini Uart Modem Control
0x7E21 5054 AUX_MU_LSR_REG Mini Uart Line Status
0x7E21 5058 AUX_MU_MSR_REG Mini Uart Modem Status
0x7E21 505C AUX_MU_SCRATCH Mini Uart Scratch
0x7E21 5060 AUX_MU_CNTL_REG Mini Uart Extra Control
0x7E21 5064 AUX_MU_STAT_REG Mini Uart Status
0x7E21 5068 AUX_MU_BAUD_REG Mini UART Baud Rate

  • AUXIRQ : the mini UART has an interrupt pending  if bit 0 is set
  • AUXENB : used to enable miniUART function
  • AUX_MU_CNTL_REG : used to enable RX & TX operation on miniUART
  • AUX_MU_IO_REG : used to write data (1 byte) to and read data (1 byte) from the RX/TX FIFO (8 byte buffer).
  • AUX_MU_IER_REG:  used to enable interrupts and to clear RX/TX FIFO
  • AUX_MU_IIR_REG: used to identify/determine TX or RX interrupt and to clear interrupt pending bit
  • AUX_MU_LCR_REG : used to set data bit length (8 bit or 7 bit)  and gives access to the baudrate register (DLAB access=1)
  • AUX_MU_BAUD : set the 16-bit wide baudrate counter.
  • AUX_MU_STAT_REG: Some information about the internal status of the miniUART.
    bit[27:24] Transmit FIFO fill level: TX FIFO有多少資料 (範圍:0~8 byte)
    bit[19:16] Receive FIFO fill level : RX FIFO有多少資料 (範圍:0~8 byte)
Note: 

  1. AUXENB : If the enable bits are clear you will have no access to a peripheral. You can not even read or write the registers! 
  2. GPIO pins should be set up first the before enabling the UART.  So when it is enabled any data at the inputs will immediately be received . If the UART1_RX line is low (because the GPIO pins have not been set-up yet)  that will be seen as a start bit and the UART will start receiving 0x00-characters. 
  3. After a reset: the baudrate will be zero and the system clock will be 250 MHz
  4. Baud Rate:

Figure 2. UART receive frame synchronization and data sampling points.

Figure  shows a common method used by a UART receiver to synchronize itself to a received frame. The receive UART uses a clock that is 16 times the data rate. A new frame is recognized by the falling edge at the beginning of the active-low START bit. This occurs when the signal changes from the active-high STOP bit or bus idle condition. The receive UART resets its counters on this falling edge, expects the mid-START bit to occur after 8 clock cycles, and anticipates the midpoint of each subsequent bit to appear every 16 clock cycles thereafter. The START bit is typically sampled at the middle of bit time to check that the level is still low and ensure that the detected falling edge was a START bit, not a noise spike. Another improvement is to sample the START bit three times (clock counts 7, 8, and 9, out of 16) instead of sampling it only at the midbit position (clock count 8 out of 16).

 arm bootloader

BCM2835設定BaudRate counter兩種方式






void rpi_aux_mu_init() {
    volatile uint32_t reg_val;
    uint32_t t;

    /* Enable aux uart */
    RPI_AUX->ENABLES = RPI_AUX_MU_ENABLE;

    RPI_AUX->MU_IER = 0;

    /* Disable flow control */
    RPI_AUX->MU_CNTL = 0;

    RPI_AUX->MU_LCR = RPI_AUX_MU_LCR_8BIT_MODE;

    RPI_AUX->MU_MCR = 0;

    /* Diable all interrupts from mu and clear the fifos */
    RPI_AUX->MU_IER = 0;
    RPI_AUX->MU_IIR = 0xC6;


    //#define bits 8
  //RPI_AUX->MU_BAUD = ( _RPI_SYS_FREQ / ( 8 * bits )) - 1;
    RPI_AUX->MU_BAUD = 270;

    /* Setup GPIO 14 and 15 as alternative function 5 which is
     UART 1 TXD/RXD. These need to be set before enabling the UART */

    reg_val = memio_read32(_GPIO_FSEL1);
    reg_val &= ~(7 << 12); // GPIO 14
    reg_val |= 2 << 12;      // ALT5
    reg_val &= ~(7 << 15); // GPIO 15
    reg_val |= 2 << 15;      // ALT5

    memio_write32(_GPIO_FSEL1, reg_val);
    memio_write32(_GPIO_PUD, 0);
    dummy(150);
    memio_write32(_GPIO_PUD_CLK0, (1 << 14) | (1 << 15));
    dummy(150);
    memio_write32(_GPIO_PUD_CLK0, 0);

    RPI_AUX->MU_CNTL = RPI_AUX_MU_CNTL_TX_ENABLE |            RPI_AUX_MU_CNTL_RX_ENABLE;
}

其他相關文件:

  1. 關於 Raspberry Pi 的UART
  2. Determining Clock Accuracy Requirements for UART Communications
  3. 程式範例  https://github.com/dwelch67/raspberrypi



2015年9月7日 星期一

Linux中斷控制器的抽象封裝



BCM2835內部的中斷控制器:


BCM2835內部的中斷控制器的架構:  主要包含GPU本身中斷(64個中斷)及21個基本中斷, 其中在 21個基本中斷,除了ARM IRQ外也同時包含了部份GPU的中斷在內 , 例如IRQ 79是GPU的 IRQ 53 (此為I2C中斷觸發),IRQ 80是GPU的 IRQ 54 (此為SPI中斷觸發)。而BCM2835 其全部54根GPIO 都是有產生中斷能力的, 由GPIO controller 產生中斷進入到中斷控制器的IRQ是配置在IRQ 49~52。



GPU  Interrupt Pending Bit (0~63)

ARM Basic Interrupt Pending Bit (0~20)



關於BCM2835 GPIO控制器

 共有54根GPIO分成2個bank , 一個管0~31, 另一個管32~53。由GPIO所產生中斷訊號共有3條, gpio_int[0],gpio_int[1], gpio_int[3], 分別是中斷控制器IRQ49, IRQ50及IRQ52,gpio_intp[0]是表示GPIO 0~31有產生中斷,而gpio_intp[1]是表示GPIO為32~53有中斷產生,而gpio_intp[3]是表示任一根GPIO中斷產生,不論是bank 0或bank1。





GPIO Event Detect Status Register


GPIO若要要產生中斷能力, 則必須設定 GPRENn GPFENn GPHENn, GPLENn, 4個暫存器,
分別用來設定欲中斷觸發類型, Rising Edge Enable, .Falling Edge, Enable,High Level Enable, Low Level Enable。若某根GPIO要Rising 及Falling Edge 都要觸發中斷, 則須別在GPRENn 及GPFENn 對應的bit設1.
每當有中斷產生時, 其對應pending register 的bit都會為1, 用以標示該中斷已產生。每當ISR 跑完後,都必須將其bit 清除, 否則該中斷將無法再繼續產生。而通常清除的方法,就是在對應的bit 上"寫 1" 。在BCM2835的GPIO中斷其等同於pending register的意義是 EDS Register (Event Detect Status ) 用以記錄GPIO 的中斷觸發事件 (不論是Edge 或 Level 都記錄)。


Linux中斷通用層的資料結構

Linux 必須有能力處理不同Interrupt controller 硬體間的差異,如此一來才具備可攜性。Linux底下主要透過三個資料結構完成:

  1. struct irq_chip: 中斷硬體控制器,實作中斷控制晶片的行為,例如 mask IRQ, unmask IRQ
  2. struct irqaction: 指向中斷發生時動作行為, 即描述ISR
  3. struct irq_desc: 中斷描述表 Interrupt Descriptor Table (IRQ Line描述)

Linux中斷通用層的資料結構之關聯


建立GPIO Interrupt Descriptor Table

用一個for loop 產生多張Interrupt Descriptor (struct irq_dest), 每個Descriptor均有不同GPIO IRQ Line編號,但都指向同一個Interrupt controller Chip。

static void bcm2708_gpio_irq_init(struct bcm2708_gpio *ucb)
{
  unsigned irq;
  ucb->gc.to_irq = bcm2708_gpio_to_irq;

 //for loop 針對每一個 IRQ Line 建立 irq_desc 
  for (irq = GPIO_IRQ_START; irq < (GPIO_IRQ_START + GPIO_IRQS); irq++) 
 { 
  irq_set_chip_data(irq, ucb);  //set chip specific data for an irq (170+i)
  irq_set_chip (irq, &bcm2708_irqchip); //set the irq chip for an irq
  set_irq_flags (irq, IRQF_VALID);
  }

  setup_irq (IRQ_GPIO3, &bcm2708_gpio_irq);  // 稍後說明
}



註冊GPIO IRQ 使用 request_irq() API

每一個IRQ Line都有了irq descriptor 之後, 就可以開始註冊每一個GPIO ISR , 可直接透過來完成。

 request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)


request_irq的第一個參數irq, 即是向系統的IDT(Interrupt Descriptor Table), (IDT為struct irq_desc的集合) 找到對應的irq_desc, 然後登記ISR,即 irq_desc->action=handler。例如,
request_irq(193, .....); 從IDT尋找179 是屬於GPIO interrupt chip controller, 所以一旦request_irq成功, 則IRQ 193 是掛在名為GPIO irq_chip, 而非ARMCTRL irq_chip。

誰來執行我們的GPIO IRQ Handler

在一個程式代碼中, 最後一行呼叫   setup_irq (IRQ_GPIO3, &bcm2708_gpio_irq); 其目的是向Interrupt controller Chip(ARMCTRL) 註冊IRQ 52 及其ISR bcm2708_gpio_irq 。
bcm2708_gpio_irq為一個 struct irqaction 結構, 其中 .handler指向一個 interrupt handler "bcm2708_gpio_interrupt" ,由此handler 來執行我的IRQ Handler。

bcm2708_gpio_interrupt 函式作法就是掃描 GPIO EDS Register , 若bit 為1, 則執行 generic_handle_irq (gpio_to_irq(gpio));  目的是透過 irq 去搜尋其 irq_desc, 然後再由irq_desc->action 呼叫對應註冊的ISR。struct irqaction為一個串列, 所以會不斷呼叫下去,直到有一個ISR 傳回IRQ_HANDLED。當IRQ為Shard IRQ 時,則發生此情況,若此次非由該裝置產生,則傳回IRQ_NONE, 但一定要有一個ISR傳IRQ_HANDLED,否則將會導致kernel pacnic.



static struct irqaction bcm2708_gpio_irq = {
  .name = "BCM2708 GPIO catchall handler",
  .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
  .handler = bcm2708_gpio_interrupt,
};

static irqreturn_t bcm2708_gpio_interrupt (int irq, void *dev_id)
{
  unsigned long edsr;
  unsigned bank;
  int i;
  unsigned gpio;
  for (bank = 0; bank <= 1; bank++) { //scan pending register (Event Detect Status)
  edsr = readl(__io_address(GPIO_BASE) + GPIOEDS(bank));
  for_each_set_bit(i, &edsr, 32) {     // i1bit位置處
  gpio = i + bank * 32;
  generic_handle_irq (gpio_to_irq(gpio)); //call GPIO ISR
      // clear pending regisgter
  writel(1<<i,__io_address(GPIO_BASE) + GPIOEDS(bank));
  }
  }
  return IRQ_HANDLED;
}


定義一個GPIO 中斷的irq chip

static struct irq_chip bcm2708_irqchip = {
 .name = "GPIO",
 .irq_enable = bcm2708_gpio_irq_unmask,  
.irq_disable = bcm2708_gpio_irq_mask,  
.irq_unmask = bcm2708_gpio_irq_unmask, 
.irq_mask = bcm2708_gpio_irq_mask,
 .irq_set_type = bcm2708_gpio_irq_set_type
};


說明: 宣告一個 struct irq_chip, 名稱為"GPIO", 並設定其相關底層的控制設定 ,如底下GPIO mask的程式, 即在對應的pin上. 對GPRENn GPFENn GPHENn, GPLENnBCM2835 設0。(ARM Peripherals Manual GPIO p.89)

static void bcm2708_gpio_irq_mask(struct irq_data *d)
{
  unsigned irq = d->irq;
  struct bcm2708_gpio *gpio = irq_get_chip_data(irq);
  unsigned gn = irq_to_gpio(irq);
  unsigned gb = gn / 32;
  unsigned long rising  = readl(gpio->base + GPIOREN(gb));
  unsigned long falling = readl(gpio->base + GPIOFEN(gb));
  unsigned long high    = readl(gpio->base + GPIOHEN(gb));
  unsigned long low     = readl(gpio->base + GPIOLEN(gb));
  gn = gn % 32;
  writel(rising  & ~(1 << gn), gpio->base + GPIOREN(gb));
  writel(falling & ~(1 << gn), gpio->base + GPIOFEN(gb));
  writel(high    & ~(1 << gn), gpio->base + GPIOHEN(gb));
  writel(low     & ~(1 << gn), gpio->base + GPIOLEN(gb));
}


DEMO


實作出來的結果, GPIO 控制器所產生的中斷, 是註冊在中斷控制器(ARMCTRL)的IRQ 號碼52 (gpio_int[3]), IRQ Handler 的名稱為 "BCM2708 GPIO catchall handler", 此IRQ Handler 再來scan GPIO controller 是那一根gpio pin# 產生中斷? 接著再呼叫該gpio pin#所註冊的ISR,此ISR即為
request_irq(193, irq_handler ,"my_button_int",..) 所註冊的ISR, 此中斷193號碼是掛在GPIO controller 的中斷。GPIO pin# IRQ 號碼為 170+GPIO Pin number。 另外, 因為所有GPIO 產生的中斷都是串接在中斷控制器(ARMCTRL)的, 故IRQ 52 的CPU中斷次數為個別GPIO 中斷次數的加總。

cat /proc/interrupts 輸出內容






2015年8月28日 星期五

Raspberry Pi 上的 Python 筆記 -- Tkinter


之前一開始學 Python 的時候就接觸過有關 GUI 的開發,不過那個時候因為還是程式語言初學者,連類別都還沒搞懂,想當然爾就不了了之。 最近則是要在 Raspberry Pi 上面用 GUI 介面的操作設計出來的系統,所以才又重新把目光放到Python的GUI函示庫。

上網找了一下,Python 大概有常見的GUI Framework 為  Tkinter、wxPython、pyQt 這三個。當初學的是 wxPython ,感覺還蠻簡單好用的。但就初學者而言,還是建議從 Tkinter 開始學起,因為其架構簡單好學,缺點就是功能上較沒有其他兩套這麼豐富,但就要建構一個簡單的像是控制系統而言,已經足夠了。而且是 Python 的標準套件庫,直接支援 Python2 和 Python3 ,安裝也較簡單,有時候甚至已經內建在其中了。

不過廢話不多說,下面就開始第一個連 Hello World 都不算是的範例程式碼:
-----------------------
-----------------------
先來看第一行程式碼

root = tk.Tk()

tk.Tk() 會回傳一個物件代表這個程式的主視窗,而我的習慣取名為 root 。這個主視窗物件有一個 .mainloop() 的method ,呼叫之後程式的進行就會進入一個無窮迴圈,並且卡在這裡,同時畫面上就會跳出來一個小視窗。這個就是這個程式的主視窗。


按右上的叉叉鈕,該程式就會停止。

接下來在這個主視窗內,放進去一個按鈕元件(button)

-------------------------
-------------------------



程式碼

button = tk.Button(master=root,text="hello", command=btn_call_back)

回傳了一個按鈕物件,其中將 root 物件用 "master" 參數傳入表示將這個 Button 物件 "掛" 在主視窗物件 "root" 的下面。Tkinter 裡的物件關係架構像是一個樹狀圖,每個物件都會有一個 "上層" 物件叫 master 。最上層的主視窗 (root) 為整個樹狀結構的最上層所以不用。

text 參數傳入一個字串,當做是按鈕上面顯示出來的文字。command 參數則傳入一個 function pointer (用 C 語言的說法,之後這樣的說法就叫 "慣C" 說法好了。),只要這個Button 被按下去,就會去執行該行程式碼。

<忽然覺得好累...待續...>


2015年8月25日 星期二

ADC 取樣後求Peak 值



一個類比訊號, 經由ADC 取樣並轉換後的數位值(raw data),以gnuplot 繪出後如下圖, 如何針對raw data 找出所有波峰值與波谷值 ? 





Sample.txt

2015年8月22日 星期六

程式打在MS power point上,令人討厭的幾件事!



每次用power point 時編輯程式教學投影時, 常常程式用的字串雙引號, 常會變成  “abcde”,我要的是 "abcde", 換了其他字型也沒用, 另一個問題是, 變數明明打的小寫命名, 每次變數第一個字母都自動轉成大寫, ...

有沒有辦法解決這個問題呢?

進入Power Point 取消自動轉換, 3個step 搞定...






2015年8月3日 星期一

Python程式-格式化輸出與字串處理 (三)


格式化輸出 Print: 結構語法基本上同C/C++語言printf


>>> a=4
>>> b=5

>>> print('a=%d'%a)
a=4
>>> print(a,'*',b,'=',a*b)
4 * 5 = 20

一個以上的%d 輸出
>>> print('%d*%d=%d'%(a,b,a*b))
4*5=20

# 多了和C不一樣的功能, 可以用dict的結構,用key找對應的value來輸出
>>> print('%(#2)d*%(#1)d=%(#v)d'%{'#1':a,'#2':b,'#v':a*b})
5*4=20


>>> print('a=%d'%a)
a=4
>>> print('a=%10d'%a)  # 空10格
a=         4
>>> print('a=%010d'%a)  # 空10格且前面補0
a=0000000004

卬浮點數 %f
>>> money=172.85
>>> print('%f',money)
%f 172.85

>>> print('%f'%money)
172.850000

>>> print('$%6.2f''%money)
$172.85

>>> print('$%*.2f'%(10,money)) # 空10格
$         172.85

字串函數


>>> text='it robotic lab'
>>> text.capitalize()
'It robotic lab'
>>> text
'it robotic lab'

>>> text=text.capitalize()  #存回text
>>> text
'It robotic lab'