Giao thức kết nối I2C 8051

unsigned char Soft_I2c_Read(bit ack)
{
    unsigned char i, dat=0;
    for(i=0;i<8;i++)
	{
	    SOFT_I2C_SDA = 1;
		Soft_I2c_Delay();
	    SOFT_I2C_SCL = 1;
		Soft_I2c_Delay();
	    dat <<= 1;
		if(SOFT_I2C_SDA)  // tại sao phải kiểm tra chân SOFT_I2C_SDA trong khi nó đc gán là 1
		{
		 	dat |= 0x01;
		}
		SOFT_I2C_SCL = 0;
    }
	if(ack)
	{
	 	Soft_I2c_Ack();
	}
	else
	{
	 	Soft_I2c_Nak();
	}
    return dat;
}

Em không hiểu chổ đó lắm mong đc chỉ giáo!

Nguyên tắc lấy mẫu (sample) thì khi chân giữ xung nhip SCL đang ở mức cao thì SDA không được thay đổi trạng thái. Nếu SDA thay đổi thì sẽ rơi vào Start/Stop Bit (Condition).

Bởi vì đây là hàm I2C Read.
Trong chế độ này, SDA được điều khiển bởi thiết bị I2C khác đang giao tiếp với chip chủ.
Gán SDA = 1 tại chip chủ mục đích để làm cho SDA có thể bị thay đổi logic bởi thiết bị khác. Nếu gán SDA bằng 0 tại chip chủ thì SDA luôn luôn = 0.
Đoạn check SDA chỉ đơn giản là kiểm tra mức logic xuất ra từ chip Slave sau mỗi xung SCL để đưa nó vào trong byte dữ liệu nhận được. Bit dữ liệu mới luôn bằng 0 nên chỉ cần kiểm tra SDA có bằng 1 hay không để thay đổi.
Thực ra đoạn if đó là thừa. Chỉ cần thế này là đủ:

dat |= (unsigned char) SDA;
2 Likes

Em không hiểu code mẫu của em có bị gì hay không mà bỏ vòng if thì nó chạy sai hoàn toàn! Em viết giao tiếp vs ds1307!

Cảm ơn anh viết đã giải thích cho em! Mà anh ơi chân SDA em chỉ gán nó = 1 thôi mà em có đụng gì nữa đâu mà rơi vào trạng thái start/stop.

Cho em hỏi thêm dat = 0 thì kết quả cuối cùng ta có 1 con số 1111 1111? --> em không hiểu cơ chế hoạt động của nó lắm + mình thu đc gì? Em tự học + IT! Nếu em hỏi gì ngu quá xin mấy anh lượng thứ + chỉ dạy thêm! Em đọc thì hiểu thằng write còn thằng read thì em bó tay.

Bạn up code sau khi sửa lên.

Khi master cần đọc dữl liệu từ slaver, nó gửi đi dữ liệu (địa chỉ + R/W…) vào mạng I2C. Thiết bị nào phù hợp sẽ chuyển sang chế độ gửi dữ liệu, master sẽ chuyển sang chế độ nhận dữ liệu.
Ở chế độ nhận dữ liệu, master set SDA sang chế độ input (set SDA=1 với 8051) để có thể nhận dữ liệu truyền về từ slaver.
Tiếp đó master phát 8 xung liên tiếp lên SCL. Sau mỗi xung, slaver gửi 1 bit lên SDA và master sẽ đọc SDA sau mỗi xung để lấy từng bit dữ liệu.
Kết thúc 8 xung, master nhận đủ 1 byte dữ liệu (8bit).
Lúc này nếu slaver yêu cầu ACK thì slaver chuyển sang chế độ chờ ACK. Master khi này phải xuất mức 1 ra SDA và xuất thêm xung thứ 9 trên SCL đến slaver. Quá trình nhận 1 byte hoàn tất.
Byte tiếp theo lặp lại tương tự.

Nếu dữ liệu nhận về là 11111111 tức là đã không có biến đổi gì trên SDA khi master xuất xung trên SCL.
Có thể do lỗi kết nối, lỗi đường truyền, không có slaver phù hợp hoặc slaver đang không hoạt động.

Code hảm read sau khi sửa:

unsigned char readI2C(bit ACK){
	unsigned char i, dat = 0;
	i = 0;
	while(i < 8){

		I2C_SDA = 1;
		delayI2C();
		I2C_SCL = 1;
		delayI2C();
		dat <<= 1;
	         dat |= 0x01;	
		I2C_SCL = 0;
	}

	return dat;
}

Hay là code nguyên project anh?

Sao lại dat |= 0x01 ???
Ở đây em phải hiểu là dat<<=1 là dịch tất cả các bit của dat sang trái để trống bit số 0.
Bit này sẽ để lưu giá trị mới của SDA.
Như vậy thì dòng sau em phải lưu SDA vào đấy.

dat<<=1;
dat |= (unsigned char) SDA.

Đây là đoạn code i2c read mình dùng để đọc ADC từ MCP3421 bạn có thể tham khảo:

unsigned char MCP3421ReadData(unsigned char ack){
    unsigned char retval=0;
    SDA=1;
    for(unsigned int i=0;i<8;i++){
        retval<<=1;
        SCL=1;
        __delay_us(5);
        retval |= (unsigned char)SDA;
        SCL=0;
    }
    if(ack>0){
        SDA=0; // ACK
        __delay_us(5);
        SCL=1;
        __delay_us(5);
        SCL=0;
        SDA=1;
    }
    return retval;
}

Cảm ơn sự chân thành giúp đở của anh nhiều!

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?