1. 程式人生 > >聲學晶片FM2018WE-380 除錯詳解

聲學晶片FM2018WE-380 除錯詳解

FM2018WE-380晶片的主要作用是抑制聲學噪聲,消除聲學回聲,提升通話質量。 **注:**本篇底部附FM2018WE-380晶片相關文件供下載學習。

主要有以下步驟: 1.上電(VDD拉高),若需VDD_1V8拉高,檢視原理圖可知需先讓GPIO27_FM2018_EN 引腳使能(置1)

2.選擇SHI模式,拉高VDD(選擇這個模式外部主機才能初始化和更改晶片內部暫存器),這裡SHI與VDD公用一個引腳(VDD_1V8) 3.接入外部時鐘(經與硬體溝通得知為 TCXO_CLK引腳匯入時鐘),檢視原理圖可知需讓GPIO30_TCXO_PW_EN 引腳使能(置1)

4.配置晶片內部所有暫存器的初始值(這裡通過I2C傳輸資料)

相關程式碼

/*****************************************************************************
 * FUNCTION
 *  fmdsp_bootup
 * DESCRIPTION
 * 
 * Set FM2018_DSP register parameters when FM2018_DSP is turned on.
 *
 * PARAMETERS mode
 *
 * RETURNS
 *  
 *****************************************************************************/
kal_uint8 fm2018_dsp_bootup(void)
{
	fm2018dsp.read = fm2018_i2c_read;           // assign read function
    fm2018dsp.write= fm2018_i2c_write;               // assign write function
    //power on 

	//SHI_S  拉高此引腳,允許外部主機初始化和更改暫存器的值(檢視晶片手冊得知)
	GPIO_ModeSetup(VDDSHI_FM2018_EN, 0);
	GPIO_InitIO(1, VDDSHI_FM2018_EN);
	GPIO_WriteIO(1, VDDSHI_FM2018_EN);
	Delayms(10);

	//RESET FM2018 復位
	GPIO_ModeSetup(AGPIO52_RESET_2018, 0);
	GPIO_InitIO(1, AGPIO52_RESET_2018);
	GPIO_WriteIO(0, AGPIO52_RESET_2018);
    Delayms(30);

	//P WD_FM2018  掉電模式(拉高是正常模式)
	GPIO_ModeSetup(GPIO49_PWD, 0);
	GPIO_InitIO(1, GPIO49_PWD);
	GPIO_WriteIO(1, GPIO49_PWD);
    
    //CLK  時鐘(對應GPIO拉高表示接入外部時鐘)
    GPIO_ModeSetup(GPIO30_TCXO_PW_EN, 0);
    GPIO_InitIO(1, GPIO30_TCXO_PW_EN);
    GPIO_WriteIO(1, GPIO30_TCXO_PW_EN);
    Delayms(5);
    
	GPIO_WriteIO(1, AGPIO52_RESET_2018);//RESET FM2018
//	Delayms(15);    

//init parameter
	fm2018dsp_init(&fm2018dsp);     //初始化晶片內部暫存器

}

初始化暫存器

/*****************************************************************************
 * FUNCTION
 *  fmdsp_bootup
 * DESCRIPTION
 * Set FM2018_DSP register parameters when FM2018_DSP is turned on.
 * PARAMETERS mode
 * RETURNS
 *****************************************************************************/
