W!o+ 的《小伶鼬工坊演義》︰ 一窺全豹之系統設計‧探索開始

W!o+ 認為一個『實驗』的『結束』,也只是『探索』之『開始』。或許舊的疑惑未解!新的疑惑又生!!因此『分析』結果,『整理 』資料 ,『思辨』要點,『綜合』通盤 …… ,『準備』下次『實驗 』計畫 ,正是『科學求知』的方法。………

固然總有『不知』的時候?總有『不能』的情況??此時恰好困知勉行乎!也許終有『知之』的一天吧!?

之前的實驗結果,一起頭就叫人訝異︰

# 關閉電源,重新開機。確定 Arduino 在 Hard Reset 狀態。
pi@raspberrypi ~/example $ python
Python 2.7.9 (default, Mar  8 2015, 00:52:26) 
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import grovepi
>>> addr = 0x05

# 韌體 Wire 程式庫,如何處理『未下指令』之 I2C 『讀取』

# 位元組讀取
>>> grovepi.read_i2c_byte(addr)
0
# 區塊讀取
>>> grovepi.read_i2c_block(addr)
[11, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255]
# 長度 32 ?
>>> len(grovepi.read_i2c_block(addr))
32

# Grove – Rotary Angle Sensor 測試。命令 3
>>> potentiometer = 0
# 設定 pinMode 為輸入。命令 5
>>> grovepi.pinMode(potentiometer,"INPUT")
1
# 讀取值
>>> sensor_value = grovepi.analogRead(potentiometer)
0
[11, 2, 191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255]
# 傳回值
>>> sensor_value
703
………

 

。就讓我們從此處展開探索之旅耶!!

先匯總『問題』如下︰

‧ 為什麼『未下指令』,還能無誤讀取?

‧ 為什麼『位元組讀取』,傳回值為 0 ?

‧ 為什麼『區塊讀取』,傳回長度是 32 ?

‧ 為什麼『Grove – Rotary Angle Sensor』測試,序列埠沒有輸出?

‧………

 

經過多次查詢以及閱讀,『整理』與之相關資料如下︰

☆【 ATmega CPU DataSheet 】

ATmega48A/PA/88A/PA/168A/PA/328/P

第二零六頁

22. 2-wire Serial Interface
22.1 Features
‧ Simple Yet Powerful and Flexible Communication Interface, only ‧ ‧ ‧ ‧ two Bus Lines Needed
‧ Both Master and Slave Operation Supported
‧ Device can Operate as Transmitter or Receiver
‧ 7-bit Address Space Allows up to 128 Different Slave Addresses
‧ Multi-master Arbitration Support
‧ Up to 400kHz Data Transfer Speed
‧ Slew-rate Limited Output Drivers
‧ Noise Suppression Circuitry Rejects Spikes on Bus Lines
‧ Fully Programmable Slave Address with General Call Support
‧ Address Recognition Causes Wake-up When AVR is in Sleep Mode
‧ Compatible with Philips’ I2C protocol

……

從屬接收模式

Atmegas328p-I2C-Slave-Receiver-Mode

 

從屬傳送模式

Atmegas328p-I2C-Slave-Transmitter-Mode

 

☆ pi@raspberrypi /usr/share/arduino/libraries/Wire/utility $ ls
twi.c twi.h
pi@raspberrypi /usr/share/arduino/libraries/Wire/utility $ pwd
/usr/share/arduino/libraries/Wire/utility

 

