GD32F303 debugging notes (3) IIC (hardware IIC + PCF8563 real-time clock)

Preface

The previous article introduced two communication protocols ( USART and SPI ) commonly used in microcontrollers, and gave the corresponding configuration process of GD32F303. This time we introduce the third common communication protocol IIC. This uses GD32's hardware IIC communication PCF8563 real-time clock.

IIC

IIC, also known as I2C, is also a serial communication protocol. It contains a clock line and a data line.

  • First of all, the standard IIC bus requires an external pull-up , that is, the clock line and the data line are connected to an external pull-up resistor (commonly 10k) to the power supply (MCU power supply voltage 3.3V or 5V or other power supply voltage)). The corresponding communication IO is configured as open-drain output . The purpose of this is simple. When multiple devices are mounted on this group of IIC buses , the response signals and data reception from the slave devices can be received . So in the idle state, these two lines are externally pulled high .
  • Based on the above basis, IIC defines the start signal , content transmission , response signal and end signal .
  • When the start signal is at a high level on the clock line, the data line is pulled down from high level to low level . Appears at the beginning of every communication.
  • The end signal is that when the clock line is at high level, the data line is pulled up from low level to high level . Appears at the end of every communication.
  • Content transmission follows the start signal, which is divided into 7-bit, 8-bit and 10-bit transmission. 7-bit and 8-bit can be understood as the same concept. 8-bit is nothing more than adding the read/write bit represented by the lowest bit in the chip manufacturer's specifications. When sending the slave device address, it is composed of the fixed high 7-bit data plus the last bit (write 0, read 1). For 10 bits, the upper two bits of address and read and write bits are sent first, and then the lower 8 bits of the address are sent. For example, 0x06F0 (0b-xxxx-x110-1111-0000) actually sends 11 bits of valid data. This article describes 7(8)-bit address transfer .
  • The response signal is easier to understand. Within a clock communication cycle after the transmission of a frame of content (8 bits) , the slave device controls whether the data line is high level or low level. A low level means that the slave has received the 8-bit content just transmitted, and a high level means that it has not been received (remember that the standard IIC is an open-drain output, the external pull-up is high, and the default is high when communication fails).
  • Communication speed . Different from SPI, SPI is as long as the communication frequency that the host can send is fast enough, and the slave can also accept a communication frequency that is high enough, it can theoretically be infinite. Like USART, common communication frequencies are 4800, 9600, 38400, 115200, etc. The IIC standard rate is 100kbps, and there are also fast mode 400kbps and high-speed mode 3.4Mbps. The standard rate supported in GD32F303 is 100kbps, fast mode 400kbps and fast+ mode 1Mbps .