void fm2018dsp_init(struct fm2018dsp_t *fm2018dsp)
{
	kal_uint8 pData[16];
	
	// set hands free mode
	tr_debug("\r\n<<<<<<<<<<<<<<<<<<fmdsp_bootup enter>>>>>>>>>>>>>>>>>>>>");
	// ============================================================ //
	// Register address and register data
	// ============================================================ //

/*1st Parameters can only be changed at initialization stage */
fm2018dsp->write(0x1E, 0x30, 0x02, 0x31);//1E30 0231  //D9-Parser enable :1, D5-Analog line-out :1, D4-Analog line-in :1 D0-Mic_0 enbale:1
fm2018dsp->write(0x1E, 0x34, 0x00, 0x2A);//1E34 002A  //Mic_pgagain     Mic0 PGA gain. 0x0a : +16db
fm2018dsp->write(0x1E, 0x35, 0x00, 0x0A);//1E35 000A  //Linein_pgagain   Line-in PGA gain. 0x0a : +16db
fm2018dsp->write(0x1E, 0x36, 0x00, 0x1D);//1E36 001D 	//Lineout_pgagain   Line-out PGA gain. 0x1d : +00db
fm2018dsp->write(0x1E, 0x39, 0x00, 0x00);//1E39 0000 	//Ana_comm_high     D0 -1:  Analog Communication Mode is triggered by rising edge 

//ANA_COMM 
//fm2018dsp->write(0x1E, 0x4A, 0x00, 0x2A);//1E4A 002A 	//ana_Mic_pgagain   at ANA_COMM mode
//fm2018dsp->write(0x1E, 0x4B, 0x00, 0x2A);//1E4A 002A 	//ana_linein_pgagain   at ANA_COMM mode
//fm2018dsp->write(0x1E, 0x4C, 0x00, 0x2A);//1E4A 002A 	//ana_lineout_pgagain   at ANA_COMM mode

/* Parameters need to be preset according to application,placement and components specs */
fm2018dsp->write(0x1E, 0x3D, 0x02, 0x00); //1E3D 0200 mic volume : 0x0100 is equal to unit gain. 0x200 is 2X gain
fm2018dsp->write(0x1E, 0x3E, 0x02, 0x00); //1E3E 0200 spk volume : 0x0100 is equal to unit gain. 0x200 is 2X gain. (affect AEC_REF signal level)
fm2018dsp->write(0x1E, 0x40, 0xff, 0xff);//microphone mute flag,   0xffff: no mute
fm2018dsp->write(0x1E, 0x41, 0x01, 0x01);//1E41 0101   number of mics, 0x101: 1 mic
//fm2018dsp->write(0x1E, 0x44, 0x00, 0x81);//1E44 0081   // ..........
fm2018dsp->write(0x1E, 0x44, 0x0C, 0x89);//1E44 0081   //  D12 - SPK_OUT equalizer D11 - noise residue smoother D7 - SNR_ADJUST D3 - BVE,D0-frame process
fm2018dsp->write(0x1E, 0x45, 0x03, 0xcf);//1E45 03cf 	 //	....
fm2018dsp->write(0x1E, 0x46, 0x00, 0x10);//1E46 0010   // ... D4- Parser and SHI,1:on  

fm2018dsp->write(0x1E, 0x47, 0x20, 0x00);//1E47 2000 	 // Mic_NSS_level     the noise suppression//Example	(0x2000 / 0x7fff = -12dB)
fm2018dsp->write(0x1E, 0x48, 0x80, 0x00);//1E48 8000   //Mic_NS_lowband_gain   this parameter defines the noise suppression gain (in band one).0x800 is unit gain.
fm2018dsp->write(0x1E, 0x49, 0x80, 0x00);//1E49 8000   //Mic_NS_highband_gain  (out of band one) the bigger the value, the more noise suppression is applied.
//fm2018dsp->write(0x1E, 0x4D, 0x80, 0x00);//1E4D 8000   //cancel the echo. 0x100 is unit gain.
fm2018dsp->write(0x1E, 0x4D, 0xE8, 0x00);//1E4D 8000   //cancel the echo. 0x100 is unit gain.
fm2018dsp->write(0x1E, 0x4F, 0x00, 0x01);//1E4F 0001   // ... normal mode
fm2018dsp->write(0x1E, 0x57, 0x7E, 0x00);//1E57 7E00   //Microphone Saturate threshold(default value 0x7E00)  system is put to a half duplex mode to prevent howing   
fm2018dsp->write(0x1E, 0x59, 0x7F, 0xFF);//1E59 7FFF   //Lineout Clip Threshold
fm2018dsp->write(0x1E, 0x5C, 0x10, 0x00);//1E5C 1000   //This parameter is to set the half duplex hold time. Time = st_hd_time * (1/8192)sec
//fm2018dsp->write(0x1E, 0x63, 0x00, 0x10);//1E63 0010   //In order to compensate the delay of ADC / DAC
fm2018dsp->write(0x1E, 0x63, 0x00, 0x10);//1E63 0010   //In order to compensate the delay of ADC / DAC


fm2018dsp->write(0x1E, 0x80, 0x03, 0x3F);//1E63 0010   //In order to compensate the delay of ADC / DAC

//STOP.................
//fm2018dsp->write(0x1E, 0x86, 0x00, 0x07);//1E86 0007
//fm2018dsp->write(0x1E, 0x87, 0x00, 0x03);//1E87 0003
fm2018dsp->write(0x1E, 0x86, 0x00, 0x0E);//1E86 0007
fm2018dsp->write(0x1E, 0x87, 0x00, 0x0E);//1E87 0003 	
//fm2018dsp->write(0x1E, 0x88, 0x30, 0x00);//1E88 3000
//fm2018dsp->write(0x1E, 0x89, 0x18, 0x00);//1E89 1800  //suppress more echo under noisy environment.
fm2018dsp->write(0x1E, 0x88, 0x30, 0x00);//1E88 3000
fm2018dsp->write(0x1E, 0x89, 0x18, 0x00);//1E89 1800  //suppress more echo under noisy environment.

fm2018dsp->write(0x1E, 0x8B, 0x7F, 0xFF);//1E8B 7FFF  //Fe_vad_th_big
fm2018dsp->write(0x1E, 0x8C, 0x7F, 0xFF);//1E8C 7FFF   //fe_vad_th
fm2018dsp->write(0x1E, 0x90, 0xFF, 0xFF);//1E90 FFFF   //Aec_nw_shift
fm2018dsp->write(0x1E, 0x91, 0x00, 0xFF);//1E91 00FF   //Aec_fe_vad_shift  ...

fm2018dsp->write(0x1E, 0x92, 0x78, 0x00);//1E92 7800 	 //
fm2018dsp->write(0x1E, 0x93, 0x78, 0x00);//1E93 7800 	 //	
fm2018dsp->write(0x1E, 0xA0, 0x80, 0x00);//1EA0 8000   
fm2018dsp->write(0x1E, 0xA1, 0x80, 0x00);//1EA1 0010   //This parameter specifies the gain applied to both main and reference channels after Linear AEC process

#if 1  //agc
fm2018dsp->write(0x1E, 0xA7, 0x02, 0x88);//1EA7 0288
fm2018dsp->write(0x1E, 0xA8, 0x19, 0x4C);//1EA8 194C   //default  mic_agc_maxgain 
fm2018dsp->write(0x1E, 0xAB, 0x00, 0x74);//1EAB 0074   //default  mic_agc_ref     sp_flag(0x1e45)
#endif

#if 1  //GND
fm2018dsp->write(0x1E, 0xB3, 0x1F, 0x00);//1EB3 1F00  default   //Gnd_div_low  
fm2018dsp->write(0x1E, 0xB4, 0x14, 0x00);//1EB4 1400  default //Gnd_div_mid
fm2018dsp->write(0x1E, 0xB4, 0x14, 0x00);//1EB5 0800  default//Gnd_div_high
#endif

#if 1 // Fq
fm2018dsp->write(0x1E, 0xBB, 0x00, 0x0B);//1EBB 000B  default  //Fq_period
fm2018dsp->write(0x1E, 0xBC, 0x68, 0x00);//1EBC 6800           //Fq_beta_v1
fm2018dsp->write(0x1E, 0xBD, 0x01, 0x00);//1EBD 0100           //Fq_beta_v2
fm2018dsp->write(0x1E, 0xBE, 0x40, 0x00);//1EBE 4000 	          //Fq_beta_mix2
fm2018dsp->write(0x1E, 0xBF, 0x70, 0x00);//1EBF 7100            //Fq_beta_uv2
fm2018dsp->write(0x1E, 0xC0, 0x26, 0x80);//1EC0 2680             //Fq_beta_uv_fe
fm2018dsp->write(0x1E, 0xC1, 0x10, 0x80);//1EC1 1080             //Fq_beta_mix_fe
fm2018dsp->write(0x1E, 0xC5, 0x2B, 0x06);//1EC5 2B06            //Fq_refch_div_12  
fm2018dsp->write(0x1E, 0xC6, 0x0C, 0x1F);//1EC6 0C1F            //F	q_refch_div_34
fm2018dsp->write(0x1E, 0xC7, 0x40, 0x00);//1EC7 0400 		      //Fq_fevad_gain_limit
fm2018dsp->write(0x1E, 0xC8, 0x28, 0x79);//1EC8 2879            //Fq_refch_gain_band1
fm2018dsp->write(0x1E, 0xC9, 0x65, 0xAB);//1EC9 65AB            //Fq_refch_gain_band2
fm2018dsp->write(0x1E, 0xCA, 0x40, 0x26);//1ECA 4026             //Fq_refch_gain_band3
fm2018dsp->write(0x1E, 0xCB, 0x7F, 0xFF);//1ECB 7FFF             //Fq_refch_gain_band4
fm2018dsp->write(0x1E, 0xCC, 0x7F, 0xFE);//1ECC 7FFE             //Fq_refch_gain_band5
#endif

#if 1 //Eq_const_lout[1] ~ Eq_const_lout[5]
fm2018dsp->write(0x1E, 0xD0, 0x7F, 0xFF);//1ED0 7FFF             //Eq_const_lout[1]
fm2018dsp->write(0x1E, 0xD1, 0x7F, 0xFF);//1ED1 7FFF             //Eq_const_lout[2]
fm2018dsp->write(0x1E, 0xD2, 0x7F, 0xFF);//1ED2 7FFF             //Eq_const_lout[3]
fm2018dsp->write(0x1E, 0xD3, 0x7F, 0xFF);//1ED3 7FFF             //Eq_const_lout[4]
fm2018dsp->write(0x1E, 0xD4, 0x7F, 0xFF);//1ED4 7FFF             //Eq_const_lout[5]
#endif


fm2018dsp->write(0x1E, 0xDA, 0x20, 0x00);//1EDA 2000             //Fq_noise_smoother_gain
fm2018dsp->write(0x1E, 0xE0, 0x00, 0x00);//1EE0 0000             //Fq_start_echo

#if 1  //Bve
fm2018dsp->write(0x1E, 0xE2, 0x00, 0x06);//1EE2 0006             //Bve_order
fm2018dsp->write(0x1E, 0xE3, 0x04, 0x00);//1EE3 0400             //Bve_max_volume
fm2018dsp->write(0x1E, 0xE4, 0x18, 0x00);//1EE4 1800             //Bve_overflow_gain
#endif

fm2018dsp->write(0x1E, 0xF8, 0x04, 0x00);//1EF8 0400            //Vad0_rat_thrd_fe
fm2018dsp->write(0x1E, 0xF9, 0x01, 0x00);//1EF9 0620            //Vad0_rat_thrd_nofe

fm2018dsp->write(0x1E, 0xFF, 0x4B, 0x00);//1EFF 4B00            //VAD3_rat_thrd
fm2018dsp->write(0x1F, 0x00, 0x7F, 0xFF);//1F00 7FFF            //Mic_cal_diff
fm2018dsp->write(0x1F, 0x01, 0x31, 0x00);//1F01 3100            //Mic_cal_gain_center

#if 1 //Eq_const_spk[1] ~ Eq_const_spk[4]
fm2018dsp->write(0x1F, 0x06, 0x16, 0x52);//1F06 1652            //Eq_const_spk[1]
fm2018dsp->write(0x1F, 0x07, 0x18, 0xE2);//1F07 18E2            //Eq_const_spk[2]
fm2018dsp->write(0x1F, 0x08, 0x0A, 0x4E);//1F08 0A4E            //Eq_const_spk[3]
fm2018dsp->write(0x1F, 0x09, 0x05, 0xDF);//1F09 05DF            //Eq_const_spk[4]
#endif


fm2018dsp->write(0x1F, 0x0A, 0x0A, 0x00);//1F0A 0A00 		      //VAD1_grd_min
fm2018dsp->write(0x1F, 0x0B, 0x03, 0x00);//1F0B 0300 		      //VAD2_grd_min
fm2018dsp->write(0x1F, 0x0C, 0x01, 0x00);//1F0C 0100            //VAD1_thrd
fm2018dsp->write(0x1F, 0x0D, 0x78, 0x00);//1F0D 7800            //VAD2_thrd 


#if 1
fm2018dsp->write(0x1F, 0x28, 0x0C, 0x00);//1F28 0C00            //Spkout_drc_th1 
fm2018dsp->write(0x1F, 0x29, 0x18, 0x80);//1F29 1880            //Spkout_drc_th2 
fm2018dsp->write(0x1F, 0x2A, 0x34, 0x00);//1F2A 3400            //Spkout_drc_slop1 
fm2018dsp->write(0x1F, 0x2B, 0x68, 0x00);//1F2B 6800            //Spkout_drc_slop2 
fm2018dsp->write(0x1F, 0x30, 0x00, 0x00);//1F30 0000            //Debug_flag
#endif




/*1st Parameters can only be changed at initialization stage */
fm2018dsp->write(0x1E, 0x52, 0x00, 0x13);//1E52 0013   //This parameter sets the DSP Mips. DSP_mips = 1.024 * (23+17) = 40Mips		
//fm2018dsp->write(0x1E, 0x58, 0x00, 0x19); /* set clock */ //26m
fm2018dsp->write(0x1E, 0x58, 0x00, 0x0C); /* set clock */ //13m
fm2018dsp->write(0x1E, 0x60, 0x00, 0x00);//1E60 0000   //external clock source is used
fm2018dsp->write(0x1E, 0x51, 0xD0, 0x00); /* no need to reload parameter */
fm2018dsp->write(0x1E, 0x70, 0x05, 0xC0); //Pwdn_device_off  : This parameter has to be set to 0x05C0 to ensure reliable power down
tr_debug("DSP run, must be in last\r\n");
fm2018dsp->write(0x1E, 0x3A, 0x00, 0x00); // DSP run, must be in last
tr_debug("\r\n==================DSP run=============");
tr_debug("\r\n......................................");

	//fm2018dsp->read(0x1E,0x3A,(kal_uint8 *)pData,2);
	//tr_debug("2 test_clock ========== %x",*pData);

}

