2016年1月22日 星期五

核心空間配置記憶體 kmalloc() 和 kfree()


void *kmalloc(size_t size, gfp_t flags)

kmalloc一次最多能申請的記憶體大小由include/Linux/Kmalloc_size.h的內容來決定,
kmalloc一次最多能申請大小為131702B也就是128KB的連續物理記憶體。


flags 的參數如下:

GFP_ATOMIC : 用於interrupt handler 或任何行程外環境的程式..絕不休眠
GFP_KERNEL : 一般核心記憶體配置..有可能休眠
GFP_USER     : 配置記憶體給user-sapce...有可能休眠
GFP_HIGHUSER : 類似 GFP_USER. 但是從高位址區取得空間...
還有一些... 我就沒有打了

高位址區(High memory address) 是一種可供32 bit平台存取大量記憶體的機制...
kmalloc() 能夠配置的記憶體容量是有上限的(因為配置出來的空間是連續的實體記憶體...這樣的空間很少)
,而且上限取決硬體平台與核心組態選項...如果你想維持程式的可移植性...就別期待能夠配置超過128KB的記憶體 ....

核心空間的cache用法如下
kmem_cache_t *kmem_cache_create(name, size, offset, flags)

name : 通常是一串簡短的文字串
offset : 是第一個物件在記憶頁的起始位置....
flag :     
      SLAB_NO_REAP : 保護cache不因系統短缺記憶體而縮減其容量
      SLAB_HWCACHE_ALIGN: 此旗標要求每個資料物件都要對齊一條塊取線....
      SLAB_CACHE_DMA : 要求每一個資料物件都配置在DMA可存取的記憶體

成功建立cache後  妳可以呼叫kmem_cache_alloc為配置cache

void * kmem_cache_alloc(*cache, flags)....

void *vmalloc(unsigned long size).... (v 是virtual的意思)
其作用為在虛擬位址空間(virtual space)配置一段連續的記憶體...
vmalloc() 所得到的每一頁的記憶體...都是個別呼叫alloc_page()所得來的...
在某些場合中,對記憶體區的請求不是很頻繁,較高的記憶體訪問時間也可以接受,
這樣就可以分配一段線性連續,物理不連續的地址,帶來的好處是一次可以分配較大塊的記憶體。
vmalloc對一次能分配的記憶體大小沒有明確限制。出於性能考慮,應謹慎使用vmalloc函數


void * ioremap (unsigned long offset, unsigned long size)
  ioremap是一種更直接的記憶體分配方式,使用時直接指定物理起始地址和需要分配記憶體的大小,
然後將該段物理地址映射到核心地址空間。
ioremap用到的物理地址空間都是事先確定的,和上面的幾種記憶體分配方式並不太一樣。
並不是分配一段新的物理記憶體。
ioremap多用於設備驅動,可以讓CPU直接訪問外部設備的IO空間。
ioremap能映射的記憶體由原有的物理記憶體空間決定,所以沒有進行測試。


get_zero_page(unsigned int flags)
       回傳一個指向新記憶頁的指標...而page的內容全部都是0
__get_free_page(unsigned int flags)
       類似於get_zero_page() , 但是不清除原來的內容
__get_free_page(unsigned int flags,unsigned int order)
      配置一個cross-page的連續記憶體(在實體記憶體上是連續的)...並傳回一個指向記憶體第一個位元組的  
      指標

這裡的flag 理論上和 kmalloc() 所使用的flag相同... 通常以 GFP_KERNEL和 GFP_ATOMIC最為常使用


Reference : Linux 驅動程式第三版
Reference : http://welkinchen.pixnet.net/blog/post/44174948-%E5%B8%B8%E7%94%A8%E6%A0%B8%E5%BF%83%E8%A8%98%E6%86%B6%E9%AB%94%E5%88%86%E9%85%8D%E5%87%BD%E6%95%B8


grove dht sensor (溫度..濕度)



雖然說對這顆sensor 已經很熟...不過這次買的starter kit 裡面有...也就順便study一下