/*
 twi.c - TWI/I2C library for Wiring & Arduino
 Copyright (c) 2006 Nicholas Zambetti. All right reserved.

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.

 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

 Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts
*/



   // Slave Receiver
    case TW_SR_SLA_ACK:   // addressed, returned ack
    case TW_SR_GCALL_ACK: // addressed generally, returned ack
    case TW_SR_ARB_LOST_SLA_ACK:   // lost arbitration, returned ack
    case TW_SR_ARB_LOST_GCALL_ACK: // lost arbitration, returned ack
      // enter slave receiver mode
      twi_state = TWI_SRX;
      // indicate that rx buffer can be overwritten and ack
      twi_rxBufferIndex = 0;
      twi_reply(1);
      break;
    case TW_SR_DATA_ACK:       // data received, returned ack
    case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack
      // if there is still room in the rx buffer
      if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){
        // put byte in buffer and ack
        twi_rxBuffer[twi_rxBufferIndex++] = TWDR;
        twi_reply(1);
      }else{
        // otherwise nack
        twi_reply(0);
      }
      break;
    case TW_SR_STOP: // stop or repeated start condition received
      // put a null char after data if there's room
      if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){
        twi_rxBuffer[twi_rxBufferIndex] = '\0';
      }
      // sends ack and stops interface for clock stretching
      twi_stop();
# 重點
      // callback to user defined callback
      twi_onSlaveReceive(twi_rxBuffer, twi_rxBufferIndex);
      // since we submit rx buffer to "wire" library, we can reset it
      twi_rxBufferIndex = 0;
      // ack future responses and leave slave receiver state
      twi_releaseBus();
      break;
    case TW_SR_DATA_NACK:       // data received, returned nack
    case TW_SR_GCALL_DATA_NACK: // data received generally, returned nack
      // nack back at master
      twi_reply(0);
      break;


    // Slave Transmitter
    case TW_ST_SLA_ACK:          // addressed, returned ack
    case TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack
      // enter slave transmitter mode
      twi_state = TWI_STX;
      // ready the tx buffer index for iteration
      twi_txBufferIndex = 0;
      // set tx buffer length to be zero, to verify if user changes it
      twi_txBufferLength = 0;
      // request for txBuffer to be filled and length to be set
# 重點
      // note: user must call twi_transmit(bytes, length) to do this
      twi_onSlaveTransmit();

# 要點
      // if they didn't change buffer & length, initialize it
      if(0 == twi_txBufferLength){
        twi_txBufferLength = 1;
        twi_txBuffer[0] = 0x00;
      }
      // transmit first byte from buffer, fall
    case TW_ST_DATA_ACK: // byte sent, ack returned
      // copy data to output register
      TWDR = twi_txBuffer[twi_txBufferIndex++];
      // if there is more to send, ack, otherwise nack
      if(twi_txBufferIndex < twi_txBufferLength){
        twi_reply(1);
      }else{
        twi_reply(0);
      }
      break;



/* 
 * Function twi_attachSlaveRxEvent
 * Desc sets function called before a slave read operation
 * Input function: callback function to use
 * Output none
 */
void twi_attachSlaveRxEvent( void (*function)(uint8_t*, int) )
{
 twi_onSlaveReceive = function;
}

/* 
 * Function twi_attachSlaveTxEvent
 * Desc sets function called before a slave write operation
 * Input function: callback function to use
 * Output none
 */
void twi_attachSlaveTxEvent( void (*function)(void) )
{
 twi_onSlaveTransmit = function;
}

 

☆ 【 Arduino Wire 程式庫】深度闡釋

:: Wire Library, Explored

An indepth reference to the Arduino Wire library.

 

☆ 『 Linux/Documentation/i2c/smbus-protocol

I2C Block Transactions
======================

The following I2C block transactions are supported by the SMBus layer and are described here for completeness. They are *NOT* defined by the SMBus specification.

I2C block transactions do not limit the number of bytes transferred but the SMBus layer places a limit of 32 bytes.

I2C Block Read: i2c_smbus_read_i2c_block_data()
================================================

This command reads a block of bytes from a device, from a designated register that is specified through the Comm byte.

S Addr Wr [A] Comm [A]
S Addr Rd [A] [Data] A [Data] A … A [Data] NA P

Functionality flag: I2C_FUNC_SMBUS_READ_I2C_BLOCK

I2C Block Write: i2c_smbus_write_i2c_block_data()
==================================================

The opposite of the Block Read command, this writes bytes to a device, to a designated register that is specified through the Comm byte. Note that command lengths of 0, 2, or more bytes are supported as they are indistinguishable from data.