以下是關於I2C詳細記錄

Some Define

#define FM2018_I2C_DELAY  20    //設定一個延時變數,用於程式碼中for迴圈延時

#define fm2018_I2C_SCL  43  //SCL(時鐘線)對應的GPIO為43
#define fm2018_I2C_SDA  44  //SDA(資料線)對應的GPIO為44 (此兩項根據檢視原理圖得到)

#define FM2018_I2C_CLK_OUTPUT   GPIO_InitIO(1,fm2018_I2C_SCL)   //初始化SCL的工作方向設定為輸出
#define FM2018_I2C_CLK_HIGH     GPIO_WriteIO(1,fm2018_I2C_SCL)  //給SCL引腳一個高電平
#define FM2018_I2C_CLK_LOW      GPIO_WriteIO(0,fm2018_I2C_SCL)  //給SCL引腳一個低電平
#define FM2018_I2C_CLK_BIT      GPIO_ReadIO(fm2018_I2C_SCL)     //讀取SCL引腳的電平狀態
#define FM2018_I2C_DATA_OUTPUT  GPIO_InitIO(1,fm2018_I2C_SDA)   //初始化SDA的工作方向設定為輸出
#define FM2018_I2C_DATA_INPUT   GPIO_InitIO(0,fm2018_I2C_SDA)   //初始化SDA的工作方向設定為輸入
#define FM2018_I2C_DATA_HIGH    GPIO_WriteIO(1,fm2018_I2C_SDA)  //給SDA引腳一個高電平
#define FM2018_I2C_DATA_LOW     GPIO_WriteIO(0,fm2018_I2C_SDA)  //給SDA引腳一個高電平
#define FM2018_I2C_DATA_BIT     GPIO_ReadIO(fm2018_I2C_SDA)     //讀取S  DA引腳的電平狀態


