I wish to establish I²C communication with NXP's LM75A temperature sensor using an LPC810 microcontroller for educational purposes, without utilising the ROM API.
I have managed to send I²C commands unilaterally and display text on an LCD, for instance, but I am unsure how to write code for communicating with a slave device like the LM75A, which requires read operations. I have attached my current source code below; at present, I cannot read the temperature from the LM75A.
■ Overview
・I/O ports assigned for I2C functionality
・Communication via polling, without interrupts
■ Main Queries
When wishing to return an ACK signal in master receive mode, it appears the LPC810 lacks such a register (only registers for the three conditions: START, CONTINUE, STOP). Can this be read using the attached code? If possible, I would appreciate a concrete example of source code.
#ifdef __USE_CMSIS
#include "LPC8xx.h"
#endif
#include <cr_section_macros.h>
#include "romI2C.h" //本コードはROMAPIを使用してないので、I2Cの定数定義部分のみ
#include "I2C_DEFINE.h"
#define LM75AAddr (0x48) //温度センサ アドレス
#define I2C_RW_W (0x00) //RW(LSB)はLCDは常に0
#define LM75TempRegi_pointer (0x00) //Tempレジスタポイタ
//Command:RS=0,Data:RS=1
#define LM75ConfRegi_pointer (0x01) //Configuration registerポインタ
int waiting=0;
void SwitchMatrix_Init();
void IOCON_Init();
void I2C_Init();
//void I2C_one_Byte_Write(uint8_t data);
void I2C_Multiple_Write(uint8_t Addr,uint8_t WriteByte,uint8_t *i2c_data);
void Systick_Init();
void I2C_Addr_WSend(uint8_t Addr);
void I2C_Addr_RSend(uint8_t Addr);
void Temp_SenceInit();
uint8_t I2C_Multiple_Read(uint8_t Addr,uint8_t ReadByte);
uint16_t ReadTemperture_LM75A(uint8_t Addr);
void mon_CFG(); //I2Cデータモニタ用関数
//void I2C_Stop();
//Systick
void SysTick_Handler(){
waiting=0;
}
static void wait_ms(uint32_t ms){
waiting=1;
SysTick_Config(SystemCoreClock/1000*ms);
while(waiting);
}
int main(void) {
int LED_C;
uint16_t temp=0;
LPC_GPIO_PORT->DIR0 |=(1<<1);
SystemCoreClockUpdate();
// Enter an infinite loop, just incrementing a counter
IOCON_Init();
SwitchMatrix_Init();
while(1){
temp=ReadTemperture_LM75A(LM75AAddr);
}
return 0 ;
}//main
void I2C_Init(){
LPC_SYSCON->PRESETCTRL &= ~(0x1<<6); //Assert Reset
LPC_SYSCON->PRESETCTRL |= (0x1<<6); //Clear Resett
//Set i2c clock DIV
LPC_I2C->DIV = 0x0001; //PCLK I2C Clock
LPC_I2C->MSTTIME=0; //Mater SCL Low Timee
LPC_I2C->CFG = I2C_CFG_MSTEN | 0x04; //マスターモード モニタイネーブル クロックストレッチ
}
void Temp_SenceInit(){ //温度センサ初期設定
uint8_t Config[3]={LM75AAddr,LM75ConfRegi_pointer,0x00}; //
I2C_Multiple_Write(LM75AAddr, 3, Config);
}
void I2C_Addr_WSend(uint8_t Addr){
while(!(LPC_I2C->STAT & I2C_STAT_MSTPENDING));
if((LPC_I2C->STAT & I2C_STAT_MSTSTATE) != I2C_STAT_MSTST_IDLE);
LPC_I2C->MSTDAT = (Addr<<1|0); // address and 0 for RWn bit
LPC_I2C->MSTCTL = I2C_MSTCTL_MSTSTART; // send start
while(!(LPC_I2C->STAT & I2C_STAT_MSTPENDING));
if((LPC_I2C->STAT & I2C_STAT_MSTSTATE) != I2C_STAT_MSTST_TX);
}
void I2C_Addr_RSend(uint8_t Addr){
while(!(LPC_I2C->STAT & I2C_STAT_MSTPENDING));
if((LPC_I2C->STAT & I2C_STAT_MSTSTATE) != I2C_STAT_MSTST_IDLE);
LPC_I2C->MSTDAT = (Addr<<1)|1; // address and 0 for RWn bit
LPC_I2C->MSTCTL = I2C_MSTCTL_MSTSTART; // send start
while(!(LPC_I2C->STAT & I2C_STAT_MSTPENDING));
if((LPC_I2C->STAT & I2C_STAT_MSTSTATE) != I2C_STAT_MSTST_RX);
}
void I2C_Multiple_Write(uint8_t Addr,uint8_t WriteByte,uint8_t *i2c_data){
//LPC_I2C->CFG = I2C_CFG_MSTEN; //マスターモード
I2C_Addr_WSend(Addr);
for (int i = 1; i < WriteByte; ++i){
while(!(LPC_I2C->STAT & I2C_STAT_MSTPENDING));
if((LPC_I2C->STAT & I2C_STAT_MSTSTATE) != I2C_STAT_MSTST_IDLE);//
LPC_I2C->MSTDAT = *(i2c_data+i); // send data
wait_ms(100); //wait
while(!(LPC_I2C->STAT & I2C_STAT_MSTPENDING));
LPC_I2C->MSTCTL = I2C_MSTCTL_MSTCONTINUE; // continue transaction
while(!(LPC_I2C->STAT & I2C_STAT_MSTPENDING));
if((LPC_I2C->STAT & I2C_STAT_MSTSTATE) != I2C_STAT_MSTST_TX); //break(); //abort();
while(!(LPC_I2C->STAT & I2C_STAT_MSTPENDING));
if((LPC_I2C->STAT & I2C_STAT_MSTSTATE) != I2C_STAT_MSTST_IDLE); //break();*/
}
LPC_I2C->MSTCTL = I2C_MSTCTL_MSTSTOP; //
while(!(LPC_I2C->STAT & I2C_STAT_MSTPENDING));
//if((LPC_I2C->STAT & I2C_STAT_MSTSTATE) != I2C_STAT_MSTST_IDLE);
}
//温度センサからの温度データ読みだし
uint16_t ReadTemperture_LM75A(uint8_t Addr){ //
uint8_t Readdata_MSB;
uint8_t Readdata_LSB;
uint16_t Readdata;
//I2C_Addr_RSend(Addr); //read
I2C_Addr_WSend(Addr); //write
LPC_I2C->MSTDAT = LM75TempRegi_pointer;
//LPC_I2C->MSTCTL = I2C_MSTCTL_MSTSTART; //RE-START
LPC_I2C->MSTCTL = I2C_MSTCTL_MSTCONTINUE; //
while(!(LPC_I2C->STAT & I2C_STAT_MSTPENDING));
if((LPC_I2C->STAT & I2C_STAT_MSTSTATE) != I2C_STAT_MSTST_TX);
LPC_I2C->MSTDAT = (Addr<<1)|1; //読みだし
LPC_I2C->MSTCTL = I2C_MSTCTL_MSTSTART; //RE=START
while(!(LPC_I2C->STAT & I2C_STAT_MSTPENDING));
Readdata_MSB=LPC_I2C->MSTDAT;
LPC_I2C->MSTCTL = I2C_MSTCTL_MSTCONTINUE;
while(!(LPC_I2C->STAT & I2C_STAT_MSTPENDING));
Readdata_LSB=LPC_I2C->MSTDAT;
while(!(LPC_I2C->STAT & I2C_STAT_MSTPENDING));
if((LPC_I2C->STAT & I2C_STAT_MSTSTATE) != I2C_STAT_MSTST_RX);
while(!(LPC_I2C->STAT & I2C_STAT_MSTPENDING));
LPC_I2C->MSTCTL = I2C_MSTCTL_MSTSTOP; // send stop NACKのスレーブへの送信
while(!(LPC_I2C->STAT & I2C_STAT_MSTPENDING));
if((LPC_I2C->STAT & I2C_STAT_MSTSTATE) != I2C_STAT_MSTST_IDLE) ;
Readdata=Readdata_MSB<<8|Readdata_LSB;
return Readdata;
}
void SwitchMatrix_Init()
{
/* Enable SWM clock */
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<7|1<<5); //Enable SWM Clock,I2C Clock
LPC_SWM->PINASSIGN7 = 0x00ffffffUL; //SDA
/* I2C0_SCL */
LPC_SWM->PINASSIGN8 = 0xffffff04UL; //SCL
LPC_SWM->PINENABLE0 = 0xffffffb3UL;
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<18); //Enables clock for IOCON block.
}
void IOCON_Init() {
/* Enable UART clock */
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<18);
}
void Systick_Init(){
SysTick->CTRL=0; //Systick_Countter stop
SysTick->LOAD=12000; //1ms
SysTick->VAL=0; //
SysTick->CTRL=0x07; //Systick
}
void mon_CFG(){
uint32_t mondata;
mondata=LPC_I2C->MONRXDAT;
}
Habib_MS,
Thank you for your message.
I wrote a program using Chapter 29.2 “Code Example I2C” as a reference, but it isn't working properly.
Currently, I cannot visualize how the LPC810 operates when controlling as a master using hardware I2C.
I believe the code example lists three constants to set in LPC_I2C->MSTCTL: I2C_MSTCTL_MSTSTART, I2C_MSTCTL_MSTCONTINUE, and I2C_MSTCTL_MSTSTOP.
I2C_MSTCTL_MSTSTART handles the start condition and restart condition, and also...
I2C_MSTCTL_MSTSTOP is for the stop condition,
but could you please explain how I2C_MSTCTL_MSTCONTINUE behaves in both master transmit mode and master receive mode?
Translated with DeepL.com (free version)
Hello @LPC81x1114,
Of course.
The macro I2C_MSTCTL_MSTCONTINUE is defined with a value of 1, as shown in Chapter 29.2.1 "Definitions". It is used in the MSTCTL register, which writes a 1 to the MSTCONTINUE bit. This bit performs the following function:
In simplified terms, this bit tells the LPC controller that it can proceed with the next operation in the I2C protocol. For example, if a transmission has already started and an ACK has been received, the next step might be to send a data byte to the slave. This is when the MSTCONTINUE bit is activated.
That is why, in the example "Master writes one byte to slave", the bit is set after receiving an ACK and before sending the data byte.
BR
Habib
Hello @LPC81x1114,
I highly recommend reviewing Section 7.10: "Protocols for Writing and Reading the Registers" in the LM75A datasheet. This section explains how the I2C communication frames should be structured to perform various actions with the sensor. For example, it includes the frame format required to write to the configuration register:
Additionally, there are other useful frame formats mentioned that can help you better understand the communication process. This section also highlights key aspects of I2C communication.
I also suggest checking out Section 7.3: "Slave Address", which explains how to configure the sensor's slave address.
On another note, the RM provides several example codes that directly access I2C registers without using the ROM API. These examples can serve as a helpful reference and are located in the chapter 29.2 called "Code Examples I2C".
Hope this helps.
BR
Habib