EMAC driver for the ENC28J60 Ethernet controller. This is a simplified fork of https://github.com/tobiasjaster/ENC28J60-EMAC-Driver published by Tobias Jaster.
Dependents: MQTT_Hello MQTT_HelloENC28J60
EMAC driver for the ENC28J60 Ethernet controller
This is a fork (the INT and RST pins are not used) of the ENC28J60-EMAC driver published by Tobias Jaster at
https://github.com/tobiasjaster/ENC28J60-EMAC-Driver
Usage:
- Connect the ENC28J60 module to the Mbed board as follows:
- Import (add) this ENC28J60-EMAC library to your program.
- Add a "mbed_app.json" file with the following content to the root directory of your program:
{ "target_overrides": { "*": { "platform.callback-nontrivial": true, "enc28j60-emac.mosi": "D11", "enc28j60-emac.miso": "D12", "enc28j60-emac.sck" : "D13", "enc28j60-emac.cs" : "D10" } } }
- Replace "D11", ..., "D10" with the actual pin names you selected on the Mbed board to be used for the SPI communication.
- To set the MAC address define an array with the desired address bytes and call the "set_hwaddr(mac)" function before calling the network interface "connect" function.
For example:
const uint8_t MAC[6] = { 0, 1, 2, 3, 4, 5 }; EthernetInterface net; int main() { net.get_emac().set_hwaddr(MAC); // set MAC address if (net.connect() != 0) { printf("Error: Unable to connect to the network.\n"); return -1; } ...
Diff: enc28j60_emac.cpp
- Revision:
- 0:b599e748252c
- Child:
- 1:bce04bfc41fe
diff -r 000000000000 -r b599e748252c enc28j60_emac.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/enc28j60_emac.cpp Fri Mar 26 15:58:28 2021 +0000 @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2019 Tobias Jaster + * + * Modified by Zoltan Hudak + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "mbed_interface.h" +#include "mbed_wait_api.h" +#include "mbed_assert.h" +#include "netsocket/nsapi_types.h" +#include "mbed_shared_queues.h" + +#include "enc28j60_emac.h" + +/** + * @brief + * @note + * @param + * @retval + */ +W5500_EMAC::W5500_EMAC() : + _enc28j60(new ENC28J60(ENC28J60_MOSI, ENC28J60_MISO, ENC28J60_SCK, ENC28J60_CS)), + _prev_link_status_up(PHY_STATE_LINK_DOWN), + _link_status_task_handle(0), + _receive_task_handle(0), + _memory_manager(NULL) +{ } + +/** + * @brief + * @note Allocates buffer chain from a pool and filles it with incoming packet. + * @param + * @retval Data from incoming packet + */ +emac_mem_buf_t* W5500_EMAC::low_level_input() +{ + emac_mem_buf_t* chain; + emac_mem_buf_t* buf; + packet_t packet; + + if (_enc28j60->getPacketInfo(&packet) != ENC28J60_ERROR_OK) { + return NULL; + } + + if (packet.payload.len == 0) { + return NULL; + } + + // Allocate a buffer chain from the memory pool. + chain = _memory_manager->alloc_pool(packet.payload.len, ENC28J60_BUFF_ALIGNMENT); + buf = chain; + + // Iterate through the buffer chain and fill it with packet payload. + while (buf != NULL) { + packet.payload.buf = (uint8_t*)_memory_manager->get_ptr(buf); + packet.payload.len = (uint16_t) (_memory_manager->get_len(buf)); + + // Fill the ethernet stack buffer with packet payload received by ENC28J60. + _enc28j60->readPacket(&packet); + + buf = _memory_manager->get_next(buf); + } + + // Return the buffer chain filled with packet payload. + return chain; +} + +/** + * @brief Receive task. + * @note Passes packet payload received by ENC28J60 to the ethernet stack + * @param + * @retval + */ +void W5500_EMAC::receive_task() +{ + emac_mem_buf_t* payload; + + _ethLockMutex.lock(); + payload = low_level_input(); + if (payload != NULL) { + if (_emac_link_input_cb) { + _emac_link_input_cb(payload); // pass packet payload to the ethernet stack + _memory_manager->free(payload); + } + } + + _enc28j60->freeRxBuffer(); // make room in ENC28J60 receive buffer for new packets + _ethLockMutex.unlock(); +} + + +/** + * @brief + * @note Passes a packet payload from the ethernet stack to ENC28J60 for transmittion + * @param + * @retval + */ +bool W5500_EMAC::link_out(emac_mem_buf_t* buf) +{ + emac_mem_buf_t* chain = buf; + packet_t packet; + enc28j60_error_t error; + + if (buf == NULL) { + return false; + } + + _ethLockMutex.lock(); + // Iterate through the buffer chain and fill the packet with payload. + while (buf != NULL) { + packet.payload.buf = (uint8_t *) (_memory_manager->get_ptr(buf)); + packet.payload.len = _memory_manager->get_len(buf); + error = _enc28j60->loadPacketInTxBuffer(&packet); + if (error != ENC28J60_ERROR_OK) { + _memory_manager->free(chain); + _ethLockMutex.unlock(); + return false; + } + + error = _enc28j60->transmitPacket(&packet); + if (error != ENC28J60_ERROR_OK) { + _memory_manager->free(chain); + _ethLockMutex.unlock(); + return false; + } + + buf = _memory_manager->get_next(buf); + } + + _memory_manager->free(chain); + _ethLockMutex.unlock(); + + return true; +} + +/** + * @brief + * @note + * @param + * @retval + */ +void W5500_EMAC::link_status_task() +{ + uint16_t phy_basic_status_reg_value = 0; + bool current_link_status_up = false; + + /* Get current status */ + + _ethLockMutex.lock(); + _enc28j60->phyRead(PHSTAT2, &phy_basic_status_reg_value); + + current_link_status_up = (bool) ((phy_basic_status_reg_value & PHSTAT2_LSTAT) != 0); + + /* Compare with previous state */ + if (current_link_status_up != _prev_link_status_up) { + if (_emac_link_state_cb) { + _emac_link_state_cb(current_link_status_up); + } + + _prev_link_status_up = current_link_status_up; + } + + _ethLockMutex.unlock(); +} + +/** + * @brief + * @note + * @param + * @retval + */ +bool W5500_EMAC::power_up() +{ + volatile uint32_t timeout = 500; + _enc28j60->writeOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV); + while (_enc28j60->readReg(ESTAT_CLKRDY) == 0) { + ThisThread::sleep_for(1ms); + timeout--; + if (timeout == 0) { + return false; + } + } + + /* Trigger thread to deal with any RX packets that arrived + * before receiver_thread was started */ + _receive_task_handle = mbed::mbed_event_queue()->call_every + ( + RECEIVE_TASK_PERIOD_MS, + mbed::callback(this, &W5500_EMAC::receive_task) + ); + + _prev_link_status_up = PHY_STATE_LINK_DOWN; + mbed::mbed_event_queue()->call(mbed::callback(this, &W5500_EMAC::link_status_task)); + + /* Allow the Link Status task to detect the initial link state */ + ThisThread::sleep_for(10ms); + _link_status_task_handle = mbed::mbed_event_queue()->call_every + ( + LINK_STATUS_TASK_PERIOD_MS, + mbed::callback(this, &W5500_EMAC::link_status_task) + ); + + return true; +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint32_t W5500_EMAC::get_mtu_size() const +{ + return ENC28J60_ETH_MTU_SIZE; +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint32_t W5500_EMAC::get_align_preference() const +{ + return ENC28J60_BUFF_ALIGNMENT; +} + +/** + * @brief + * @note + * @param + * @retval + */ +void W5500_EMAC::get_ifname(char* name, uint8_t size) const +{ + memcpy(name, ENC28J60_ETH_IF_NAME, (size < sizeof(ENC28J60_ETH_IF_NAME)) ? size : sizeof(ENC28J60_ETH_IF_NAME)); +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint8_t W5500_EMAC::get_hwaddr_size() const +{ + return ENC28J60_HWADDR_SIZE; +} + +/** + * @brief + * @note + * @param + * @retval + */ +bool W5500_EMAC::get_hwaddr(uint8_t* addr) const +{ + enc28j60_error_t error = _enc28j60->readMacAddr((char*)addr); + if (error == ENC28J60_ERROR_OK) { + return true; + } + else { + return false; + } +} + +/** + * @brief + * @note + * @param + * @retval + */ +void W5500_EMAC::set_hwaddr(const uint8_t* addr) +{ + if (!addr) { + return; + } + + memcpy(_hwaddr, addr, sizeof _hwaddr); + _ethLockMutex.lock(); + + enc28j60_error_t error = _enc28j60->writeMacAddr((char*)addr); + _ethLockMutex.unlock(); + if (error) { + return; + } +} + +/** + * @brief + * @note + * @param + * @retval + */ +void W5500_EMAC::set_link_input_cb(emac_link_input_cb_t input_cb) +{ + _emac_link_input_cb = input_cb; +} + +/** + * @brief + * @note + * @param + * @retval + */ +void W5500_EMAC::set_link_state_cb(emac_link_state_change_cb_t state_cb) +{ + _emac_link_state_cb = state_cb; +} + +/** + * @brief + * @note + * @param + * @retval + */ +void W5500_EMAC::add_multicast_group(const uint8_t* addr) +{ + // No action for now +} + +/** + * @brief + * @note + * @param + * @retval + */ +void W5500_EMAC::remove_multicast_group(const uint8_t* addr) +{ + // No action for now +} + +/** + * @brief + * @note + * @param + * @retval + */ +void W5500_EMAC::set_all_multicast(bool all) +{ + // No action for now +} + +/** + * @brief + * @note + * @param + * @retval + */ +void W5500_EMAC::power_down() +{ + _enc28j60->disableMacRecv(); + if (_enc28j60->readReg(ESTAT_RXBUSY) != 0) { + _enc28j60->enableMacRecv(); + return; + } + + if (_enc28j60->readReg(ECON1_TXRTS) != 0) { + _enc28j60->enableMacRecv(); + return; + } + + _enc28j60->writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_VRPS); + _enc28j60->writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void W5500_EMAC::set_memory_manager(EMACMemoryManager& mem_mngr) +{ + _memory_manager = &mem_mngr; +} + +/** + * @brief + * @note + * @param + * @retval + */ +W5500_EMAC& W5500_EMAC::get_instance() +{ + static W5500_EMAC emac; + return emac; +} + +EMAC& EMAC::get_default_instance() +{ + return W5500_EMAC::get_instance(); +}