#define FM2018_I2C_ACK_BIT \    //一個位元組傳輸完成後,第九位的應答(從機有應答,資料線對應為低電平)
	do{\
		kal_uint32 x;\
		FM2018_I2C_CLK_LOW; \                   //時鐘線拉低
		for (x=0; x<FM2018_I2C_DELAY; x++); \   //延時
		FM2018_I2C_DATA_OUTPUT; \               //資料線設定為輸出模式
		FM2018_I2C_DATA_LOW; \                  //資料線輸出低電平
		for (x=0; x<FM2018_I2C_DELAY; x++); \   //延時
		FM2018_I2C_CLK_HIGH; \                  //時鐘線拉高
		for (x=0; x<FM2018_I2C_DELAY; x++); \   //延時
		FM2018_I2C_CLK_LOW; \                   //時鐘線拉低                  
		for (x=0;x<FM2018_I2C_DELAY;x++);\      //延時
	}while(0)

#define FM2018_I2C_NACK_BIT \   //一個位元組傳輸完成後,第九位的應答(從機無應答,資料線對應為高電平) 
	do{\
		kal_uint32 x;\
		FM2018_I2C_CLK_LOW; \                   //時鐘線拉低
		for (x=0; x<FM2018_I2C_DELAY; x++); \   //延時
		FM2018_I2C_DATA_OUTPUT; \               //資料線設定為輸出模式
		FM2018_I2C_DATA_HIGH; \                 //資料線輸出高電平
		for (x=0; x<FM2018_I2C_DELAY; x++); \   //延時
		FM2018_I2C_CLK_HIGH; \                  //時鐘線拉高
		for (x=0; x<FM2018_I2C_DELAY; x++); \   //延時
		FM2018_I2C_CLK_LOW; \                   //時鐘線拉低
		for (x=0;x<FM2018_I2C_DELAY;x++);\      //延時
    }while(0) 

