2014年9月24日 星期三

The Software for Minimal I2C

http://boldinventions.com/index.php?option=com_content&view=article&id=52:i2csimpleimplement&catid=34:category-electronics-articles&Itemid=53

The Software for Minimal I2C 

The Software for Minimal I2C 

Are you an I2C noob?  If so, check out this article before trying to port the source code.  
Before we actually get into the software, these diagrams show how the bus is hooked up to the CPU.
I2C Connection to a CPUIf the CPU has the capability of driving open collector output lines and reading their state directly, then the I2C hardware connection couldn't be easier. The 5k pullup resistors make the two bus lines float high if no device on the bus is pulling them low. There is a tradeoff between the resistor value and the capacitance load on the lines. If the resistance is too high, then more devices on the bus tend to increase capacitance, and the signals are less square, leading eventually to failures. If the resistance is too low, then the weakest devices on the bus will not be able to sink enough current to drive the lines to logical 0.





I2C Seperate In and Out ConnectionsIf open collector lines are not available, it would be possible to use 2 outputs, and 2 input lines to accomplish the same thing. The diodes simply prevent the Out0 and Out1 output lines from driving the I2C lines high.






C - language Source code for Minimal I2C

The following source code is an example of a minimal I2C implementation.  It was actually translated from FORTH, so this may contain some small mistakes.

CPU-Dependent Primitives

The following bits are implementation-dependent code, which will vary depending on which CPU is being used.

#define SCLOUT( val )      set_p0_state( val )    /* CPU Dependent function to set p0 to low-state val=0, or float val=1 */
#define SDAOUT( val )      set_p1_state( val )  
#define SCL_IN             get_p0_state()         /* Returns 1 if p0 is high, or 0 if p0 is low. */
#define SDA_IN             get_p1_state()

#define MSEC_DELAY(usec)   cpu_dependent_delay(usec)  /* Waste time for some microseconds (not milliseconds) */

typedef unsigned char BYTE
 

Initialization

The first software step is to initialize the io ports to be open collector outputs, and then put the bus into an idle state. An idle state is where both lines are high. It may also be a good idea to generate aSTOP condition on the bus before putting it idle.
void i2c_setup()
{
    set_p0_open_collector();   /* Implementation dependent - must be made to match your CPU */
    set_p1_open_collector();

    SCLOUT(0);       /* Generate a STOP condition */
    SDAOUT(0);
    SCLOUT(1);       /* SCL is high */
    MSEC_DELAY(10);  /* Wait */
    SDAOUT(1);       /* Now cause a low-to-high transistion on SDA */
}

I2C Start and Stop Conditions

The following code implements the I2C Start and Stop conditions using the primitives defined above.
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\ void i2c_do_stop(void)   
\\  puts data low, then clock low,
\\  then clock high, then data high.
\\  This should cause a stop, which
\\  should idle the bus, I.E. both clk and data are high.
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

void i2c_do_stop(void)
{
  SCLOUT(0);
  MSEC_DELAY(10);
  SDAOUT(0);
  MSEC_DELAY(10);
  SCLOUT(1);
  MSEC_DELAY(10);
  SDAOUT(1);
}

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\ void i2c_do_start(void)
\\   Sets clock high, then data high.  This will do a stop if data was low.
\\   Then sets data low, which should be a start condition.
\\   After executing, data is left low, while clock is left high
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
void i2c_do_start(void)
{
   SCLOUT(1);
   SDAOUT(1);
   MSEC_DELAY(10);
   SDAOUT(0);
   MSEC_DELAY(10); \\ waste time
}

 
 Next - More I2c Source Code


 
Last Updated on Sunday, 17 May 2009 13:52

2014年9月19日 星期五

[基礎程式語法]第十四章 副程式與函式

參考:http://home.educities.edu.tw/mariorpg/C++/c114.html

1.副程式概論:
之前我們寫的程式都在主程式(main)的範圍內,不過完整的程式檔案可不只它而已,這章將介紹主程式的幫手-副程式!

副程式跟主程式一樣的重要,差別只在於它不會像主程式一樣的自動執行,在呼叫(主程式或別的副程式皆可)時才會去執行。其實,我們之前寫的程式中已經用過副程式了,只是那些副程式都是C語言中的庫存函式(Compiler中內見的),所以現在我們就來寫寫屬於自己的副程式吧!

副程式的主要用途為將一些常用的程式碼,獨立拿出來寫成另一大段的程式碼,當主程式或其它副程式需要用到時,便可呼叫副程式來使用,而不需再去copy這一段程式碼。此種方法為「物件導向(OPP)」的一種,之後的章節也會以類似的方法來寫程式。

1.副程式的寫法:
和主程式很類似,main改成副程式的名子,且必須寫在主程式的上方!
注意:傳回值一定要填寫,否則會出現編譯錯誤!

傳回型態 副程式名(引數內容)
{
  副程式的內容
}

2.呼叫副程式:
副程式名();  //直接輸入名稱後加上()即可!

範例:寫一個歡迎的副程式,並呼叫它!
#include <stdio.h>
#include <iostream.h>

void come(void)   //副程式come:內容為顯示歡迎的訊息
{
printf("歡迎進入C++的副程式!\n");
}