S Addr Wr [A] Comm [A] Data [A] Data [A] … [A] Data [A] P

Functionality flag: I2C_FUNC_SMBUS_WRITE_I2C_BLOCK

 

☆ 『 i2c-tools_3.1.0.orig.tar.bz2

/*
 * smbusmodule.c - Python bindings for Linux SMBus access through i2c-dev
 * Copyright (C) 2005-2007 Mark M. Hoffman <mhoffman@lightlink.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */


PyDoc_STRVAR(SMBus_read_i2c_block_data_doc,
 "read_i2c_block_data(addr, cmd, len=32) -> results\n\n"
 "Perform I2C Block Read transaction.\n");

static PyObject *
SMBus_read_i2c_block_data(SMBus *self, PyObject *args)
{
# 要點
 int addr, cmd, len=32;
 union i2c_smbus_data data;

 if (!PyArg_ParseTuple(args, "ii|i:read_i2c_block_data", &addr, &cmd,
 &len))
 return NULL;

 SMBus_SET_ADDR(self, addr);

 data.block[0] = len;
 /* save a bit of code by calling the access function directly */
 if (i2c_smbus_access(self->fd, I2C_SMBUS_READ, (__u8)cmd,
 len == 32 ? I2C_SMBUS_I2C_BLOCK_BROKEN:
 I2C_SMBUS_I2C_BLOCK_DATA, &data)) {
 PyErr_SetFromErrno(PyExc_IOError);
 return NULL;
 }

 /* first byte of the block contains (remaining) data length */
 return SMBus_buf_to_list(&data.block[1], data.block[0]);
}

PyDoc_STRVAR(SMBus_write_i2c_block_data_doc,
 "write_i2c_block_data(addr, cmd, [vals])\n\n"
 "Perform I2C Block Write transaction.\n");

static PyObject *
SMBus_write_i2c_block_data(SMBus *self, PyObject *args)
{
 int addr, cmd;
 union i2c_smbus_data data;

 if (!PyArg_ParseTuple(args, "iiO&:write_i2c_block_data", &addr, &cmd,
 SMBus_list_to_data, &data))
 return NULL;

 SMBus_SET_ADDR(self, addr);

 /* save a bit of code by calling the access function directly */
 if (i2c_smbus_access(self->fd, I2C_SMBUS_WRITE, (__u8)cmd,
 I2C_SMBUS_I2C_BLOCK_BROKEN, &data)) {
 PyErr_SetFromErrno(PyExc_IOError);
 return NULL;
 }

 Py_INCREF(Py_None);
 return Py_None;
}

 

同時也參考他人之討論︰

☆ 『  [I2C] onRequest and onReceive as slave with Raspberry Pi Master

Sep 10, 2013, 06:39 pmLast Edit: Sep 10, 2013, 06:57 pm by brainvoid Reason: 1
 
Hey guys,
I discovered a big Problem for my Project today. I try to connect a few Arduinos to a Raspberry Pi with I2C. I have a onRequest and a onReceive function (code below). On my Raspberry Pi I use smbus with python.My onReceive function works fine and well, every “bus.write[…]” function calls my onReceive function as it should.
But not onRequest. Only when i call “read_byte(addr)” in my python script, the Arduino will call the onRequest function. But it is necessary that I can use at least “byte read_byte_data(addr,cmd)” or “long[] read_block_data(addr,cmd)”. These functions call the onReceive Method, whyever.So I thought “no Problem”, just use onReceive like onRequest and write data there. But then the BUS is corrupted and the Python on Raspberry Pi gets an I/O Error.I just recognized that with the command “print bus.read_i2c_block_data(0x04, 0x01)” it prints nearly the right output (take a look in my log below), but on my Arduino Serial connection the “dataRequest() called with command” is not printed. I just don’t understand o_OI don’t know what I’m doing wrong o_O. Can someone explain this?Best regards
brainvoid

───

 

請判斷,這是否足以『回答』上述『問題』?能夠『準備』新實驗『計劃』的呢??