#define FM2018_I2C_START_TRANSMISSION \     //傳輸開始位 
	do{ \
		kal_uint32 x; \
		FM2018_I2C_CLK_OUTPUT; \                //時鐘線設定為輸出模式
		FM2018_I2C_DATA_OUTPUT; \               //資料線設定為輸出模式
		FM2018_I2C_CLK_HIGH; \                  //時鐘線拉高
		FM2018_I2C_DATA_HIGH; \                 //資料線拉高
		for(x=0;x<FM2018_I2C_DELAY;x++); \      //延時
		FM2018_I2C_DATA_LOW; \                  //資料線拉低
		for(x=0;x<FM2018_I2C_DELAY;x++); \      //延時
		FM2018_I2C_CLK_LOW; \                   //時鐘線拉低
		for(x=0;x<FM2018_I2C_DELAY;x++); \      //延時
	}while(0)

#define FM2018_I2C_STOP_TRANSMISSION \      //傳輸停止位
	do{ \
		kal_uint32 x; \
		FM2018_I2C_CLK_OUTPUT; \                //時鐘線設定為輸出模式
		FM2018_I2C_DATA_OUTPUT; \               //資料線設定為輸出模式
		FM2018_I2C_CLK_LOW; \                   //時鐘線拉低
		FM2018_I2C_DATA_LOW; \                  //資料線拉低
		for(x=0;x<FM2018_I2C_DELAY;x++);\       //延時
		FM2018_I2C_CLK_HIGH; \                  //時鐘線拉高
		for(x=0;x<FM2018_I2C_DELAY;x++);\       //延時
		FM2018_I2C_DATA_HIGH;\                  //資料線拉高
		for(x=0;x<FM2018_I2C_DELAY;x++);\       //延時
	}while(0)