int main(void)
{
int x;

printf("請輸入一個數並按Enter來呼叫副程式:\n");
cin>>x;
come();  //呼叫副程式come

  system(pause);
  return 0;
}

3.副程式也可呼叫其它的副程式:
不過必需注意一點,被呼叫的副程式必須寫在叫它的副程式前面,不然會出現編譯上的錯誤!
:副程式A去呼叫副程式B,B就必須寫在A的前面才行!

範例:先以主程式呼叫副程式a,再以副程式a去呼叫副程式b
#include <stdio.h>
#include <iostream.h>

void b(void)
{
printf("我是副程式b!\n");
}

void a(void)
{
printf("我是副程式a!\n");
b();      //呼叫副程式b
}

int main(void)
{
a();     //呼叫副程式a
system("pause");
}

4.副程式呼叫自己:
副程式除了呼叫主副程式外,還可以呼叫自己,此動作稱為遞迴

範例:寫一個副程式來計算階乘(n!)
#include < stdio.h >

int fact(int n)
{
if(n<=1)
return 1;
return n*fact(n-1);     //n!=n*(n-1)!
}

void main(void)
{
int n;
printf(“請輸入階乘的數(n):\n);
scanf("%d",&n);
printf("%d!=%d\n",n,fact(n));
}

2.全域與區域變數:
變數如果只宣告在某個副程式或主程式中,就只能在該程式內使用,但如果將變數宣告在外面,即可從宣告那行之後任一行使用!

#include <stdio.h>

int x;    //從宣告的這行後都可使用

void number(void)
{
x=x+5;
}

int main(void)
{
number();
printf("%d\n",x);
}

1.全域與區域變數:
如果變數在某個主副程式內宣告,則為該程式的區域變數,若是在全部程式的上方宣告,則為全域變數想顯示全域變數時,在變數前方加上::即可!

#include < stdio.h >

int a=5;     //這個a為全域變數,可供任何主副程式使用!

void file(void)
{
int a=3;    //這個a為區域變數,只能在此副程式內使用!
printf("%d\n",a);        //顯示區域變數的a,a=5
printf("%d\n",::a);      //顯示全域變數的a,a=3
}

void main(void)
{
file();
}

2.全域變數的陣列:
函式中的變數都是放在記憶體的stack區段,區段容量通常都不大,如果宣告了一個太大的陣列就會把stack都佔滿,很有可能一執行就當機,此時就需要把陣列由主副程式中移到外面變成全域的陣列:

1.宣告方式: #define 陣列指標 (x)*(y)*(z)
2.套用方式陣列名稱[陣列指標]

範例:宣告一個全域陣列來使用
#include <stdio.h>
#define N 1024*1024

int a[N];       //宣告陣列a成全堿變數,a不再放在stack.

void main(void)
{
}

3.函式的用法:
雖然副程式很好用,但是卻有個很大的缺點-主副程式的參數與變數不一致!
簡單說就是假設主程式有個變數i想代入副程式計算,副程式接收的i與主程式傳入的i兩者是不同的,因此當副程式傳回給主程式i時會發現i並未作運算,因此想作這方面的事則必須使用另一種副程式-函式,而函式的使用分為三個步驟:

1.函式的宣告:
首先必需在主程式main的上方宣告函式的原型,宣告的方式如下:
引數內容為資料型態 + 資料的變數,可同時代入多種不同型態的引數,:(int x,float y,char a)

傳回型態 函式名稱(引數內容);  //引數內容即是代入函式作處理的資料

2.函式的定義:
寫在主程式main的下方,就是指函式的處理內容,寫的方式與副程式一樣,只不過要加上return來傳回:

傳回型態 函式名稱(引數內容)
{
  函式內容

  return 傳回變數;
}

3.函式的使用:
最後便是在主程式中使用該函式了,只要輸入名稱然後在括弧內輸入引數即可:

函式名稱(引數內容);

範例:以副程式將x=3代入函式g(x)=x+5計算後,再代回主程式顯示!
#include <stdio.h>
#include <iostream.h>

void g(x);

int main(void)
{
int x=3;
int answer; 

answer=g(x);
cout<<answer<<endl;
      
system("pause");
return 0;  
}

int g(int x)
{
x=x+5;

return x;
}

4.函式的傳回值:
常見的函式傳回值宣告法,有下列六種:

1.void函式:
如果函式的內容是不需傳回主程式作運算的(多半為文字類訊息),則需將資料型態設為void:

void 函式名稱(引數內容)    //如果是文字類訊息,引數內容則為空白

2.int函式:
將資料型態設為int,即傳回整數的資料;如果傳入的資料經計算為浮點數或字元,則將自動轉為整數傳回

int 函式名稱(引數內容)

3.long函式:
當傳入的整數資料過大,超出-32670~32670的範圍時,則可使用這種資料型態來宣告函式

long 函式名稱(引數內容)

4.double函式:
將資料型態設為double,即傳回雙精浮點數的資料,此資料型態包函了浮點數(float)與整數(int)

double 函式名稱(引數內容)

5.char函式:
將資料型態設為char,即傳回單字元的資料,:A,B,C

char 函式名稱(引數內容)

6.bool函式:
如果想將函式作邏輯上的判斷,則可使用這種型態的函式

bool 函式名稱(引數內容)    //此種函式的傳回值(return)只能為trueflase