6. C51 Communication

6.1 IIC Communication Instruction

6.1.1 Getting Started

  • Wiring Instruction

Connect the 5V, GND, SDA, and SCL pins of the 8-ch Line Follower to the corresponding pins on the C51 controller. The wiring method is shown in the diagram below:

Note

Before powering on, ensure that no metal objects are touching the controller. Otherwise, the exposed pins at the bottom of the board may cause a short circuit and damage the controller.

  • Program Download

Source Code

(1) Connect the C51 controller to your computer using a USB cable.

(2) Double-click to open the download tool AIapp-ISP-v6.95E. In the software, select the correct COM port from the dropdown menu. The COM port number may vary depending on your computer. Do not select COM1, as it is reserved for system communication.

(3) Click the “Check MCU” button. Then press the K1 reset button on the control board. The onboard LED will flash once, and the software will automatically detect the chip model. The detected chip model will be shown in the upper-left “MCU Type” field, and logs will appear in the bottom-right output panel.

Note

If the LED does not flash or the chip is not detected, try pressing the K1 button multiple times quickly.

(4) Click “Open Code File”, navigate to the folder LineFollowerLearn8CH\Objects, and select the generated HEX file.

(5) Click “Download/Program”, then press the K1 reset button again. The software will begin flashing the firmware. Once the output log displays the message shown below, the download has completed successfully.

Note

If the download does not start automatically after pressing the button, try pressing the K1 button multiple times.

6.1.2 Test Case

  • Project Outcome

Note

Before recognition, you need to perform the one-click intelligent calibration for the sensor to ensure proper detection.

Once the 8-ch Line Follower detects the corresponding color of the line-following target, the serial port will print the status, analog values, and threshold values of each sensor. The printed data will appear as follows:

  • Program Brief Analysis

Source Code

(1) The corresponding libraries are imported, including “LineFollowerLearn8CH.h”, which provides support for the 8-ch line follower.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include "AI8051U.h"
#include "stdio.h"
#include "type.h"
#include "intrins.h"
#include "led.h"
#include "buzzer.h"
#include "pwm_servos.h"
#include "robot_arm.h"
#include "global.h"
#include "serial_servos.h"

#include "AI8051U_Uart.h"
#include "pc_porting.h"
#include "LineFollowerLearn8CH.h"

(2) The sensor communication method can be switched using a macro definition. In this case, set linefollower_8ch_communication to sensor_IIC.

16
17
18
19
20
linefollower_8ch linefollower;
#define sensor_IIC  1                                        //8-channel line follower sensor IIC communication (八路巡线传感器IIC通信)
#define sensor_UART 2                                        //8-channel line follower sensor UART communication (八路巡线传感器UART通信)
#define linefollower_8ch_communication  sensor_IIC          //Macro definition to enable communication mode for 8-channel line follower sensor (八路巡线传感器通信方式使能宏定义)

(3) The program then initializes the timer base value and defines a delay function.

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/* Calculate timer initial value (计算定时器初值) */
#define TIMER0_INIT_VALUE (65536 - (TIME_us * (MAIN_Fosc / 1000000)) / 12)

#define ULTRASOUND_OFFSET 9.0f


//void Delay20ms(void)	//@40MHz
//{
//	unsigned long edata i;

//	_nop_();
//	_nop_();
//	i = 199998UL;
//	while (i) i--;
//}

void Delay200ms(void)	//@40MHz
{
	unsigned long edata i;

	_nop_();
	_nop_();
	i = 1999998UL;
	while (i) i--;
}

void Delay500ms(void)	//@40MHz
{
	unsigned long edata i;

	_nop_();
	_nop_();
	i = 4999998UL;
	while (i) i--;
}

void Delay1100ms(void)	//@40MHz
{
	unsigned long edata i;

	_nop_();
	_nop_();
	i = 10999998UL;
	while (i) i--;
}

(4) It checks the macro definition for the sensor connection method and performs initialization accordingly. For example, if the connection method is IIC, the program initializes the related IIC communication members of the sensor.

116
117
118
119
120
#if (linefollower_8ch_communication == sensor_IIC)
	LineFollowerLearn8CH_IIC_init(); 
	
#elif (linefollower_8ch_communication == sensor_UART)
	LineFollowerLearn8CH_UART_init(&linefollower , manual_mode);  //Initialize the sensor object for UART communication and specify the working mode (初始化传感器对象用于UART通信相关的成员,指定工作模式)

(5) Inside the while loop, the connection type is first determined. If the connection is IIC, the program sends a request and parses the returned data to obtain each channel’s status, analog value, and threshold. The acquired low and high 8-bit values are combined into a 16-bit value.