圖示
實現程式碼
開始位
應答位(有應答)
應答位(無應答)
停止位

I2C初始化

void fm2018_i2c_init(void)
{
	GPIO_ModeSetup(fm2018_I2C_SCL, 0);  //設定SCL的工作模式是作為GPIO
	GPIO_InitIO(1, fm2018_I2C_SCL);     //初始化SCL的工作方向設定為輸出
	
	GPIO_ModeSetup(fm2018_I2C_SDA, 0);  //設定SDA的工作模式是作為GPIO
	GPIO_InitIO(1, fm2018_I2C_SDA);     //初始化SDA的工作方向設定為輸出
}

傳送一個位元組

//傳送一個位元組的資料
static kal_uint8 fm2018_i2c_send_byte(kal_uint8 send_byte)
{
	kal_uint8 ret;
	signed char i;
	kal_uint32 j;

	for (i=7;i>=0;i--)                          //傳送8個位(1個位元組)
	{	/* data bit 7~0 */              
		if (send_byte & (1<<i))                 //判斷對應位是否為1
		{
			FM2018_I2C_DATA_HIGH;               //為1時,資料位拉高
		}
		else
		{
			FM2018_I2C_DATA_LOW;                //為0時,資料位拉低
		}
		for(j=0;j<FM2018_I2C_DELAY;j++);        //延時
		FM2018_I2C_CLK_HIGH;                    //時鐘線拉高
		for(j=0;j<FM2018_I2C_DELAY;j++);        //延時
		FM2018_I2C_CLK_LOW;                     //時鐘線拉低
		for(j=0;j<FM2018_I2C_DELAY;j++);        //延時
	}
	
	/* don't care bit, 9th bit */               //獲取第9位應答位有無應答
	FM2018_I2C_DATA_INPUT;                      //將資料線設定為輸入模式
	for(j=0;j<FM2018_I2C_DELAY;j++);            //延時
	FM2018_I2C_CLK_HIGH;                        //時鐘線拉高
	for(j=0;j<FM2018_I2C_DELAY;j++);            //延時
	ret = !FM2018_I2C_DATA_BIT;                 //獲取此時資料位狀態(低電平有應答,高電平無應答)
	for(j=0;j<FM2018_I2C_DELAY;j++);            //延時
	FM2018_I2C_CLK_LOW;                         //時鐘線拉低
	FM2018_I2C_DATA_OUTPUT;                     //讀完應答位後要將資料線恢復到輸出狀態
	for(j=0;j<FM2018_I2C_DELAY;j++);            //延時
	
	if(0==ret)                                  //若高電平,無應答,傳送失敗
    {
	    tr_debug("i2c send byte fail!\r\n");
    }
    else                                        //若低電平,有應答,傳送成功
    {
        tr_debug("i2c send byte success!\r\n");
    }   
	
	return ret;
}

