Theo dõi toàn bộ tutorial lập trình enc28j60 tại đây
Viết chức năng đọc các gói tin ( đọc bộ đệm ethernet )
Bây giờ chúng ta sẽ học cách nhận các gói, vì toàn bộ trao đổi trên mạng cục bộ được chia thành các gói.
Việc nhận các gói trong chip xảy ra thông qua bộ đệm vòng.
Chúng ta hãy xem một ví dụ nhận gói trong hình
Trong bộ đệm sẽ chứa rất nhiều gói tin, cứ bắt đầu mỗi gói sẽ là 1 con trỏ dài 2 byte lưu vị trí của gói tin tiếp theo, tiếp đến 4 byte lưu trạng thái nhận, tiếp đó là dữ liệu của gói tin, và 4 byte cuối sẽ lưu giá trị checksum
Trong trường vector status, 2 byte đầu tiền [0->15] chứa độ dài của gói tin, 2 byte tiếp chứa các trạng thái nhận liên quan đến gói đó, các bạn xem chú thích trên bảng nhé !
Thanh ghi EPKTCNT ở bank2 lưu số lượng gói đang nhận, vì vậy, chúng ta sẽ kiểm tra nó nếu > 0 tức là đang có gói tin trong bộ đệm, hãy đọc nó ra !
Giờ hãy tạo một hàm để đọc một gói trong tệp enc28j60.c và tạo một vài biến cục bộ để phục vụ tính toán và thêm 1 biến toàn cục để lưu vị trí cho gói tiếp theo (nhớ thêm nguyên mẫu hàm vào file .h)
1 2 3 4 5 6 7 8 9 10 |
uint16_t ENC28J60_read_packet(uint8_t *buf,uint16_t buflen) { uint16_t status; uint16_t len=0; if(ENC28J60_readByte(EPKTCNT)>0) // neu co goi tin { } return len; } |
Và tạo #define cho thanh ghi EPKTCNT
1 2 |
// Bank 1 registers #define EPKTCNT (0x19|0x20) |
Mình sẽ tạo thêm 1 biến toàn cục tên là NextPacketPtr, biến này sẽ lưu vị trí của gói tin tiếp theo. Khi khởi động, gói tin sẽ bắt đầu ở vị trí RXSTART_INIT nên các bạn gán vị trí này cho biến NextPacketPtr ngay trong hàm khởi tạo luôn nhé !
Tiếp tục quay trở lại hàm ENC28J60_read_packet, khi kiểm tra và thấy có gói tin mới, chúng ta sẽ đọc nó ra theo các bước sau:
- Đặt con trỏ đọc ERDPT vào vị tri bắt đầu của gói tin
- Đọc 2 byte đầu tiên trong bộ đệm, đây sẽ là vị trí bắt đầu của gói tin tiếp theo -> nó cũng chính là vị trí kết thúc của gói tin hiện tại
- Đọc 2 byte tiếp theo trong bộ đệm, nó chính là độ dài của gói tin [Status vector 0:15]
- Đọc 2 byte tiếp theo trong bộ đệm, nó chứa thông tin trạng thái nhận [Status vector 16:31]
- Bắt đầu từ bây giờ chính là dữ liệu của gói, các bạn đọc từ vị trí này tới hết độ dài của gói tin nhé, chúng ta có thể trừ đi 4 byte checksum ở cuối nếu không cần kiểm tra lại checksum
- Set ERXRDP tới vị trí của gói tin tiếp theo
- Sau khi đọc xong chúng ta set bit PKTDEC trong thanh ghi ECON2 để EPKTCNT giảm đi 1
Để đọc buffer, các bạn phải gửi command ở nhóm lệnh có Opcode là Read Buffer Memory = 0x3A. Bởi vậy hãy thêm #define cho nó nhé
1 |
#define ENC28J60_READ_BUF_MEM 0x3A |
Mình sẽ tạo thêm 1 hàm để đọc 1 byte từ buffer cho tiện hen ( nhớ thêm nguyên mẫu hàm cho file .h)
1 2 3 4 |
uint8_t ENC28J60_read_Byte_Buffer(void) { return ENC28J60_read_command(ENC28J60_READ_BUF_MEM,0); } |
Do nhóm lệnh Read Buffer Memory nó nhận data vào là n/a nên chúng ta nạp vào là gì cũng được. Ở đây mình gửi value=0 luôn.
Đọc 1 byte thì đương nhiên sẽ có đọc nhiều byte, thay vì gọi hàm ENC28J60_read_Byte_Buffer liên tục sẽ chậm (do gửi 2 byte để đọc về 1 byte), chip hỗ trợ chúng ta 1 cách đọc nhanh hơn nhiều, không cần phải gửi Opcode luôn tục)
Cách đọc 1 khối data lớn trong Read Buffer Memory
- CS Enable
- Gửi Opcode Read Buffer Memory
- Liên tục đọc data ra qua giao thức SPI
- CS Disable
Bí quyết nằm ở chỗ chúng ta không tắt chân CS nên chip sẽ không reset Opcode đã gửi, do vậy không cần gửi đi gửi lại Opcode nhiều lần
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void ENC28J60_ReadBuffer(uint16_t len, uint8_t* data) { SS_SELECT(); SPI_SendByte(ENC28J60_READ_BUF_MEM); //gui Opcode while(len) { len--; // read data *data = (uint8_t)SPI_ReceiveByte(); data++; } *data='\0'; //set end buffer SS_DESELECT(); } |
Được rồi, giờ viết nối nội dung cho hàm ENC28J60_read_packet nhé !
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
//Thiet lap con tro de doc du lieu tu goi tin nhan duoc ENC28J60_writeByte16(ERDPT,NextPacketPtr); //Doc gia tri con tro cua goi tin tiep theo NextPacketPtr=ENC28J60_read_Byte_Buffer(); NextPacketPtr|=ENC28J60_read_Byte_Buffer()<8; // Doc kich thuoc cua goi tin len = ENC28J60_read_Byte_Buffer(); len |= ENC28J60_read_Byte_Buffer()<<8; len-=4; // xoa 4byte checksum o cuoi if(len>buflen) len=buflen; //Doc trang thai cua bo nhan status = ENC28J60_read_Byte_Buffer(); status |= ENC28J60_read_Byte_Buffer()<<8; if ((status & 0x80)==0)len=0; //kiem tra bit Received Ok, neu err thi khong doc goi nay else { //doc du lieu trong bo dem ENC28J60_ReadBuffer(len, buf); } // Chuyen con tro du lieu nhan toi phan dau cua goi tin tiep theo. if(NextPacketPtr-1>RXSTOP_INIT)ENC28J60_writeByte16(ERXRDPT,RXSTOP_INIT); else ENC28J60_writeByte16(ERXRDPT,NextPacketPtr); ENC28J60_write_command(ENC28J60_BIT_FIELD_SET,ECON2,ECON2_PKTDEC); |
Nhớ #define địa chỉ cho thanh ghi ECON2
1 2 3 |
// ENC28J60 ECON2 Register Bit Definitions #define ECON2 0x1E #define ECON2_PKTDEC 0x40 |
Viết chức năng gửi các gói tin ( ghi bộ đệm ethernet)
Chương trình gửi gói cũng tương tự như đọc
Hàm gửi buffer ra cho chip ENC28J60
1 2 3 4 5 6 7 8 |
static void ENC28J60_writeBuf(uint16_t len,uint8_t* data) { SS_SELECT(); SPI_SendByte(ENC28J60_WRITE_BUF_MEM); while(len--) SPI_SendByte(*data++); SS_DESELECT(); } |
Thêm nguyễn mẫu hàm và #define cho Opcode ENC28J60_WRITE_BUF_MEM
1 |
#define ENC28J60_WRITE_BUF_MEM 0x7A |
Các bước thiết lập để truyền gói tin – lập trình enc28j60 tutorial
- Kiểm tra bit TXRTS trong thanh ghi ECON1, nếu nó đang được xóa thì chip sẵn sàng để truyền
- Cài thanh ghi EWRPT (16bit) (con trỏ truyền) địa chỉ bắt đầu lưu bộ đệm truyền
- Cài thanh ghi ETXND (16bit) địa chỉ của byte cuối cùng trong bộ đệm
- Mở đầu bằng cách truyền byte control=0x00 vào bộ đệm
- Truyền gói tin sang cho chip
- Set bit TXRTS trong thanh ghi lên 1 để chip ENC28J60 bắt đầu truyền gói tin lên mạng
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
void ENC28J60_send_packet(uint8_t *buf,uint16_t buflen) { //cho qua trinh truyen truoc do hoan tat while(ENC28J60_readByte(ECON1)&ECON1_TXRTS); // Thiet lap con tro bat dau khong gian bo dem truyen ENC28J60_writeByte16(EWRPT,TXSTART_INIT); //Thiet lap con tro toi vi tri cua byte cuoi cua goi tin ENC28J60_writeByte16(ETXND,TXSTART_INIT+buflen); //truyen byte dau tien la 0x00 vao bo dem ENC28J60_write_command(ENC28J60_WRITE_BUF_MEM,0,0); //truyen goi tin vao bo dem ENC28J60_writeBuf(buflen,buf); //set bit TXRTS cho enc28j60 truyen goi tin ENC28J60_write_command(ENC28J60_BIT_FIELD_SET,ECON1,ECON1_TXRTS); } |
Thêm nguyên mẫu cho hàm và thêm #define cho thanh ghi EIR vào file .h
1 2 3 4 5 6 7 8 9 |
// ENC28J60 EIR Register Bit Definitions #define EIR 0x1C #define EIR_PKTIF 0x40 #define EIR_DMAIF 0x20 #define EIR_LINKIF 0x10 #define EIR_TXIF 0x08 #define EIR_WOLIF 0x04 #define EIR_TXERIF 0x02 #define EIR_RXERIF 0x01 |
Như vậy, mình đã hướng dẫn các bạn viết xong các hàm cơ bản để giao tiếp với chip ENC28J60, bắt đầu từ bây giờ, chúng ta sẽ làm việc, mổ xẻ, bóc tách nội dung bên trong của các gói tin, xử lí các gói tin !
Các bạn có thể tải source của bài hôm nay tại đây
Bài tiếp theo – lập trình enc28j60 tutorial
Cái biến NextPacketPtr sau khi nó đã lớn hơn RXSTOP_INIT, có cần phải reset về RXSTART_INIT không vậy Ad
ví trí của NextPacketPtr do ic enc28j60 quyết định nhé