原理和protocol 都和以前一樣...只是它包好了一個function...你可以直接呼叫去讀值

直接看sample code
# module_type:
#             DHT11 0
#             DHT22 1
#             DHT21 2
#             DHT2301 3


[ temp,hum ] = dht(dht_sensor_port,0)  #Get the temperature and Humidity from the DHT sensor

其實就一行code....

dht(port_num, module_type)

module  的數值有四種.....由於我們的是DHT11 ....所以填0...

然後直接就把   溫度和濕度放在變數  temp 和 hum...很方便吧.....

當然我們還是來看看它是怎麼實做出來的....

整個 source code在Reference 2可以看到

// pull the pin high and wait 250 milliseconds
digitalWrite(_pin, HIGH);
delay(250);

一開始....先把  GPIO 拉High 維持 250ms

data[0] = data[1] = data[2] = data[3] = data[4] = 0;

先把data[0] 到 data[4] 初始化

// now pull it low for ~20 milliseconds
pinMode(_pin, OUTPUT);
digitalWrite(_pin, LOW);
delay(20);
digitalWrite(_pin, HIGH);
delayMicroseconds(40);
pinMode(_pin, INPUT);

然後再拉Low ....維持 20 ms

然後再拉High....維持 40  us.....

接下來把pin改成input mode...

define MAXTIMINGS 85


for ( i=0; i< MAXTIMINGS; i++) {
counter = 0;
while (digitalRead(_pin) == laststate) {
counter++;
delayMicroseconds(1);
if (counter == 255) {
break;
}
}
laststate = digitalRead(_pin);
if (counter == 255) break;
// ignore first 3 transitions
if ((i >= 4) && (i%2 == 0)) {
// shove each bit into the storage bytes
data[j/8] <<= 1;
if (counter > _count)
data[j/8] |= 1;
j++;
}
}

他用一個for loop.....  最多跑85次

接下用一個 while loop...一直去check 現在讀到的value和之前讀到的value有沒有一樣
如果有一樣...就去睡覺1us....讓cpu可以去做其他事情
這個while loop會check到現在讀出來的level和上次是不一樣的
counter紀錄這段時間有多久...單位us

他這邊有個check error的機制就是當時間已經超過 255us...

它就會終止迴圈....然後認為這次的傳輸是失敗的

counter=0
while (digitalRead(_pin) == laststate){
counter++;
delayMicroseconds(1);}

if (counter == 255) break;

接下來就是去check 這次的counter有多久...

如果counter大於 _count....就為這次的傳輸是傳 '1'
如果counter沒有大於_count...代表這次的傳輸是傳'0'...也就是不需要做任何事

然後由於他是MSB先傳....所以要做bit 的shift....

至於_count是一開始物件初始化的時候會決定 .....datasheet來看...應該是30或是40

然後前四個傳輸不要管....從第5個(i>=4)開始看...而且只看偶數(i%2)......

因為我們觸發的條件是edge...但是一次傳輸有兩個edge...

但我有點搞不懂是...為什麼前四個傳輸不看

if ((i >= 4) && (i%2 == 0)) {
data[j/8] <<= 1;
if (counter > _count)
data[j/8] |= 1;
j++;}



最後再check CRC
// check we read 40 bits and that the checksum matches
if ((j >= 40) && (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) ) {
return true; }

check CRC的方式也和之前一樣....

大體上來說和之前實作的dht11一樣....




Reference 1 : http://www.seeedstudio.com/wiki/Grove_-_Temperature_and_Humidity_Sensor

Reference 2 : https://github.com/Seeed-Studio/Grove_Temperature_And_Humidity_Sensor/blob/master/DHT.cpp

grove sound sensor



從他的wiki來看....它其實就是一個微型的麥克風 加上 LM358去放大...

然後輸出就是類比信號....

直接看sample code吧

# Read the sound level
        sensor_value = grovepi.analogRead(sound_sensor)

因為是類比信號....其實就直接呼叫 grovepi.analogRead 去讀值就好啦...