讀取一個位元組

//讀取一個位元組的資料
static kal_uint8 fm2018_i2c_get_byte(kal_uint8 ack)
{
	kal_uint8 get_byte=0;
	signed char i;
	kal_uint32 j;
	
	FM2018_I2C_CLK_LOW;                         //時鐘線拉低
	FM2018_I2C_DATA_INPUT;                      //資料線設定為輸入模式
	while(!FM2018_I2C_CLK_BIT)                  //判斷時鐘線狀態
    FM2018_I2C_CLK_HIGH;                        //時鐘線拉高

	for (i=7; i>=0; i--)                        //讀取8個位(1個位元組)
	{	/* data bit 7~0 */          
		for(j=0;j<FM2018_I2C_DELAY;j++);        //延時
		FM2018_I2C_CLK_HIGH;                    //時鐘線拉高
		for(j=0;j<FM2018_I2C_DELAY;j++);        //延時
		if (FM2018_I2C_DATA_BIT)                //判斷資料位狀態
			get_byte |= (1<<i);                 //將對應資料位狀態儲存到變數中
		for(j=0;j<FM2018_I2C_DELAY;j++);        //延時
		FM2018_I2C_CLK_LOW;                     //時鐘線拉低
	}

	if(ack)
    {
		FM2018_I2C_ACK_BIT;                     //應答位設定為有應答
	}	
	else
    {
		FM2018_I2C_NACK_BIT;                    //應答位設定為無應答
		tr_debug("\r\n FM2018_I2C_NACK_BIT");
	}	
	return get_byte;
}

I2C寫入資料

寫入流程圖及程式碼 這裡寫圖片描述

kal_uint8 fm2018_i2c_write(kal_uint8 address_high, kal_uint8 address_low, kal_uint8 data_high, kal_uint8 data_low)
{
	kal_uint16 i=0;
    kal_uint8 device_id = 0xC0;             //FM2018的裝置ID為"0xC0"
    kal_uint8 sync_bit1 = 0xFC;             //同步高位元組
    kal_uint8 sync_bit2 = 0xF3;             //同步低位元組
    kal_uint8 command_start = 0x3B;         //命令啟動,寫入記憶體(mem_write)

    //start condition
	FM2018_I2C_START_TRANSMISSION;          //I2C開始傳輸
	//write addr

	fm2018_i2c_send_byte(device_id);        //傳送裝置地址位元組
	fm2018_i2c_send_byte(sync_bit1);        //傳送同步高位元組
    fm2018_i2c_send_byte(sync_bit2);        //傳送同步低位元組
    fm2018_i2c_send_byte(command_start);    //傳送命令位元組(0x3B)

    //write reg addr
    fm2018_i2c_send_byte(address_high);     //傳送暫存器地址高位元組
    fm2018_i2c_send_byte(address_low);      //傳送暫存器地址低位元組
    //write data
    fm2018_i2c_send_byte(data_high);        //傳送資料高位元組
    fm2018_i2c_send_byte(data_low);         //傳送資料低位元組

	//stop condition
	FM2018_I2C_STOP_TRANSMISSION;           //I2C停止傳輸

	return KAL_SUCCESS;  
}

#I2C讀取資料 讀取流程圖及程式碼 這裡寫圖片描述

