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.h
- Revision:
- 1:bce04bfc41fe
- Parent:
- 0:b599e748252c
- Child:
- 2:19d290369c66
--- a/enc28j60_emac.h Fri Mar 26 15:58:28 2021 +0000 +++ b/enc28j60_emac.h Sat Mar 27 22:45:46 2021 +0000 @@ -15,173 +15,216 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef ENC28J60_EMAC_H_ -#define ENC28J60_EMAC_H_ +#ifndef ENC28J60_ETH_DRV_H_ +#define ENC28J60_ETH_DRV_H_ -#include <ctype.h> -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <stdint.h> #include "mbed.h" -#include "EMAC.h" -#include "rtos.h" - #include "enc28j60_reg.h" -#include "enc28j60.h" #include "enc28j60_emac_config.h" -class W5500_EMAC : - public EMAC +#define ENC28J60_READWRITE 1 + +/** + * \brief Error code definitions + * + */ +typedef struct +{ + uint8_t* buf; + uint16_t len; +} payload_t; + +typedef struct +{ + uint32_t addr; + payload_t payload; +} packet_t; + +/** + * \brief Error code definitions + * + */ +typedef enum +{ + ENC28J60_ERROR_OK = 0U, /*!< no error */ + ENC28J60_ERROR_TIMEOUT = 1U, /*!< timeout */ + ENC28J60_ERROR_BUSY = 2U, /*!< no error */ + ENC28J60_ERROR_PARAM = 3U, /*!< invalid parameter */ + ENC28J60_ERROR_INTERNAL = 4U, /*!< internal error */ + ENC28J60_ERROR_WRONG_ID = 5U, /*!< internal error */ + ENC28J60_ERROR_NOPACKET = 10U, + ENC28J60_ERROR_RECEIVE = 11U, + ENC28J60_ERROR_LASTPACKET = 12U, + ENC28J60_ERROR_POSITIONLENGTH = 13U, /*!< internal error */ + ENC28J60_ERROR_SIZE = 20U, /*!< internal error */ + ENC28J60_ERROR_FIFOFULL = 21U, /*!< internal error */ + ENC28J60_ERROR_NEXTPACKET = 22U, /*!< internal error */ +} enc28j60_error_t; + +/** + * \brief Interrupt source definitions + * + */ +typedef enum +{ + ENC28J60_INTERRUPT_ENABLE = EIE_INTIE, + ENC28J60_INTERRUPT_RX_PENDING_ENABLE= EIE_PKTIE, + ENC28J60_INTERRUPT_DMA_ENABLE = EIE_DMAIE, + ENC28J60_INTERRUPT_LINK_STATE_ENABLE= EIE_LINKIE, + ENC28J60_INTERRUPT_TX_ENABLE = EIE_TXIE, + ENC28J60_INTERRUPT_TX_ERROR_ENABLE = EIE_TXERIE, + ENC28J60_INTERRUPT_RX_ERROR_ENABLE = EIE_RXERIE +} enc28j60_interrupt_source; + +class ENC28J60 { public: - W5500_EMAC(); + ENC28J60(PinName mosi, PinName miso, PinName sclk, PinName cs); - /** Return the ENC28J60 EMAC - * - * Returns the default on-board EMAC - this will be target-specific, and - * may not be available on all targets. - */ - static W5500_EMAC& get_instance(void); - - /** - * Return maximum transmission unit - * - * @return MTU in bytes - */ - virtual uint32_t get_mtu_size(void) const; + ENC28J60(mbed::SPI * spi, PinName cs); /** - * Gets memory buffer alignment preference + * \brief Initializes ENC28J60 Ethernet controller to a known default state: + * - device ID is checked + * - global interrupt is enabled, but all irq sources are disabled + * - Establish link enabled + * - Rx enabled + * - Tx enabled + * Init should be called prior to any other process and + * it's the caller's responsibility to follow proper call order. * - * Gets preferred memory buffer alignment of the Emac device. IP stack may - * or may not align link out memory buffer chains using the alignment. - * - * @return Memory alignment requirement in bytes + * \return error code /ref enc28j60_error_t */ - virtual uint32_t get_align_preference(void) const; + void init(void); - /** - * Return interface name - * - * @param name Pointer to where the name should be written - * @param size Maximum number of character to copy - */ - virtual void get_ifname(char* name, uint8_t size) const; + /** This returns a unique 6-byte MAC address, based on the device UID + * This function overrides hal/common/mbed_interface.c function + * @param mac A 6-byte array to write the MAC address + */ + void mbed_mac_address(char* mac); + MBED_WEAK uint8_t mbed_otp_mac_address(char* mac); + void mbed_default_mac_address(char* mac); /** - * Returns size of the underlying interface HW address size. + * \brief Initiates a soft reset, returns failure or success. * - * @return HW address size in bytes + * \return error code /ref enc28j60_error_t */ - virtual uint8_t get_hwaddr_size(void) const; + enc28j60_error_t softReset(void); /** - * Return interface-supplied HW address - * - * Copies HW address to provided memory, @param addr has to be of correct - * size see @a get_hwaddr_size + * \brief Set maximum transition unit by Rx fifo size. + * Note: The MTU will be smaller by 512 bytes, + * because the status uses this fixed space. * - * HW address need not be provided if this interface does not have its own - * HW address configuration; stack will choose address from central system - * configuration if the function returns false and does not write to addr. - * - * @param addr HW address for underlying interface - * @return true if HW address is available + * \param[in] val Size of the fifo in kbytes */ - virtual bool get_hwaddr(uint8_t* addr) const; + void setRxBufSize(uint32_t val); /** - * Set HW address for interface - * - * Provided address has to be of correct size, see @a get_hwaddr_size + * \brief Reset PHY * - * Called to set the MAC address to actually use - if @a get_hwaddr is - * provided the stack would normally use that, but it could be overridden, - * eg for test purposes. - * - * @param addr Address to be set + * \return error code /ref enc28j60_error_t + */ + enc28j60_error_t resetPhy(void); + + /** + * \brief Enable receive */ - virtual void set_hwaddr(const uint8_t* addr); + void enableMacRecv(void); + + /** + * \brief Disable receive + */ + void disableMacRecv(void); /** - * Sends the packet over the link - * - * That can not be called from an interrupt context. + * \brief Read MAC address from EEPROM. * - * @param buf Packet to be send - * @return True if the packet was send successfully, False otherwise + * \param[in,out] mac array will include the read MAC address in + * 6 bytes hexadecimal format. + * It should be allocated by the caller to 6 bytes. + * + * \return error code /ref enc28j60_error_t */ - virtual bool link_out(emac_mem_buf_t* buf); + enc28j60_error_t readMacAddr(char* mac); /** - * Initializes the HW + * \brief Write MAC address to EEPROM. * - * @return True on success, False in case of an error. + * \param[in,out] mac array will include the write MAC address in + * 6 bytes hexadecimal format. + * It should be allocated by the caller to 6 bytes. + * + * \return error code /ref enc28j60_error_t */ - virtual bool power_up(void); + enc28j60_error_t writeMacAddr(char* mac); /** - * Deinitializes the HW + * \brief Check device ID. * + * \return error code /ref enc28j60_error_t */ - virtual void power_down(void); - - /** - * Sets a callback that needs to be called for packets received for that - * interface - * - * @param input_cb Function to be register as a callback - */ - virtual void set_link_input_cb(emac_link_input_cb_t input_cb); + bool check_id(void); /** - * Sets a callback that needs to be called on link status changes for given - * interface + * \brief Get the data size of the Rx buffer, aka Maximum Transition Unit * - * @param state_cb Function to be register as a callback - */ - virtual void set_link_state_cb(emac_link_state_change_cb_t state_cb); - - /** Add device to a multicast group - * - * @param address A multicast group hardware address + * \return Fifo data size in bytes */ - virtual void add_multicast_group(const uint8_t* address); + enc28j60_error_t setWritePrt(uint16_t position, uint16_t offset); + enc28j60_error_t transmitPacket(packet_t* packet); + enc28j60_error_t loadPacketInTxBuffer(packet_t* packet); - /** Remove device from a multicast group + /** + * \brief Get the free space of Rx fifo in bytes. * - * @param address A multicast group hardware address + * \param[in] dev Ethernet device structure \ref enc28j60_eth_dev_t + * + * \return Space available to store received data in bytes */ - virtual void remove_multicast_group(const uint8_t* address); + uint32_t getRxBufFreeSpace(void); - /** Request reception of all multicast packets + /** + * \brief Get the size of next unread packet in Rx buffer, using the peak + * register, which is not destructive so can be read asynchronously. + * Warning: In case of heavy receiving load, it's possible this register + * is not perfectly in sync. * - * @param all True to receive all multicasts - * False to receive only multicasts addressed to specified groups + * \param[in] dev Ethernet device structure \ref enc28j60_eth_dev_t + * + * \return Size in bytes of the next packet can be read from Rx fifo, according + * to the peek register. */ - virtual void set_all_multicast(bool all); - - /** Sets memory manager that is used to handle memory buffers - * - * @param mem_mngr Pointer to memory manager - */ - virtual void set_memory_manager(EMACMemoryManager& mem_mngr); + enc28j60_error_t setRxBufReadPtr(uint16_t position); + enc28j60_error_t getPacketInfo(packet_t* packet); + void readPacket(packet_t* packet); + void freeRxBuffer(void); + uint16_t getRecvPointer(void); + uint16_t getWritePointer(void); + void readBuf(uint8_t* data, uint16_t len); + void writeBuf(uint8_t* data, uint16_t len); + uint8_t readReg(uint8_t address); + uint16_t readRegPair(uint8_t address); + void writeReg(uint8_t address, uint8_t data); + void writeRegPair(uint8_t address, uint16_t data); + enc28j60_error_t phyRead(uint8_t address, uint16_t* data); + enc28j60_error_t phyWrite(uint8_t address, uint16_t data); + bool linkStatus(void); + uint8_t readOp(uint8_t op, uint8_t address); + void writeOp(uint8_t op, uint8_t address, uint8_t data); private: - void link_status_task(); - void receive_task(); - bool low_level_init_successful(); - emac_mem_buf_t* low_level_input(); - - ENC28J60* _enc28j60; - bool _prev_link_status_up; - int _link_status_task_handle; - int _receive_task_handle; - EMACMemoryManager* _memory_manager; - Mutex _ethLockMutex; - uint8_t _hwaddr[ENC28J60_HWADDR_SIZE]; - - emac_link_input_cb_t _emac_link_input_cb; - emac_link_state_change_cb_t _emac_link_state_cb; + void _setBank(uint8_t address); + void _read(uint8_t cmd, uint8_t* buf, uint16_t len, bool blocking); + void _write(uint8_t cmd, uint8_t* buf, uint16_t len, bool blocking); +#ifdef ENC28J60_READWRITE + void _readwrite(uint8_t cmd, uint8_t* readbuf, uint8_t* writebuf, uint16_t len, bool blocking); +#endif + SPI* _spi; + Mutex _SPIMutex; + DigitalOut _cs; + uint8_t _bank; + bool _ready; + uint32_t _next; }; -#endif /* ENC28J60_EMAC_H_ */ +#endif /* ENC28J60_ETH_DRV_H_ */