Due to the limitation of IIC communication rate and relatively complete communication protocol, it is usually used in situations where the amount of data is not very large . For example, reading or writing several or dozens of data from the AT24CXX series EEPROM chip, reading or writing real-time clock data from the PCF8563 clock chip, using IIC to communicate with some specific functional ICs (such as Nanxin's SC8812 to implement the PD protocol Charging, such as the communication MPU6050 acquiring various attitude angle data, etc.), or driving a 0.96-inch OLED display, etc. This article uses the hardware IIC of GD32F303 to set and read the PCF8563 clock .

Programming of each module

Before configuring, please make sure you already have a GD32F303 keil project that includes its corresponding standard library . The project can be created using official routines or according to the project creation and compilation of GD32F303 Debugging Notes (Zero) . In addition, it is highly recommended to have an oscilloscope or logic analyzer nearby to view the communication waveforms output by our ports.

1. Clock configuration

  • Turn on the GPIO port clock, GPIO pin multiplexing clock, AF clock, and IIC module clock ( note that I am using the IIC1 module ).
void SystemClock_Reconfig(void)
{
    
    
		/* Enable all peripherals clocks you need*/
		rcu_periph_clock_enable(RCU_GPIOA);
		rcu_periph_clock_enable(RCU_GPIOB);
		rcu_periph_clock_enable(RCU_GPIOC);
		rcu_periph_clock_enable(RCU_GPIOD);
		
		rcu_periph_clock_enable(RCU_DMA0);
		rcu_periph_clock_enable(RCU_DMA1);
		rcu_periph_clock_enable(RCU_I2C1);
//		rcu_periph_clock_enable(RCU_ADC0);
//		rcu_periph_clock_enable(RCU_ADC2);
//		rcu_periph_clock_enable(RCU_USART1);
		rcu_periph_clock_enable(RCU_USART2);
		rcu_periph_clock_enable(RCU_SPI2);
		/* Timer1,2,3,4,5,6,11,12,13 are hanged on APB1,
		 * Timer0,7,8,9,10 			 are hanged on APB2
		 */
		rcu_periph_clock_enable(RCU_TIMER1);	

		rcu_periph_clock_enable(RCU_AF);

2. GPIO configuration

Please add image description

  • According to the description of the IIC1 pin in the manual above, the relevant IO configuration is as follows:
// IIC port and pins definition
#define IIC1_PORT					GPIOB
#define IIC1_SCL_PIN				GPIO_PIN_10
#define IIC1_SDA_PIN				GPIO_PIN_11

void GPIO_Init(void)
{
    
    
	/* 使用SW下载,不使用JTAG下载,管脚用作其它功能 */
	gpio_pin_remap_config(GPIO_SWJ_SWDPENABLE_REMAP, ENABLE);

	/* demo board IIC1 I/O */
	gpio_init(IIC1_PORT, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, IIC1_SCL_PIN | IIC1_SDA_PIN);	

}

3. IIC configuration

  • Configure IIC1 with a rate of 60kHz, high-low level ratio in fast mode and fast+ mode (the standard we use is optional), use IIC mode and IIC slave 7-bit address mode, enable IIC1 response and enable the IIC1 module.
/* IIC通信中PCF8563芯片的地址 */
#define ADDRESS_PCF8563			((uint8_t)0xA2)

void IICx_Init(void)
{
    
    
	/* configure I2C1 clock */
	i2c_clock_config(I2C1,60000,I2C_DTCY_2);
	/* configure I2C1 address */
	i2c_mode_addr_config(I2C1, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, ADDRESS_PCF8563);
	/* enable I2C1 */
	i2c_enable(I2C1);
	/* enable acknowledge */
	i2c_ack_config(I2C1, I2C_ACK_ENABLE);
	
//  /* enable I2C1 DMA */
//  i2c_dma_enable(I2C1, I2C_DMA_ON);
}

4. IIC writing function

  • The code that uses IIC to communicate with a slave device is not the only constant , which is determined by the internal register addressing bits of the slave device itself (some slave devices also have page operations in addition to the register itself) .
  • Let’s first take a look at the description of its internal registers in the data sheet of the PCF8563 chip:
    Please add image description

Please add image description

  • Focus on the parts circled in red boxes in the above figure. They are the registers we use IIC to write and read, and each register does not use up to 8 bits. Let us make a macro definition here:
/* PCF8563芯片状态控制寄存器的地址 
 * 00H~01H共2个8位寄存器:
 */
#define ADDRESS_CTL_STATUS1	((uint8_t)0x00)		//控制状态寄存器 1
#define ADDRESS_CTL_STATUS2	((uint8_t)0x01)		//控制状态寄存器 2

/* PCF8563芯片里时间和日期寄存器的地址 
 * 从02H~08H共七个8位寄存器依次包含:秒(0~59)、分(0~59)、时(0~23)、
 * 日(1~31)、周几(0~6)、月份(1~12)、年份(0~99)
 */
#define ADDRESS_SECOND_RES	((uint8_t)0x02)		//秒寄存器 
#define ADDRESS_MINUTE_RES	((uint8_t)0x03)		//分寄存器 
#define ADDRESS_HOUR_RES	((uint8_t)0x04)		//时寄存器 
#define ADDRESS_DAY_RES		((uint8_t)0x05)		//日期寄存器 
#define ADDRESS_WEEKDAY_RES	((uint8_t)0x06)		//周几寄存器 
#define ADDRESS_MONTH_RES	((uint8_t)0x07)		//月份寄存器 
#define ADDRESS_YEAR_RES	((uint8_t)0x08)		//年份秒寄存器 

/* PCF8563芯片时间寄存器最大位数
 * 秒和分最多7Bits
 * 时和日期最多6Bits
 */
#define BCD_MinAndSec		((uint8_t)0x7F)				//取低7位
#define BCD_HourAndDay		((uint8_t)0x3F)				//取低6位																							
#define BCD_Weekday			((uint8_t)0x07)				//取低3位	
#define BCD_Months			((uint8_t)0x1F)				//取低5位	
#define BCD_Years			((uint8_t)0xFF)				//取低8位
#define BCD_Century			((uint8_t)0x80)				//取第7位 month寄存器里的bit7
  • Next, let's look at the multibyte write process recommended in the data sheet.
    Please add image description
  • The master sends a start signal , the master sends the slave device address and writes 0 in the lowest bit to indicate a write operation. The master sends the first address of the register to be operated in the slave address , waits for the slave to respond , the master sends 8 bit data , and then waits for the slave to respond . The master then sends data and waits for the slave to respond . After multiple responses and sending data, the master then sends a stop signal . Pay attention to the bold words, the logic we write below should also be the same.
  • Let’s take a look at the process recommendations for write operations in GD32.
    Please add image description
  • Then according to the timing requirements in the above two manuals, we write as follows:
  • DevAddress is the slave device address, MemAddress is the register to be operated on in the slave device, and then the data we really want to write. In order to ensure the correctness of IIC timing, we see that there are flag bits after every official signal operation. As the old saying goes, there is a timeout mechanism in all unnecessary infinite loops.
void IICx_Mem_Write(uint32_t i2c_periph,uint8_t DevAddress,uint8_t MemAddress,uint8_t* ndata,uint8_t size,uint32_t Timeout)
{
    
    
	uint32_t Timeout_t=0;
	uint8_t i=0;
	
	Timeout_t = Timeout;
	/* wait until I2C bus is idle */
	while(i2c_flag_get(i2c_periph, I2C_FLAG_I2CBSY))
	{
    
    
		if(Timeout_t > 0)	Timeout_t--;
		else				break;
	}
	
	/* send a start condition to I2C bus */
	i2c_start_on_bus(i2c_periph);
	
	Timeout_t = Timeout;
	/* wait until SBSEND bit is set */
	while(!i2c_flag_get(i2c_periph, I2C_FLAG_SBSEND))
	{
    
    
		if(Timeout_t > 0)	Timeout_t--;
		else				break;
	}
	
	/* send slave address to I2C bus*/
	i2c_master_addressing(i2c_periph, DevAddress, I2C_TRANSMITTER);
	
	Timeout_t = Timeout;
	/* wait until ADDSEND bit is set*/
	while(!i2c_flag_get(i2c_periph, I2C_FLAG_ADDSEND))
	{
    
    
		if(Timeout_t > 0)	Timeout_t--;
		else				break;
	}
	/* clear ADDSEND bit */
	i2c_flag_clear(i2c_periph, I2C_FLAG_ADDSEND);	
		
	/* send a data byte */
	i2c_data_transmit(i2c_periph,MemAddress);
	
	Timeout_t = Timeout;
	/* wait until the transmission data register is empty*/
	while(!i2c_flag_get(i2c_periph, I2C_FLAG_TBE))
	{
    
    
		if(Timeout_t > 0)	Timeout_t--;
		else				break;
	}
	for(i=0;i<size;i++)
	{
    
    
		/* send a data byte */
		i2c_data_transmit(i2c_periph, (*ndata));
		
		Timeout_t = Timeout;
		/* wait until the transmission data register is empty*/
		while(!i2c_flag_get(i2c_periph, I2C_FLAG_TBE))
		{
    
    
			if(Timeout_t > 0)	Timeout_t--;
			else				break;
		}
		
		ndata++;
	}
	/* send a stop condition to I2C bus*/
	i2c_stop_on_bus(i2c_periph);
	
	Timeout_t = Timeout;
	/* wait until stop condition generate */ 
	while(I2C_CTL0(i2c_periph)&0x0200)
	{
    
    
		if(Timeout_t > 0)	Timeout_t--;
		else				break;
	}
}

5. IIC reading function

  • Let’s take a look at the read timing in the PCF8563 chip manual first.
    Please add image description
  • The read timing of Fig 19 is: the master sends the start signal , the master sends the slave device address and writes 0 in the lowest bit to indicate a write operation, the master sends the first address of the register to be operated in the slave address , waits for the slave to respond , and the master sends again . start signal , the host sends the slave device address and writes 1 in the lowest bit to indicate a read operation, the slave sends 8-bit data , then waits for the host to respond , the slave sends data again , waits for the host to respond, and the slave sends data and the host responds multiple times The host does not respond and sends a stop signal .
  • The reading timing of Fig 19 is recommended here . The reason is also very simple. To read data from a register, you must first know what register you are reading. Otherwise, even if the data is read out, you don't know whose data it is. Before each read, point to the first register to be read.
  • Next, let’s take a look at the operation process of read timing in GD32.
    Please add image description
    Please add image description
  • The above GD32 gives two host receiving solutions. Plan A corresponds to using IIC to receive interrupts, and plan B corresponds to not using IIC to receive interrupts. Considering that communication is not very frequent, here we use plan B , which is to block query reception.
  • DevAddress is the address of the slave device, and MemAddress is the register to be operated on in the slave device. First point to the register we want to read, and then read multiple bytes. Note that what I am giving here is multi-byte reading , and the number of bytes read must be no less than 3 (there is a segment i==(size - 3) in the code) . If you want to read a single byte, just send a stop signal directly after reading it once, regardless of whether there is a response.
void IICx_Mem_Read(uint32_t i2c_periph,uint8_t DevAddress,uint8_t MemAddress,uint8_t* ndata,uint8_t size,uint32_t Timeout)
{
    
    
	uint32_t Timeout_t=0;
	uint8_t i=0;
	
	/******************************************************/
	/*	Send Slave address and Specified Register Address */
	/******************************************************/
	
	Timeout_t = Timeout;
	/* wait until I2C bus is idle */
	while(i2c_flag_get(i2c_periph, I2C_FLAG_I2CBSY))
	{
    
    
		if(Timeout_t > 0)	Timeout_t--;
		else				break;
	}
	/* send a start condition to I2C bus */
	i2c_start_on_bus(i2c_periph);
	
	Timeout_t = Timeout;
	/* wait until SBSEND bit is set */
	while(!i2c_flag_get(i2c_periph, I2C_FLAG_SBSEND))
	{
    
    
		if(Timeout_t > 0)	Timeout_t--;
		else				break;
	}
	
	/* send slave address to I2C bus*/
	i2c_master_addressing(i2c_periph, DevAddress, I2C_TRANSMITTER);//I2C_RECEIVER		I2C_TRANSMITTER
	
	Timeout_t = Timeout;
	/* wait until ADDSEND bit is set*/
	while(!i2c_flag_get(i2c_periph, I2C_FLAG_ADDSEND))
	{
    
    
		if(Timeout_t > 0)	Timeout_t--;
		else				break;
	}
	/* clear ADDSEND bit */
	i2c_flag_clear(i2c_periph, I2C_FLAG_ADDSEND);	

	/* send a data byte */
	i2c_data_transmit(i2c_periph,MemAddress);
	
	Timeout_t = Timeout;
	/* wait until the transmission data register is empty*/
	while(!i2c_flag_get(i2c_periph, I2C_FLAG_TBE))
	{
    
    
		if(Timeout_t > 0)	Timeout_t--;
		else				break;
	}	

	/* send a stop condition to I2C bus*/
	i2c_stop_on_bus(i2c_periph);
	
	Timeout_t = Timeout;
	/* wait until stop condition generate */ 
	while(I2C_CTL0(i2c_periph)&0x0200)
	{
    
    
		if(Timeout_t > 0)	Timeout_t--;
		else				break;
	}	
	
	/* enable acknowledge */
	i2c_ack_config(i2c_periph, I2C_ACK_ENABLE);

	/******************************************************/
	/*	    Send Slave address and Read Data 	 		  */
	/******************************************************/
	
	Timeout_t = Timeout;
	/* wait until I2C bus is idle */
	while(i2c_flag_get(i2c_periph, I2C_FLAG_I2CBSY))
	{
    
    
		if(Timeout_t > 0)	Timeout_t--;
		else				break;
	}
	/* send a start condition to I2C bus */
	i2c_start_on_bus(i2c_periph);
	
	Timeout_t = Timeout;
	/* wait until SBSEND bit is set */
	while(!i2c_flag_get(i2c_periph, I2C_FLAG_SBSEND))
	{
    
    
		if(Timeout_t > 0)	Timeout_t--;
		else				break;
	}

	/* send slave address to I2C bus*/
	i2c_master_addressing(i2c_periph, DevAddress, I2C_RECEIVER);
	
	Timeout_t = Timeout;
	/* wait until ADDSEND bit is set*/
	while(!i2c_flag_get(i2c_periph, I2C_FLAG_ADDSEND))
	{
    
    
		if(Timeout_t > 0)	Timeout_t--;
		else				break;
	}
	/* clear ADDSEND bit */
	i2c_flag_clear(i2c_periph, I2C_FLAG_ADDSEND);	
	
	for(i=0;i<size;i++)
	{
    
    
		if( i==(size - 3) )
		{
    
    
			Timeout_t = Timeout;
			/* wait until the second last data byte is received into the shift register */
			while(!i2c_flag_get(i2c_periph, I2C_FLAG_BTC))
			{
    
    
				if(Timeout_t > 0)	Timeout_t--;
				else				break;			
			}
			/* disable acknowledge */
      		i2c_ack_config(i2c_periph, I2C_ACK_DISABLE);
		}
		Timeout_t = Timeout;
		/* wait until the RBNE bit is set */
	    while(!i2c_flag_get(i2c_periph, I2C_FLAG_RBNE))
		{
    
    
			if(Timeout_t > 0)	Timeout_t--;
			else				break;			
		}
    	/* read data from I2C_DATA */
    	(*ndata) = i2c_data_receive(i2c_periph);	
		ndata++;
	}
	
	/* send a stop condition to I2C bus*/
  	i2c_stop_on_bus(i2c_periph);
	
	Timeout_t = Timeout;
	/* wait until stop condition generate */ 
 	while(I2C_CTL0(i2c_periph)&0x0200)
	{
    
    
		if(Timeout_t > 0)	Timeout_t--;
		else				break;
	}	
	
	/* enable acknowledge */
  	i2c_ack_config(i2c_periph, I2C_ACK_ENABLE);
}

6. Processing of real-time clock data

  • After completing the fifth step above, there is still one data processing left. Let’s take a look at how the clock chip manual describes its data storage form:
    Please add image description
  • Seeing the red box in the manual above, we know that the time and date registers are recorded in the form of BCD code, and it is the 8421 code in the BCD code. This means that we have to convert our normal decimal numbers into BCD codes before sending the data, and then convert the data into decimal numbers after reading the data.

1. First define a structure that contains the hours, minutes, seconds, year, month, day and day of the week we need:

typedef struct 
{
    
    
	struct
	{
    
    
		uint8_t Second;
		uint8_t Minute;
		uint8_t Hour;
	}time;
	
	struct
	{
    
    
		uint8_t weekday;
		uint8_t day;
		uint8_t month;
		uint8_t year;
	}date;
		
} PCF8563_Info;

extern PCF8563_Info RTC_Message;

2. Function to convert decimal number into BCD code:

static uint8_t RTC_BinToBcd(uint8_t BinValue)
{
    
    
	uint8_t cacheBuf = 0;
	
	while(BinValue >= 10)
	{
    
    
		BinValue -= 10;
		cacheBuf += 1;
	}
	
	cacheBuf = (cacheBuf<<4) + BinValue;
	
	return (cacheBuf);
}

3. Function to convert BCD code into decimal number:

static uint8_t RTC_BcdToBin(uint8_t BCDValue,uint8_t xRegister)
{
    
    
	uint8_t cacheBuf = 0;
		
	cacheBuf = ( BCDValue & (xRegister&0xF0) ) >> 4;
	cacheBuf = cacheBuf*10 + (BCDValue & (xRegister&0x0F)); 
	
	return (cacheBuf);
}

4. Write the real-time clock function:

void Write_To_PCF8563(PCF8563_Info* set_pcf8563_time)
{
    
    	
	static uint8_t temp_send_BCD[7]={
    
    0};
	
	*temp_send_BCD 			= RTC_BinToBcd(set_pcf8563_time->time.Second);
	*(temp_send_BCD + 1)	= RTC_BinToBcd(set_pcf8563_time->time.Minute);
	*(temp_send_BCD + 2)	= RTC_BinToBcd(set_pcf8563_time->time.Hour);
	*(temp_send_BCD + 3)	= RTC_BinToBcd(set_pcf8563_time->date.day);
	*(temp_send_BCD + 4)	= RTC_BinToBcd(set_pcf8563_time->date.weekday);
	*(temp_send_BCD + 5)	= RTC_BinToBcd(set_pcf8563_time->date.month);
	*(temp_send_BCD + 6)	= RTC_BinToBcd(set_pcf8563_time->date.year);

	IICx_Mem_Write(I2C1,ADDRESS_PCF8563,ADDRESS_SECOND_RES,temp_send_BCD,7,0xFFFFU);
}

5. Read real-time clock function:

PCF8563_Info Read_From_PCF8563(void)
{
    
    
	PCF8563_Info Readbuf={
    
    0};
	static uint8_t ReadFromPCF[7]={
    
    0};
	
	IICx_Mem_Read(I2C1,ADDRESS_PCF8563,ADDRESS_SECOND_RES,ReadFromPCF,7,0xFFFFU);
	
	Readbuf.time.Second 	= RTC_BcdToBin(ReadFromPCF[0],BCD_MinAndSec);
	Readbuf.time.Minute 	= RTC_BcdToBin(ReadFromPCF[1],BCD_MinAndSec);
	Readbuf.time.Hour		= RTC_BcdToBin(ReadFromPCF[2],BCD_HourAndDay);
	Readbuf.date.day		= RTC_BcdToBin(ReadFromPCF[3],BCD_HourAndDay);
	Readbuf.date.weekday	= RTC_BcdToBin(ReadFromPCF[4],BCD_Weekday);
	Readbuf.date.month		= RTC_BcdToBin(ReadFromPCF[5],BCD_Months);
	Readbuf.date.year		= RTC_BcdToBin(ReadFromPCF[6],BCD_Years);
	
	return (Readbuf);
}

7. Main function part

1. Display part

void TASK_LCD_REFRESH(void)
{
    
    
	lv_task_handler();
}

2. Task function

PCF8563_Info RTC_Message={
    
    0};

void TASK_PCF8563(void)
{
    
    
	RTC_Message = Read_From_PCF8563();
}

3. Main function

  • A structure variable of type PCF8563_Info is defined here to prepare for modifying the time base of the real-time clock. For example, it is set here to: 23:59:55 on November 10, 2021, Friday.
  • TMT is a time slice framework. See GITEE for the source code . Here we set up a task to read the data in the real-time clock every 1000ms.
  • lvgl is a lightweight graphical interface library that allows ordinary 32-bit microcontrollers to have a good UI interface display. There are many demonstration videos at Bilibili, so I won’t introduce them here. I will introduce how to use lvgl in a later article. Ported into GD32.
int main(void)
{
    
    	
	PCF8563_Info set_pcf8563={
    
    
		.time.Second	= 55,
		.time.Minute	= 59,
		.time.Hour		= 23,
		.date.weekday	= 4,
		.date.day		= 10,
		.date.month		= 11,
		.date.year		= 21
	};
	
	SystemTick_Init();	
	SystemClock_Reconfig();	
	GPIO_Init();
	Timer1_Init();
	Timer3_Init();
	DMA_Init();
	USARTx_Init();
	SPIx_Init();
	IICx_Init();
	FWDGT_Init();	
	NVIC_Init();
	/* 时间片框架(可忽略) */
	TMT_Init();
	/* lvgl库初始化(可忽略) */
	lv_init(); 
	lv_port_disp_init();     
	lv_port_indev_init(); 
	/* TMT任务创建,这里知道LCD_REFRESH每10ms执行一次,其它1s执行一次即可 */
	TMT.Create(TASK_LCD_REFRESH,10);
	TMT.Create(TASK_PCF8563,1000);
	TMT.Create(TASK_FWDGT_RELOAD,1000);
	 
	/* 此处为lvgl中的btn控件创建,能让屏上显示变量这里也忽略即可 */
	lv_obj_t * btn1;
	lv_obj_t * btn2;
	lv_obj_t * btn3;
	lv_obj_t * btn4;
	lv_obj_t * btn5;
	lv_obj_t * btn6;
	static lv_style_t style1;
	.
	.
	.
	/*======================*/

	/* 这里把赋的初值传过去 */
	Write_To_PCF8563(&set_pcf8563);
	while(1)
	{
    
    
		TMT.Run();

		lv_label_set_text_fmt(label_1,"hello C world");																																		
		lv_label_set_text_fmt(label_4,"date:20%02d-%02d-%02d", RTC_Message.date.year,RTC_Message.date.month,RTC_Message.date.day);
		lv_label_set_text_fmt(label_5,"time: %02d:%02d:%02d", RTC_Message.time.Hour,RTC_Message.time.Minute,RTC_Message.time.Second);
		lv_label_set_text_fmt(label_6,"weekday:%d", RTC_Message.date.weekday);

	}
}

8. Result Demonstration

1. Actual effect

Hardware IIC read time

You can also click here to view the video link of the effect

2. Drive waveform

- IIC complete waveform
Please add image description

  • The complete waveform is as above, the yellow line is the clock line and the blue line is the data line. The frequency should be 60kHz. The oscilloscope calculates the frequency incorrectly due to the reduction.

    - The initial part of IIC
    Please add image description - It's much better here. The frequency is about 60k. Look from the left to see if there is a starting signal first . Is the first data 0xA2 (0b1010 0010), and then there is a clock cycle response to see if it is pulled low by the slave. Then the second data is seconds register 0x02 (0b0000 0010), followed by another slave response. And so on.

    - IIC stop part
    Please add image description - The same goes for the end, see if there is a stop signal at the end .

9. Summary

  • So far, we have implemented the three most common communications in microcontrollers (SPI and USART, please see my other articles) using the hardware modules that come with GD32. In fact, there is also single-bus communication (only one line is used). The DS18B20 used at the beginning of learning and the signal reception in the infrared remote control are all this. We can also define a single-bus protocol ourselves, stipulating what waveform the starting signal is. What model is the stop signal, what waveform is the high level, and what waveform is the low level. Just like signaling, as long as both parties agree on certain specifications, an agreement can be formed and the communication can be successful.

!!!This article was originally published by Huanxi 6666 on CSDN. Please indicate the source when copying or reprinting:)!!!

Guess you like

Origin blog.csdn.net/qq_37554315/article/details/120875305
IIC