kal_uint8 fm2018_i2c_read(kal_uint8 address_high, kal_uint8 address_low, kal_uint8 *pData, kal_uint8 datasize)
{
	kal_uint16 i=0;
    kal_uint8 device_id = 0xC0;             //FM2018的裝置ID為"0xC0"
    kal_uint8 sync_bit1 = 0xFC;             //同步高位元組
    kal_uint8 sync_bit2 = 0xF3;             //同步低位元組
    kal_uint8 command_start = 0x37;         //命令啟動,讀取記憶體(mem_read)

	//start condition
	FM2018_I2C_START_TRANSMISSION;          //I2C開始傳輸
	//write addr        
    fm2018_i2c_send_byte(device_id);        //傳送裝置地址位元組
    fm2018_i2c_send_byte(sync_bit1);        //傳送同步高位元組
    fm2018_i2c_send_byte(sync_bit2);        //傳送同步低位元組
    fm2018_i2c_send_byte(command_start);    //傳送命令位元組(0x37)
    fm2018_i2c_send_byte(address_high);     //傳送暫存器地址高位元組
    fm2018_i2c_send_byte(address_low);      //傳送暫存器地址低位元組
    FM2018_I2C_STOP_TRANSMISSION;           //I2C停止傳輸

    //0X25    
	//start condition
	FM2018_I2C_START_TRANSMISSION;          //I2C開始傳輸
    fm2018_i2c_send_byte(device_id);        //傳送裝置地址位元組
    fm2018_i2c_send_byte(sync_bit1);        //傳送同步高位元組
    fm2018_i2c_send_byte(sync_bit2);        //傳送同步低位元組
    command_start = 0x60;                   
    fm2018_i2c_send_byte(command_start);    //傳送命令位元組(0x60)(reg_read)
    fm2018_i2c_send_byte(0x25);             //傳送資料埠實體地址低位元組(0x25)
    FM2018_I2C_STOP_TRANSMISSION;           //I2C停止傳輸

    FM2018_I2C_START_TRANSMISSION;          //I2C開始傳輸  
    //read data
    fm2018_i2c_send_byte(0xC1);             //可能是告訴FM2018即將需要讀取一個位元組的資料
    *pData = fm2018_i2c_get_byte(0);        //讀取一個位元組的資料(這裡讀取低地址0x25內的資料)
    //其中這裡的引數0表示無應答,從機停止傳輸,等待主機的停止訊號,若設定為1有應答,從機收到主機的應答訊號之後,從機繼續輸出下一位元組
    tr_debug("fm2018_i2c_read fm2018_i2c_get_byte(0x25) = %x",*pData);
    i++;    


    //0X26
    //start condition
	FM2018_I2C_START_TRANSMISSION;          //I2C開始傳輸
    fm2018_i2c_send_byte(device_id);        //傳送裝置地址位元組
    fm2018_i2c_send_byte(sync_bit1);        //傳送同步高位元組
    fm2018_i2c_send_byte(sync_bit2);        //傳送同步低位元組
    command_start = 0x60;                   
    fm2018_i2c_send_byte(command_start);    //傳送命令位元組(0x60)(reg_read)
    fm2018_i2c_send_byte(0x26);             //傳送資料埠實體地址高位元組(0x26)
    FM2018_I2C_STOP_TRANSMISSION;           //I2C停止傳輸

    FM2018_I2C_START_TRANSMISSION;          //I2C開始傳輸 
    //read data
    fm2018_i2c_send_byte(0xC1);             //可能是告訴FM2018即將需要讀取一個位元組的資料
    *pData = fm2018_i2c_get_byte(0);        //讀取一個位元組的資料(這裡讀取低地址0x26內的資料)
    //其中這裡的引數0表示無應答,從機停止傳輸,等待主機的停止訊號,若設定為1有應答,從機收到主機的應答訊號之後,從機繼續輸出下一位元組
    tr_debug("fm2018_i2c_read fm2018_i2c_get_byte(0x26) = %x",*pData);
    i++;
    datasize = i;                           //計數一共讀取了多少位元組的資料       
	//stop condition
	FM2018_I2C_STOP_TRANSMISSION;           //I2C停止傳輸
	return KAL_SUCCESS;
}

針對裝置地址與同步位的規定,由晶片自身決定,參見下圖: 這裡寫圖片描述

針對程式中的不同的命令位元組,是由由晶片自身特性決定的。詳見下圖: 這裡寫圖片描述 這裡寫圖片描述