Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
LPC1769Emac.cpp
- Committer:
- moccos
- Date:
- 2012-05-06
- Revision:
- 0:b4bf563e9741
- Child:
- 1:95a4c234aaf6
File content as of revision 0:b4bf563e9741:
#include "LPC1769Emac.h"
#include <LPC1768/ARM/cmsis.h>
#include <mbed.h>
#include "LAN8710AReg.h"
#include "LPC1769Reg.h"
#include "Frame.h"
#define BUFFER_SECTION __attribute((section("AHBSRAM1"),aligned(8)))
#define SET_EMAC_BIT(NAME, PATTERN) (LPC_EMAC->NAME = PATTERN}
#define ENABLE_EMAC_BIT(NAME, MASK) (LPC_EMAC->NAME |= MASK}
#define DISABLE_EMAC_BIT(NAME, MASK)(LPC_EMAC->NAME = LPC_EMAC->NAME & ~MASK}
using namespace LPC1769Reg;
uint8_t LPC1769Emac::mac_[6];
Descriptor LPC1769Emac::rx_desc_[LPC1769Emac::N_RX_BUF] BUFFER_SECTION;
Descriptor LPC1769Emac::tx_desc_[LPC1769Emac::N_TX_BUF] BUFFER_SECTION;
StatusRx LPC1769Emac::rx_status_[LPC1769Emac::N_RX_BUF] BUFFER_SECTION;
StatusTx LPC1769Emac::tx_status_[LPC1769Emac::N_TX_BUF] BUFFER_SECTION;
Frame LPC1769Emac::rx_frame_[LPC1769Emac::N_RX_BUF] BUFFER_SECTION;
Frame LPC1769Emac::tx_frame_[LPC1769Emac::N_TX_BUF] BUFFER_SECTION;
// === inline debug function
#ifdef __MY_DEBUG__
#include "global.h"
static inline void print_buffer(const char* title, uint8_t *buf, uint16_t size) {
serial.printf("%s (%d bytes) dst:%02x %02x %02x %02x %02x %02x ",
title, size, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
buf += 6;
serial.printf("src:%02x %02x %02x %02x %02x %02x ",
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
buf += 6;
serial.printf("type:%02x %02x\r\n", buf[0], buf[1]);
}
#else
static inline void print_buffer(const char* title, uint8_t *buf, uint16_t size){}
#endif
// === public functions ===
LPC1769Emac::LPC1769Emac() {
// set address
// enable ethernet block
LPC_SC->PCONP |= 0x40000000;
// enable alternate functions for ethernet.
// old values are not saved because those pins have only two functions.
LPC_PINCON->PINSEL2 = 0x50150105;
LPC_PINCON->PINSEL3 = (LPC_PINCON->PINSEL3 & ~0x0000000F) | 0x00000005;
}
LPC1769Emac::~LPC1769Emac() {
// TODO: disable PHY before disable ethernet block?
// disable alternate functions for ethernet
LPC_PINCON->PINSEL2 = 0x00000000;
LPC_PINCON->PINSEL3 = LPC_PINCON->PINSEL3 & ~0x0000000F;
// disable ethernet block
LPC_SC->PCONP = LPC_SC->PCONP & ~0x40000000;
}
bool
LPC1769Emac::PhyWrite(uint8_t reg, uint16_t value) {
LPC_EMAC->MADR = PHY_ADDR | reg;
LPC_EMAC->MWTD = value;
uint32_t i;
for (i = 0; i < 0x10000; i++) {
if ((LPC_EMAC->MIND & MIND_BUSY) == 0) {
return true;
}
}
return false;
}
uint16_t
LPC1769Emac::PhyRead(uint16_t reg) {
LPC_EMAC->MADR = PHY_ADDR | reg;
LPC_EMAC->MCMD = MCMD_READ;
uint32_t i;
for (i = 0; i < 0x10000; i++) {
if ((LPC_EMAC->MIND & MIND_BUSY) == 0) {
break;
}
}
LPC_EMAC->MCMD = 0x0000;
return LPC_EMAC->MRDD;
}
void
LPC1769Emac::SetAddress(uint8_t a5, uint8_t a4, uint8_t a3, uint8_t a2, uint8_t a1, uint8_t a0) {
mac_[5] = a0;
mac_[4] = a1;
mac_[3] = a2;
mac_[2] = a3;
mac_[1] = a4;
mac_[0] = a5;
//WriteAddress_();
}
void
LPC1769Emac::StartRx() {
LPC_EMAC->Command |= CMD_RX_EN;
LPC_EMAC->MAC1 |= MAC1_RX_EN;
//LPC_EMAC->IntEnable |= INT_RX_DONE;
}
void
LPC1769Emac::StartTx() {
LPC_EMAC->Command |= CMD_TX_EN;
//LPC_EMAC->IntEnable |= INT_TX_DONE;
}
void
LPC1769Emac::StopRx() {
LPC_EMAC->Command = LPC_EMAC->Command & ~CMD_RX_EN;
LPC_EMAC->MAC1 &= ~MAC1_RX_EN;
//LPC_EMAC->IntEnable &= ~INT_RX_DONE;
}
void
LPC1769Emac::StopTx() {
LPC_EMAC->Command = LPC_EMAC->Command & ~CMD_TX_EN;
//LPC_EMAC->IntEnable &= ~INT_TX_DONE;
}
bool
LPC1769Emac::Link() {
return ((PhyRead(SMIReg::BasicStatus) & SMIReg::BS_LINK) > 0);
}
uint16_t
LPC1769Emac::Recv(void *buf, uint16_t max_size) {
uint32_t index = LPC_EMAC->RxConsumeIndex;
if (index == LPC_EMAC->RxProduceIndex || max_size == 0) return 0;
uint16_t packet_size = (rx_status_[index].info & Descriptor::SIZE_MASK) + 1;
uint16_t size = packet_size - read_size_;
if (size > Frame::MAX_FRAME_LEN) size = Frame::MAX_FRAME_LEN;
if (size > max_size) size = max_size;
memcpy(buf, read_next_, size);
read_size_ += size;
if (read_size_ >= packet_size) {
// goto next buffer
index = (index >= LPC_EMAC->RxDescriptorNumber) ? 0 : ++index;
read_size_ = 0;
read_next_ = rx_frame_[index].buffer;
LPC_EMAC->RxConsumeIndex = index;
} else {
read_next_ += size;
}
return size;
}
uint16_t
LPC1769Emac::ReadyToReceive() {
uint32_t index = LPC_EMAC->RxConsumeIndex;
if (index == LPC_EMAC->RxProduceIndex) return 0;
else return (rx_status_[index].info & Descriptor::SIZE_MASK) + 1;
}
uint16_t
LPC1769Emac::Write(void *buf, uint16_t size) {
if (size + write_size_ > Frame::MAX_FRAME_LEN) size = Frame::MAX_FRAME_LEN - write_size_;
if (size == 0) return 0;
uint32_t index = LPC_EMAC->TxProduceIndex;
memcpy(write_next_, buf, size);
write_next_ += size;
write_size_ += size;
tx_desc_[index].control = (tx_desc_[index].control & (~Descriptor::SIZE_MASK)) | (size - 1);
return size;
}
bool
LPC1769Emac::Send() {
if (write_size_ == 0) return true;
uint32_t index = LPC_EMAC->TxProduceIndex;
uint32_t index_next = (index == LPC_EMAC->TxDescriptorNumber) ? 0 : index + 1;
if (index_next == LPC_EMAC->TxConsumeIndex) return false;
tx_desc_[index].control = (tx_desc_[index].control & (~Descriptor::SIZE_MASK)) | (write_size_ - 1);
LPC_EMAC->TxProduceIndex = index_next;
write_next_ = tx_frame_[index_next].buffer;
write_size_ = 0;
return true;
}
bool
LPC1769Emac::Send(void *buf, uint16_t size) {
write_next_ -= write_size_;
write_size_ = 0;
if (!Write(buf, size)) return false;
return Send();
}
bool
LPC1769Emac::Reset(LinkMode mode) {
bool link_100m = false;
bool duplex_full = false;
switch (mode) {
case HalfDuplex10:
break;
case FullDuplex10:
duplex_full = true;
break;
case HalfDuplex100:
link_100m = true;
break;
case FullDuplex100:
duplex_full = true;
link_100m = true;
break;
case AutoNegotiate:
duplex_full = true;
link_100m = true;
break;
default:
return false;
}
// reset EMAC
LPC_EMAC->MAC1 = MAC1_RESET_MASK;
LPC_EMAC->Command = CMD_RESET_MASK;
wait_us(100);
// emac settings
LPC_EMAC->MCFG = MCFG_RESET_MII | MCFG_CS_DIV52; // reset MII Management
wait_us(200);
LPC_EMAC->MAC1 = 0x0000;
LPC_EMAC->CLRT = CLRT_DEFAULT;
LPC_EMAC->MAXF = Frame::MAX_FRAME_LEN;
LPC_EMAC->IPGR = IPGR_DEFAULT;
LPC_EMAC->MCFG = MCFG_CS_DIV52;
if (duplex_full) {
LPC_EMAC->MAC2 = MAC2_AUTO_PAD | MAC2_FULL_DUPLEX;
LPC_EMAC->IPGT = IPGT_FULL_DUPLEX;
LPC_EMAC->Command = CMD_RMII | CMD_FULL_DUPLEX;
} else {
LPC_EMAC->MAC2 = MAC2_AUTO_PAD;
LPC_EMAC->IPGT = IPGT_HALF_DUPLEX;
LPC_EMAC->Command = CMD_RMII;
}
LPC_EMAC->SUPP = link_100m ? SUPP_100M_MODE : 0x0000;
// reset PHY
// "the reset process will be completed within 0.5s from the setting of this bit" (8710a datasheet)
PhyWrite(SMIReg::BasicControl, SMIReg::BC_RESET);
uint32_t i;
bool success_flag = false;
for (i = 0; i < 500; i++) {
if ((PhyRead(SMIReg::BasicControl) & SMIReg::BC_RESET) == 0) { // reset bit is self-clearing
success_flag = true;
break;
}
wait_ms(1);
}
if (!success_flag) return false; // failed to reset
// why 100M settings fail??
switch (mode) {
case HalfDuplex10:
PhyWrite(SMIReg::BasicControl, 0x0000);
break;
case FullDuplex10:
PhyWrite(SMIReg::BasicControl, SMIReg::BC_FULL_DUPLEX);
break;
case HalfDuplex100:
PhyWrite(SMIReg::BasicControl, SMIReg::BC_100M);
break;
case FullDuplex100:
PhyWrite(SMIReg::BasicControl, SMIReg::BC_FULL_DUPLEX | SMIReg::BC_100M);
break;
case AutoNegotiate:
PhyWrite(SMIReg::BasicControl, SMIReg::BC_AUTONEG);
// CheckAutoNeg_();
break;
default:
return false;
}
InitRxBuffer_();
InitTxBuffer_();
WriteAddress_();
LPC_EMAC->IntClear = INT_ALL_MASK;
LPC_EMAC->RxFilterCtrl = RXFC_BCAST | RXFC_PERFECT;
return true;
}
// === private functions ===
void
LPC1769Emac::WriteAddress_() {
LPC_EMAC->SA0 = ((uint16_t)mac_[5] << 8) | mac_[4];
LPC_EMAC->SA1 = ((uint16_t)mac_[3] << 8) | mac_[2];
LPC_EMAC->SA2 = ((uint16_t)mac_[1] << 8) | mac_[0];
}
// currently not used
bool
LPC1769Emac::CheckAutoNeg_() {
bool success_flag = false;
uint32_t i, r;
for (i = 0; i < 0x10000; i++) {
r = PhyRead(SMIReg::BasicStatus);
if (r & SMIReg::BS_AUTONEG_COMPLETE) {
success_flag = true;
break;
}
}
if (!success_flag) return false;
r = PhyRead(SMIReg::PHYSpecialControlStatus);
// update regs
bool link_100m = ((r & SMIReg::SP_100M) > 0);
bool duplex_full = ((r & SMIReg::SP_FULL_DUPLEX) > 0);
if (duplex_full) {
LPC_EMAC->MAC2 = MAC2_AUTO_PAD | MAC2_FULL_DUPLEX;
LPC_EMAC->IPGT = IPGT_FULL_DUPLEX;
LPC_EMAC->Command = CMD_RMII | CMD_FULL_DUPLEX;
} else {
LPC_EMAC->MAC2 = MAC2_AUTO_PAD;
LPC_EMAC->IPGT = IPGT_HALF_DUPLEX;
LPC_EMAC->Command = CMD_RMII;
}
LPC_EMAC->SUPP = link_100m ? SUPP_100M_MODE : 0x0000;
return true;
}
void
LPC1769Emac::InitRxBuffer_() {
uint16_t i;
// set initial descriptor
for (i = 0; i < N_RX_BUF; i++) {
rx_desc_[i].packet = (uint32_t)&rx_frame_[i];
rx_desc_[i].control = 0x80000000 | (Frame::MAX_FRAME_LEN - 1);
}
// emac registers
LPC_EMAC->RxDescriptor = (uint32_t)rx_desc_;
LPC_EMAC->RxStatus = (uint32_t)rx_status_;
LPC_EMAC->RxDescriptorNumber = N_RX_BUF - 1; // this register is minus one encoded
LPC_EMAC->RxConsumeIndex = 0;
read_next_ = rx_frame_[0].buffer;
read_size_ = 0;
}
void
LPC1769Emac::InitTxBuffer_() {
uint16_t i;
// set initial descriptor
for (i = 0; i < N_TX_BUF; i++) {
tx_desc_[i].packet = (uint32_t)&tx_frame_[i];
tx_desc_[i].control = 0xf4000000 | Frame::MAX_FRAME_LEN;
tx_status_[i].info = 0;
}
// emac registers
LPC_EMAC->TxDescriptor = (uint32_t)tx_desc_;
LPC_EMAC->TxStatus = (uint32_t)tx_status_;
LPC_EMAC->TxDescriptorNumber = N_TX_BUF - 1; // this register is minus one encoded
LPC_EMAC->TxProduceIndex = 0;
write_next_ = tx_frame_[0].buffer;
write_size_ = 0;
}