136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
	while(1)
	{	
#if (linefollower_8ch_communication == sensor_IIC)		
		detect_state_all_channel_IIC(&linefollower);
		get_AnalogVoltage_all_channel_IIC(&linefollower);
		get_ThresholdValue_all_channel_IIC(&linefollower);
		
#elif (linefollower_8ch_communication == sensor_UART)
		
		Change_TransmitData_In_Mode_manual(Transmit_SensorState);
		while(! detect_state_all_channel_UART(&linefollower));

		Change_TransmitData_In_Mode_manual(Transmit_AnalogValue);		
		while(! get_AnalogVoltage_all_channel_UART(&linefollower));
		
		Change_TransmitData_In_Mode_manual(Transmit_ThresholdValue);
		while(! get_ThresholdValue_all_channel_UART(&linefollower));		

(6) In the detect_state_all_channel_IIC() function, sensor data is requested and parsed using i2c_read_data(). The results are extracted via a for loop to retrieve the status values for each channel.

24
25
26
27
28
29
30
31
32
33
34
void detect_state_all_channel_IIC(linefollower_8ch *obj){
	uint8_t len=0;
	
	i2c_read_data(LineFollowerLearn8CH_I2C_ADDR << 1, SensorStateReg, &(obj->StateReg_OriginData), sizeof(obj->StateReg_OriginData));
	
	for(len = 0; len < SENSOR_SUM; len++)
	{
		obj->state_all_ch[len]= ((obj->StateReg_OriginData >> len) & 0x01); //Parse data to extract status value of each channel (??析数据,提取各????状态值)
	}
	
}

(7) The get_AnalogVoltage_all_channel_IIC() and get_ThresholdValue_all_channel_IIC() functions work similarly. The former retrieves and parses analog values from all channels, while the latter obtains and processes the threshold values. Both functions use i2c_read_data() to request and parse sensor data, then merge the low and high 8-bit values of each channel into 16-bit data in for loop.

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/**
 * @brief Get and parse analog values of each channel via IIC communication (IIC??信方式获取各????模拟量并??析)
 *
 * @param linefollower_8ch *obj:8-channel line follower object(八路巡线传感器对??)
 * @note SensorAnalogReg_OriginData is the raw data sent from the sensor, which can be used for debugging and observation. (SensorAnalogReg_OriginData为传感器发来的原始数据,可供调????测)
 */
void get_AnalogVoltage_all_channel_IIC(linefollower_8ch *obj){
	uint8_t len=0;
	uint8_t count=0;
	
	i2c_read_data(LineFollowerLearn8CH_I2C_ADDR << 1, SensorAnalogReg_ch1, obj->SensorAnalogReg_OriginData, sizeof(obj->SensorAnalogReg_OriginData));
	
	for(len = 0,count = 0; len < SENSOR_SUM; len++)
	{
		obj->AnalogVoltage_all_ch[len]= (obj->SensorAnalogReg_OriginData[count]) | (obj->SensorAnalogReg_OriginData[count+1]<<8);  //??析数据,把各????模拟值低8位和??8位数据合并为16位数据
		count += 2;
	}
}

/**
 * @brief Get and parse threshold values of each channel via IIC communication (IIC??信方式获取各??????值量并??析)
 *
 * @param linefollower_8ch *obj:8-channel line follower object(八路巡线传感器对??)
 * @note SensorThresholdReg_OriginData is the raw data sent from the sensor, which can be used for debugging and observation.(SensorThresholdReg_OriginData为传感器发来的原始数据,可供调????测)
 */
void get_ThresholdValue_all_channel_IIC(linefollower_8ch *obj){
	uint8_t len = 0;
	uint8_t count=0;

	i2c_read_data(LineFollowerLearn8CH_I2C_ADDR << 1, SensorThresholdReg_ch1, obj->SensorThresholdReg_OriginData, sizeof(obj->SensorThresholdReg_OriginData));
	
	for(len = 0,count = 0; len < SENSOR_SUM; len++)
	{
		obj->ThresholdVoltage_all_ch[len]= (obj->SensorThresholdReg_OriginData[count]) | (obj->SensorThresholdReg_OriginData[count+1]<<8);  //Parse data by combining low and high 8-bit data of analog values into 16-bit data for each channel (??析数据,把各????模拟值低8位和??8位数据合并为16位数据)
		count += 2;
	}
}

(8) Finally, the results are printed through the serial port.

166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
		sprintf(imformation_string,\
						"ch1:%hhu,ch2:%hhu,ch3:%hhu,ch4:%hhu,ch5:%hhu,ch6:%hhu,ch7:%hhu,ch8:%hhu\r\n",\
						linefollower.state_all_ch[0],linefollower.state_all_ch[1],linefollower.state_all_ch[2],linefollower.state_all_ch[3],\
						linefollower.state_all_ch[4],linefollower.state_all_ch[5],linefollower.state_all_ch[6],linefollower.state_all_ch[7]);
		uart1_Sendstring(imformation_string);
		Delay500ms();
		
		sprintf(imformation_string,\
						"analog1:%hu,analog2:%hu,analog3:%hu,analog4:%hu,analog5:%hu,analog6:%hu,analog7:%hu,analog8:%hu\r\n",\
						linefollower.AnalogVoltage_all_ch[0],linefollower.AnalogVoltage_all_ch[1],linefollower.AnalogVoltage_all_ch[2],\
						linefollower.AnalogVoltage_all_ch[3],linefollower.AnalogVoltage_all_ch[4],linefollower.AnalogVoltage_all_ch[5],\
						linefollower.AnalogVoltage_all_ch[6],linefollower.AnalogVoltage_all_ch[7]);
		uart1_Sendstring(imformation_string);
		Delay500ms();

		sprintf(imformation_string,\
						"Threshold1:%hu,Threshold2:%hu,Threshold3:%hu,Threshold4:%hu,Threshold5:%hu,Threshold6:%hu,Threshold7:%hu,Threshold8:%hu\r\n",\
						linefollower.ThresholdVoltage_all_ch[0],linefollower.ThresholdVoltage_all_ch[1],linefollower.ThresholdVoltage_all_ch[2],\
						linefollower.ThresholdVoltage_all_ch[3],linefollower.ThresholdVoltage_all_ch[4],linefollower.ThresholdVoltage_all_ch[5],\
						linefollower.ThresholdVoltage_all_ch[6],linefollower.ThresholdVoltage_all_ch[7]);
		uart1_Sendstring(imformation_string);	
		Delay500ms();	

6.2 UART Communication Instruction

6.2.1 Getting Started

  • Wiring Instruction

Connect the 5V, GND, TX, and RX pins of the 8-ch Line Follower to the corresponding pins on the C51 controller. The wiring method is shown in the diagram below:

Note

Before powering on, ensure that no metal objects are touching the controller. Otherwise, the exposed pins at the bottom of the board may cause a short circuit and damage the controller.

  • Program Download

Source Code

(1) Connect the C51 controller to your computer using a USB cable.

(2) Double-click to open the download tool AIapp-ISP-v6.95E. In the software, select the correct COM port from the dropdown menu. The COM port number may vary depending on your computer. Do not select COM1, as it is reserved for system communication.

(3) Click the “Check MCU” button. Then press the K1 reset button on the control board. The onboard LED will flash once, and the software will automatically detect the chip model. The detected chip model will be shown in the upper-left “MCU Type” field, and logs will appear in the bottom-right output panel.

Note

If the LED does not flash or the chip is not detected, try pressing the K1 button multiple times quickly.

(4) Click “Open Code File”, navigate to the folder LineFollowerLearn8CH\Objects, and select the generated HEX file.

(5) Click “Download/Program”, then press the K1 reset button again. The software will begin flashing the firmware. Once the output log displays the message shown below, the download has completed successfully.

Note

If the download does not start automatically after pressing the button, try pressing the K1 button multiple times.

6.2.2 Test Case

  • Project Outcome

Note

Before recognition, you need to perform the one-click intelligent calibration for the sensor to ensure proper detection.

Once the 8-ch Line Follower detects the corresponding color of the line-following target, the serial port will print the status, analog values, and threshold values of each sensor. The printed data will appear as follows:

  • Program Brief Analysis

Source Code

(1) The corresponding libraries are imported, including “LineFollowerLearn8CH.h”, which provides support for the 8-ch line follower.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include "AI8051U.h"
#include "stdio.h"
#include "type.h"
#include "intrins.h"
#include "led.h"
#include "buzzer.h"
#include "pwm_servos.h"
#include "robot_arm.h"
#include "global.h"
#include "serial_servos.h"

#include "AI8051U_Uart.h"
#include "pc_porting.h"
#include "LineFollowerLearn8CH.h"

(2) The sensor communication method can be switched using a macro definition. In this case, set linefollower_8ch_communication to sensor_UART.

16
17
18
19
20
linefollower_8ch linefollower;
#define sensor_IIC  1                                        //8-channel line follower sensor IIC communication (八路巡线传感器IIC通信)
#define sensor_UART 2                                        //8-channel line follower sensor UART communication (八路巡线传感器UART通信)
#define linefollower_8ch_communication  sensor_UART          //Macro definition to enable communication mode for 8-channel line follower sensor (八路巡线传感器通信方式使能宏定义)

(3) The program then initializes the timer base value and defines a delay function.

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/* Timer period 1000 microseconds (定时 1000us)  */
#define TIME_us   1000

/* Calculate timer initial value (计算定时器初值) */
#define TIMER0_INIT_VALUE (65536 - (TIME_us * (MAIN_Fosc / 1000000)) / 12)

#define ULTRASOUND_OFFSET 9.0f


//void Delay20ms(void)	//@40MHz
//{
//	unsigned long edata i;

//	_nop_();
//	_nop_();
//	i = 199998UL;
//	while (i) i--;
//}

void Delay200ms(void)	//@40MHz
{
	unsigned long edata i;

	_nop_();
	_nop_();
	i = 1999998UL;
	while (i) i--;
}

void Delay500ms(void)	//@40MHz
{
	unsigned long edata i;

	_nop_();
	_nop_();
	i = 4999998UL;
	while (i) i--;
}

void Delay1100ms(void)	//@40MHz
{
	unsigned long edata i;

	_nop_();
	_nop_();
	i = 10999998UL;
	while (i) i--;
}

(4) It checks the macro definition for the sensor connection method and performs initialization accordingly. For example, when the connection mode is set to UART, the IIC-related members of the sensor are still initialized, and the UART working mode of the sensor is specified.

116
117
118
119
120
121
#if (linefollower_8ch_communication == sensor_IIC)
	LineFollowerLearn8CH_IIC_init(); 
	
#elif (linefollower_8ch_communication == sensor_UART)
	LineFollowerLearn8CH_UART_init(&linefollower , manual_mode);  //Initialize the sensor object for UART communication and specify the working mode (初始化传感器对象用于UART通信相关的成员,指定工作模式)

(5) In the LineFollowerLearn8CH_UART_init() function, you can see that it first initializes the serial reception state machine, sets the sensor’s UART working mode, and initializes the serial port.

82
83
84
85
86
87
88
89
90
void LineFollowerLearn8CH_UART_init(linefollower_8ch *obj , int mode){
	uart2_init(115200);
	
	obj->RX_state_machine = unpack_FrameHEAD_1;   //Initialize UART receive state machine (初始化串口接收状态机)
	obj->Work_mode = mode;                        //Assign value to work mode member (赋值工作模式成员)
	obj->sub_work_mode_InManual = 0;              //Clear the sub-mode member for manual mode; assign value later if needed (将手动模式子模式成员清??,后面如有需要再赋值)
	obj->uart_rx_buffer_index = 0;                //Reset the UART receive buffer index (将对??串口接收缓冲区索引归??)
	obj->Data_usable = 0;                         //Clear the data available flag; it is set to 1 only after successful parsing in the UART interrupt (清空数据可用标志位,只有串口中断中的??析完成才允许置1)

(6) Inside the while loop, the connection type is first determined. If the connection mode is UART, it first enters the corresponding UART mode setting, then proceeds to request and parse the data, extracting the channel state values, analog values, and threshold values.

136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
	while(1)
	{	
#if (linefollower_8ch_communication == sensor_IIC)		
		detect_state_all_channel_IIC(&linefollower);
		get_AnalogVoltage_all_channel_IIC(&linefollower);
		get_ThresholdValue_all_channel_IIC(&linefollower);
		
#elif (linefollower_8ch_communication == sensor_UART)
		
		Change_TransmitData_In_Mode_manual(Transmit_SensorState);
		while(! detect_state_all_channel_UART(&linefollower));

		Change_TransmitData_In_Mode_manual(Transmit_AnalogValue);		
		while(! get_AnalogVoltage_all_channel_UART(&linefollower));
		
		Change_TransmitData_In_Mode_manual(Transmit_ThresholdValue);
		while(! get_ThresholdValue_all_channel_UART(&linefollower));		

(7) In the Change_TransmitData_In_Mode_manual() function, it first resets the serial reception state machine and clears all related flag variables.

127
128
129
130
131
132
133
void Change_TransmitData_In_Mode_manual(int mode_in_manual){
	
	memset((void *)linefollower.uart_rx_buffer, 0x00, sizeof(linefollower.uart_rx_buffer));
	linefollower.Data_usable = 0;
	linefollower.sub_work_mode_InManual = mode_in_manual;
	linefollower.uart_rx_buffer_index = 0;
	linefollower.RX_state_machine = unpack_FrameHEAD_1;   //Initialize the UART receive state machine (初始化串口接收状态机)

(8) Then, using a switch statement, it sends data requests for channel state values, analog values, and threshold values respectively.

135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
	switch(mode_in_manual){
		case Transmit_SensorState:{
			linefollower.DataLength_Receive = 1;
			uart4_SendByte(Transmit_SensorState);
			break;
		}
		case Transmit_AnalogValue:{
			linefollower.DataLength_Receive = SENSOR_SUM*2;
			uart4_SendByte(Transmit_AnalogValue);
			break;
		}
		case Transmit_ThresholdValue:{
			linefollower.DataLength_Receive = SENSOR_SUM*2;
			uart4_SendByte(Transmit_ThresholdValue);
			break;
		}
		default:{
			break;
		}
	}

(9) Next, the detect_state_all_channel_UART() function handles the parsing of the response data.

145
146
147
148
149
150
151
152
		Change_TransmitData_In_Mode_manual(Transmit_SensorState);
		while(! detect_state_all_channel_UART(&linefollower));

		Change_TransmitData_In_Mode_manual(Transmit_AnalogValue);		
		while(! get_AnalogVoltage_all_channel_UART(&linefollower));
		
		Change_TransmitData_In_Mode_manual(Transmit_ThresholdValue);
		while(! get_ThresholdValue_all_channel_UART(&linefollower));	

(10) In the get_AnalogVoltage_all_channel_UART() function, which parses analog values, the relevant flags are first cleared. The program then checks the Data_usable flag, which indicates whether valid UART data has been received and parsed. If successful, the parsed data is returned, otherwise, a return value of 0 indicates that parsing has failed.

380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
int get_ThresholdValue_all_channel_UART(linefollower_8ch *obj){
	uint8_t len=0;
	uint8_t count=0;
	
	if(obj->Data_usable){
	
		obj->Data_usable = 0;
		
		for(len = 0,count = 0; len < SENSOR_SUM; len++)
		{
			obj->ThresholdVoltage_all_ch[len]= (obj->SensorThresholdReg_OriginData[count]) | (obj->SensorThresholdReg_OriginData[count+1]<<8);  //Parse data, merge low 8 bits and high 8 bits of each channel's threshold value into a 16-bit value(??析数据,把各??????值低8位和??8位数据合并为16位数据)
			count += 2;
		}	
		
		return 1;
	
	}else{
		return 0;
	}
	
}

(11) Finally, the results are printed through the serial port.

166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
		sprintf(imformation_string,\
						"ch1:%hhu,ch2:%hhu,ch3:%hhu,ch4:%hhu,ch5:%hhu,ch6:%hhu,ch7:%hhu,ch8:%hhu\r\n",\
						linefollower.state_all_ch[0],linefollower.state_all_ch[1],linefollower.state_all_ch[2],linefollower.state_all_ch[3],\
						linefollower.state_all_ch[4],linefollower.state_all_ch[5],linefollower.state_all_ch[6],linefollower.state_all_ch[7]);
		uart1_Sendstring(imformation_string);
		Delay500ms();
		
		sprintf(imformation_string,\
						"analog1:%hu,analog2:%hu,analog3:%hu,analog4:%hu,analog5:%hu,analog6:%hu,analog7:%hu,analog8:%hu\r\n",\
						linefollower.AnalogVoltage_all_ch[0],linefollower.AnalogVoltage_all_ch[1],linefollower.AnalogVoltage_all_ch[2],\
						linefollower.AnalogVoltage_all_ch[3],linefollower.AnalogVoltage_all_ch[4],linefollower.AnalogVoltage_all_ch[5],\
						linefollower.AnalogVoltage_all_ch[6],linefollower.AnalogVoltage_all_ch[7]);
		uart1_Sendstring(imformation_string);
		Delay500ms();

		sprintf(imformation_string,\
						"Threshold1:%hu,Threshold2:%hu,Threshold3:%hu,Threshold4:%hu,Threshold5:%hu,Threshold6:%hu,Threshold7:%hu,Threshold8:%hu\r\n",\
						linefollower.ThresholdVoltage_all_ch[0],linefollower.ThresholdVoltage_all_ch[1],linefollower.ThresholdVoltage_all_ch[2],\
						linefollower.ThresholdVoltage_all_ch[3],linefollower.ThresholdVoltage_all_ch[4],linefollower.ThresholdVoltage_all_ch[5],\
						linefollower.ThresholdVoltage_all_ch[6],linefollower.ThresholdVoltage_all_ch[7]);
		uart1_Sendstring(imformation_string);	
		Delay500ms();