Reference :  http://www.seeedstudio.com/wiki/Grove_-_Sound_Sensor

grovepi light sensor (Light Dependent Resistor)


這個sensor也是蠻好玩的....它的輸出電阻和他所感受的光源成反比...

從他的wiki來看就知道

Resistance decreases as luminance increases

從他的spec 來看

Specifications

  • Voltage: 3-5V
  • Supply Current: 0.5-3mA
  • Light resistance: 20KΩ
  • Dark resistance: 1MΩ
  • Response time: 20-30 secs
  • Peak Wavelength: 540 nm
  • Ambient temperature: -30~70℃

最亮時...輸出電阻為  20KO...  
最暗時...輸出電阻為  1MO...

所以其實就只要去讀他的類比輸出就可以了....

由於我們有grovepi 的shield...他上面的IC就有類比輸入.....所以我們只要呼叫

analogRead() 去把值讀回來.....

因為sensor_value的數值為 0~1023..

然後帶上他的公式.... 轉換成K歐姆....


        # Get sensor value
        sensor_value = grovepi.analogRead(light_sensor)

        # Calculate resistance of sensor in K
        resistance = (float)(1023 - sensor_value) * 10 / sensor_value


但如果以後要自己實做的話...因為Raspberry 沒有類比輸入輸出....所以考慮用mcp3002 來用...



Grovepi Ultrasonic Ranger 的詳細研究


這顆sensor 蠻好玩的... 它可以用超音波的量測方式...來測量在他前面的障礙物的距離....


然後它所提供的sample code非常簡單...

# GrovePi + Grove Ultrasonic Ranger
 
from grovepi import *
 
# Connect the Grove Ultrasonic Ranger to digital port D4
# SIG,NC,VCC,GND
 
ultrasonic_ranger = 4
 
while True:
    try:
        # Read distance value from Ultrasonic
        print ultrasonicRead(ultrasonic_ranger)
 
    except TypeError:
        print "Error"
    except IOError:
        print "Error"


他就直接呼叫  ultrasonicRead(port_num);

就可以得到距離值....用是很好用....不過以後要移植平台時...這樣就不行了

所以還是來研究一下他去怎麼實作的

從他的連結所下載的ultrasonic.zip來看

它提供了兩個function....

一種是 measure in centimeter.....
一種是 measure in inch

那來看看他的實作 ....

/*The measured distance from the range 0 to 400 Centimeters*/
void Ultrasonic::MeasureInCentimeters(void)
{
pinMode(_pin, OUTPUT);
digitalWrite(_pin, LOW);
delayMicroseconds(2);
digitalWrite(_pin, HIGH);
delayMicroseconds(5);
digitalWrite(_pin,LOW);
pinMode(_pin,INPUT);
duration = pulseIn(_pin,HIGH);
RangeInCentimeters = duration/29/2;
}

從這段code來看....

1.set low 2ms
2. set high 5ms
3.  然後set low之後...設成input mode
4.  接下來量取 ultrasonic 這次傳輸(拉high一段時間...再拉low)....level為high的時間為多少
5.   得到的答案就是   duration_time /29/2


   其實它的原理就是用超音波的速度*時間來得到 距離...由於時間是算來回的時間

  所以要除以2......

  音波的速度為 每秒 330公尺...這是一個大概值....以公式來說就是  331.5 + 0.6*溫度

   如果以室溫20度來說....速度大概就是  331.5*0.6*20 = 343 (m/s)

   從上段的code來看....  pulseIn 所回傳的單位是  微秒(10^-6)

   t*10^-6 * 343 /2*100 = t*0.0343/2 = t/29/2

  公式就是這樣來的....

 





Reference 1: http://www.seeedstudio.com/wiki/Grove_-_Ultrasonic_Ranger

Reference 2: http://www.seeedstudio.com/wiki/images/9/95/Ultrasonic.zip

Reference 3: https://developer.mbed.org/cookbook/Seeed-grove-ultrasonic-ranger

Reference 4: Make :感測器運用Arduino 和Raspberry Pi 感測的專題與實驗