先快速check 一下你的i2c device有沒有連接上
先安裝必要的套件
sudo apt-get install python-smbus
sudo apt-get install i2c-tools
然後確定你的i2c port 有被打開
如果你 run的是 Raspiban ...請check
/etc/modprobe.d/raspi-blacklist.conf
並且把 blacklist i2c-bcm2708 這行給Mark
如果你用的os 是 Whzeezy 或是不同於 Occidentali...你還需要在 /etc/modules
裡面加入下列兩行
i2c-dev
i2c-bcm2708
輸入
sudo i2cdetect -y 1
就會得到下列這些資料
這是一個快速查詢你的i2c device有沒有連接上去....也check 腳位有沒有接錯
i2c 一些基本操作
Hipi::Device::I2C 提供和i2c 裝置溝通
你也可以動態的載入i2c device driver
modprobe i2c_bcm2708 modprobe i2c_dev
這樣的好處是不是root 的使用者也可以使用這個裝置...
/dev/i2c-1 是預設的device 裝置...使用的是 GPIO PAD1 pins.....
/dev/i2c-0 是使用 GPIO PAD5 pins
/dev/i2c-0 /dev/i2c-1
如果你想改變i2c 的baudrate..你可以用 modprobe 的時候來指定
modprobe i2c_bcm2708 baudrate=32000
然後你可以查看 /etc/modprobe.d ....你會看到有多了這一行
options i2c_bcm2708 baudrate=32000
bcm2835 i2c hw
首先來看看這幾行static struct platform_driver bcm2835_i2c_driver = { .probe = bcm2835_i2c_probe, .remove = bcm2835_i2c_remove, .driver = { .name = "i2c-bcm2835", .of_match_table = bcm2835_i2c_of_match, }, }; module_platform_driver(bcm2835_i2c_driver);建立了一個 名字為 bcm2835_i2c_driver 的 platform_drvier struct..其中 probe -> bcm2835_i2c_proberemove -> bcm2835_i2c_remove比較重要...接下來先來看 bcm2835_i2c_removestatic int bcm2835_i2c_remove(struct platform_device *pdev) { struct bcm2835_i2c_dev *i2c_dev = platform_get_drvdata(pdev); free_irq(i2c_dev->irq, i2c_dev); i2c_del_adapter(&i2c_dev->adapter); return 0; }最主要的就是把 irq給 release..... 然後把 i2c delete...接下來再來看 bcm2835_i2c_probe由於行數太多...我就不一一的貼出來....i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);用google 查了一下 devm_kzalloc...他的意思如下Memory allocated with this function is automatically freed on driver detach意思是說用這種function 配置出來的memory會被自動地釋放當device 拔除時...所以這行的意思是說...就是去要求一段核心的記憶體...其大小是 sizeof(*i2c_dev)接下來是把設備的相關信息放到設備結構裡,需要使用的時候可以方便的拿出來大概就類似這樣static inline void dev_set_drvdata(struct device *dev, void *data) { dev->driver_data = data; }接下來就是要求 i2c 的register base... 放在變數 i2c_dev->regsi2c_dev->clk = devm_clk_get(&pdev->dev, NULL);ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", &bus_clk_rate);divider = DIV_ROUND_UP(clk_get_rate(i2c_dev->clk), bus_clk_rate);接下來就是 寫入硬體的reg.....bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DIV, divider);從 bcm2835.c 可以查到#define BCM2835_I2C_DIV 0x14從HW 的datasheet來看....就是把 Clock Divider的值存進去....公式為 SCL = core clock/ CDIV再來看 bcm2835_i2c_writel 的實作
static inline void bcm2835_i2c_writel(struct bcm2835_i2c_dev *i2c_dev, u32 reg, u32 val) { writel(val, i2c_dev->regs + reg); }就是把 divider的值寫入到 BCM2835_I2C_DIV所指的register....接下來就是去要irq.....irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);ret = request_irq(i2c_dev->irq, bcm2835_i2c_isr, IRQF_SHARED, dev_name(&pdev->dev), i2c_dev);當i2c 產生中斷時....會跳到 bcm2835_2ic_isr 的function 去執行adap = &i2c_dev->adapter; i2c_set_adapdata(adap, i2c_dev);adap->algo = &bcm2835_i2c_algo;static const struct i2c_algorithm bcm2835_i2c_algo = { .master_xfer = bcm2835_i2c_xfer, .functionality = bcm2835_i2c_func, };跟系統告知說..當要執行master_xfer -> bcm2835_i2c_xferfunctionality -> bcm2835_i2c_funcbcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, 0);#define BCM2835_I2C_C 0x0其用意就是先全部disable....ret = i2c_add_adapter(adap);查了一下google....大概就是把adapter 加進去這些大概就是 i2c_probe 的 主要code....接下來看當中斷發生時會處理甚麼事情 bcm2835_i2c_isr()static irqreturn_t bcm2835_i2c_isr(int this_irq, void *data) { struct bcm2835_i2c_dev *i2c_dev = data; u32 val, err; val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S); val &= BCM2835_I2C_BITMSK_S; bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_S, val); err = val & (BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR); if (err) { i2c_dev->msg_err = err; complete(&i2c_dev->completion); return IRQ_HANDLED; } if (val & BCM2835_I2C_S_RXD) { bcm2835_drain_rxfifo(i2c_dev); if (!(val & BCM2835_I2C_S_DONE)) return IRQ_HANDLED; } if (val & BCM2835_I2C_S_DONE) { if (i2c_dev->msg_buf_remaining) i2c_dev->msg_err = BCM2835_I2C_S_LEN; else i2c_dev->msg_err = 0; complete(&i2c_dev->completion); return IRQ_HANDLED; } if (val & BCM2835_I2C_S_TXD) { bcm2835_fill_txfifo(i2c_dev); return IRQ_HANDLED; } return IRQ_NONE; }第一步 先 check register BCM2835_I2C_S(0x4)val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S); val &= BCM2835_I2C_BITMSK_S;bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_S, val);把status 讀出來放在 val...接著把 Register BCM2835_I2C_S reset#define BCM2835_I2C_BITMSK_S 0x03FF接下來就是check 各個 bitval & BCM2835_I2C_S_RXD -> bcm2835_drain_rxfifo(i2c_dev);val & BCM2835_I2C_S_TXD -> bcm2835_fill_txfifo(i2c_dev);val & (BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR) -> errorval & BCM2835_I2C_S_DONE -> complete(&i2c_dev->completion);
接下來看怎麼傳送資料 bcm2835_fill_txfifo()static void bcm2835_fill_txfifo(struct bcm2835_i2c_dev *i2c_dev) { u32 val; while (i2c_dev->msg_buf_remaining) { val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S); if (!(val & BCM2835_I2C_S_TXD)) break; bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_FIFO, *i2c_dev->msg_buf); i2c_dev->msg_buf++; i2c_dev->msg_buf_remaining--; } }用一個while loop來check 要傳送的msg還有沒有....一開始先呼叫 bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);#define BCM2835_I2C_S 0x4#define BCM2835_I2C_S_TXD BIT(4)然後取出 I2C_TXD 的bit -> val & BCM2835_I2C_S_TXD從下圖的datasheet可以看出....bit 4 的意思是Fifo can accepts data0 -> fifo full.... 1 -> FiFo has space to accept data*i2c_dev->msg_buf);#define BCM2835_I2C_FIFO 0x10然後就把資料寫到 BCM2835_I2C_FIFO register裡面接下來看怎麼接收資料 bcm2835_drain_rxfifo()static void bcm2835_drain_rxfifo(struct bcm2835_i2c_dev *i2c_dev) 99 { 100 u32 val; 101 102 while (i2c_dev->msg_buf_remaining) { 103 val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S); 104 if (!(val & BCM2835_I2C_S_RXD)) 105 break; 106 *i2c_dev->msg_buf = bcm2835_i2c_readl(i2c_dev, 107 BCM2835_I2C_FIFO); 108 i2c_dev->msg_buf++; 109 i2c_dev->msg_buf_remaining--; 110 } 111 }一樣是看i2c_dev-> msg_buf_remaining 的數值...如果不為零...代表有值...去i2c register讀出來...讀data之前...要先check BCM2835_I2C_S_RXD#define BCM2835_I2C_S_RXD BIT(5)0 -> fifo is empty.... 1-> Fifo contains at least one byte...然後read BCM2835_I2C_FIFO register....去把data 讀出來...接下來看function bcm2835_i2c_func()
static u32 bcm2835_i2c_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; }回傳系統預設得 I2C_FUNC_I2C 和 I2C_FUNC_SMBUS_EMUL接下來看master_xfer bcm2835_i2c_xfer()
static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { struct bcm2835_i2c_dev *i2c_dev = i2c_get_adapdata(adap); int i; int ret = 0; for (i = 0; i < num; i++) { ret = bcm2835_i2c_xfer_msg(i2c_dev, &msgs[i]); if (ret) break; } return ret ?: i; }其實就是呼叫 bcm2835_i2c_xfer_msg 去check message...
Reference : https://learn.adafruit.com/adafruit-16-channel-servo-driver-with-raspberry-pi/configuring-your-pi-for-i2c
Reference : http://skpang.co.uk/blog/archives/575
Reference : http://raspberrypi.znix.com/hipidocs/topic_i2cdev.htm
Reference : Reading I2C Inputs using C
Reference : https://learn.sparkfun.com/tutorials/raspberry-pi-spi-and-i2c-tutorial
Reference : http://www.airspayce.com/mikem/bcm2835/
Reference : https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
Reference : bcm2835.h
Reference : i2c-bcm2835.c
Reference : http://mark0522.blogspot.tw/2014/08/i2c1.html
沒有留言:
張貼留言