diff --git a/Mieke/SW/ETH/main.c b/Mieke/SW/ETH/main.c new file mode 100644 index 0000000..f35a8fb --- /dev/null +++ b/Mieke/SW/ETH/main.c @@ -0,0 +1,115 @@ +/* + Ethernet shield test program + Copyright (C) 2018 Andreas Mieke + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "stm32f10x.h" +#include "stm32f10x_usart.h" // Keil::Device:StdPeriph Drivers:USART + +#include "w5100.h" +#include "socket.h" + +#include "string.h" + +#define BUFFLEN 256 + +// Socket (TCP) with ID 1 +SOCKET tcp = 1; +uint8_t buffer[BUFFLEN] = {0}; +uint8_t len = 0; + +void USART_SendString(USART_TypeDef *USARTx, uint8_t *str) +{ + // Send a string character by character over UART + while(*str) { + while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET); + USART_SendData(USARTx, *str++); + } +} + +int main() { + // Enable USART 1 and GPIOA clock + RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); + + // Create gpio strct and set default values + GPIO_InitTypeDef gpio; + GPIO_StructInit(&gpio); + + // Set PA9 to alternate function push pull (TxD) + gpio.GPIO_Mode = GPIO_Mode_AF_PP; + gpio.GPIO_Pin = GPIO_Pin_9; + GPIO_Init(GPIOA, &gpio); + + // Set PA10 to input floating (RxD) + gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING; + gpio.GPIO_Pin = GPIO_Pin_10; + GPIO_Init(GPIOA, &gpio); + + // Set up USART1 + USART_InitTypeDef usart; + USART_StructInit(&usart); + USART_Init(USART1, &usart); + + // Set up USART1 clocks + USART_ClockInitTypeDef usartclock; + USART_ClockStructInit(&usartclock); + USART_ClockInit(USART1, &usartclock); + + // Enable USART1 + USART_Cmd(USART1, ENABLE); + + // Initzialize Ethernet module + ETH_init(); + + // Set gateway IP + uint8_t gid[4] = {192, 168, 10, 1}; + + // Set subnet mask + uint8_t sma[4] = {255, 255, 255, 0}; + + // Set MAC address (unused testing address space) + uint8_t mac[6] = {0x1E, 0x8E, 0xA8, 0x88, 0x1C, 0xAA}; + + // Set source IP (IP of the Ethernet module) + uint8_t sip[4] = {192, 168, 10, 2}; + + // Transmit all these values to the module + ETH_set_gateway_IP(gid); + ETH_set_subnet_mask(sma); + ETH_set_mac(mac); + ETH_set_IP(sip); + + // Try to create a TCP socket on port 80 as long as it does not work + do { + ETH_socket(tcp, ETH_SMR_TCP, 80, 0); + } while (ETH_socket_status(tcp) != ETH_SSR_INIT); + + // Wait for the socket to finish creating + while(!ETH_listen(tcp)); + + // Endless loop + do { + // Wait for data to be available + while(ETH_recv_available(tcp) == 0); + // Receive data + buffer[ETH_recv(tcp, buffer, BUFFLEN)] = 0; + // Send data to USART + USART_SendString(USART1, buffer); + // Send string over tcp + ETH_send(tcp, "Hallo, Welt!", 12); + } while (1); +} diff --git a/Mieke/SW/ETH/socket.c b/Mieke/SW/ETH/socket.c new file mode 100644 index 0000000..d0ff773 --- /dev/null +++ b/Mieke/SW/ETH/socket.c @@ -0,0 +1,391 @@ +/* + Ethernet shield test program + Copyright (C) 2018 Andreas Mieke + Copyright (C) 2010 Arduino LLC + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "socket.h" + +// Check if address is a net addres 0.0.0.0 +#define is_net_addr(addr) ((addr[0] == 0x00) && \ + (addr[1] == 0x00) && \ + (addr[2] == 0x00) && \ + (addr[3] == 0x00)) + +// Check if address is broadcast address 255.255.255.255 +#define is_broadcast_addr(addr) ((addr[0] == 0xFF) && \ + (addr[1] == 0xFF) && \ + (addr[2] == 0xFF) && \ + (addr[3] == 0xFF)) + +// Check if port is valid (not 0) +#define is_valid_port(port) (port != 0x00) + +static uint16_t local_port; + +uint8_t ETH_socket(SOCKET s, uint8_t protocol, uint16_t port, uint8_t flag) +{ + // Check if protocol is supported + if ((protocol == ETH_SMR_TCP) || (protocol == ETH_SMR_UDP) || (protocol == ETH_SMR_IPRAW) || (protocol == ETH_SMR_MACRAW) || (protocol == ETH_SMR_PPPoE)) + { + // Close socket, if open + ETH_close(s); + // Set the mode register to the protocol, plus eventual flags + ETH_writeSnMR(s, protocol | flag); + if (port != 0) { + // If port is not 0 (0 on client mode) write port to the port register + ETH_writeSnPORT(s, port); + } + else { + // Write a random port to the port register (for client mode) + local_port++; + ETH_writeSnPORT(s, local_port); + } + + // Execute the open socket command + ETH_exec_socket_cmd(s, ETH_SCR_OPEN); + return 1; + } + return 0; +} + +uint8_t ETH_socket_status(SOCKET s) +{ + // Read the status register and return it + uint8_t tmp = ETH_readSnSR(s); + return tmp; +} + +void ETH_close(SOCKET s) +{ + // Execute close socket and disable interrupts + ETH_exec_socket_cmd(s, ETH_SCR_CLOSE); + ETH_writeSnIR(s, 0xFF); +} + +uint8_t ETH_connect(SOCKET s, uint8_t * addr, uint16_t port) +{ + // Check if port and address is valid + if (is_net_addr(addr) || is_broadcast_addr(addr) || !is_valid_port(port)) + return 0; + + // Write IP to destination register + ETH_writeSnDIPR(s, addr); + // Write port to destination register + ETH_writeSnDPORT(s, port); + // Execute connect + ETH_exec_socket_cmd(s, ETH_SCR_CONNECT); + return 1; +} + +void ETH_disconnect(SOCKET s) +{ + // Execute disconnect + ETH_exec_socket_cmd(s, ETH_SCR_DISCON); +} + +uint8_t ETH_listen(SOCKET s) +{ + // If state is not initzialized, return + if (ETH_readSnSR(s) != ETH_SSR_INIT) { + return 0; + } + // Execute the listen command + ETH_exec_socket_cmd(s, ETH_SCR_LISTEN); + return 1; +} + +uint16_t ETH_send(SOCKET s, const uint8_t * buf, uint16_t len) +{ + uint8_t status=0; + uint16_t ret=0; + uint16_t freesize=0; + + // If data is bigger then Tx memory, split it + if (len > ETH_SSIZE) + ret = ETH_SSIZE; + else + ret = len; + + do + { + // Write data to Tx memory + freesize = ETH_get_TX_free_size(s); + status = ETH_readSnSR(s); + if ((status != ETH_SSR_ESTABLISHED) && (status != ETH_SSR_CLOSE_WAIT)) + { + ret = 0; + break; + } + } + while (freesize < ret); + + // Start data processing and execute send command + ETH_send_data_processing(s, (uint8_t *)buf, ret); + ETH_exec_socket_cmd(s, ETH_SCR_SEND); + + // Check interrupt if everything is okay + while ((ETH_readSnIR(s) & ETH_SIR_SEND_OK) != ETH_SIR_SEND_OK ) + { + // If socket is closed set the state locally right + if (ETH_readSnSR(s) == ETH_SSR_CLOSED ) + { + ETH_close(s); + return 0; + } + } + // Mark interrupt as handled + ETH_writeSnIR(s, ETH_SIR_SEND_OK); + return ret; +} + +int16_t ETH_recv(SOCKET s, uint8_t * buf, int16_t len) +{ + // Check remaining size + int16_t ret = ETH_get_RX_received_size(s); + if (ret == 0) + { + // Read status + uint8_t status = ETH_readSnSR(s); + // If status is wrong, return 0 read bytes + if (status == ETH_SSR_LISTEN || status == ETH_SSR_CLOSED || status == ETH_SSR_CLOSE_WAIT) + { + ret = 0; + } + else + { + // Return -1 if there is nothing to read + ret = -1; + } + } + // If there is more to read then we have space, just read to the supplied limit + else if (ret > len) + { + ret = len; + } + + // If there is data to read and we have space, beginn processing and reading + if (ret > 0) + { + ETH_recv_data_processing(s, buf, ret, 0x00); + ETH_exec_socket_cmd(s, ETH_SCR_RECV); + } + return ret; +} + +int16_t ETH_recv_available(SOCKET s) +{ + // Check if data is available to read + return ETH_get_RX_received_size(s); +} + +uint16_t ETH_peek(SOCKET s, uint8_t *buf) +{ + // Receive one byte + ETH_recv_data_processing(s, buf, 1, 1); + return 1; +} + +uint16_t ETH_sendto(SOCKET s, const uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t port) +{ + uint16_t ret=0; + + // If write size is bigger then memory, limit to memory + if (len > ETH_SSIZE) ret = ETH_SSIZE; + else ret = len; + + // Check if address and port is right + if (is_net_addr(addr) || !is_valid_port(port) || ret == 0) { + ret = 0; + } + else + { + // Set address and port to send to + ETH_writeSnDIPR(s, addr); + ETH_writeSnDPORT(s, port); + + // Process data and send it + ETH_send_data_processing(s, (uint8_t *)buf, ret); + ETH_exec_socket_cmd(s, ETH_SCR_SEND); + + // Wait for the data to be sent + while ((ETH_readSnIR(s) & ETH_SIR_SEND_OK) != ETH_SIR_SEND_OK) + { + // If time out reset send and timout interrupts + if (ETH_readSnIR(s) & ETH_SIR_TIMEOUT) + { + ETH_writeSnIR(s, (ETH_SIR_SEND_OK | ETH_SIR_TIMEOUT)); + return 0; + } + } + + // Reset send ok interrupt + ETH_writeSnIR(s, ETH_SIR_SEND_OK); + } + return ret; +} + +uint16_t ETH_recvfrom(SOCKET s, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t *port) +{ + uint8_t head[8]; + uint16_t data_len=0; + uint16_t ptr=0; + + // Check if there is something to receive + if (len > 0) + { + // Get the receive pointer + ptr = ETH_readSnRX_RD(s); + // Handle receiving according to socket type + switch (ETH_readSnMR(s) & 0x07) + { + case ETH_SMR_UDP : + ETH_read_data(s, ptr, head, 0x08); + ptr += 8; + addr[0] = head[0]; + addr[1] = head[1]; + addr[2] = head[2]; + addr[3] = head[3]; + *port = head[4]; + *port = (*port << 8) + head[5]; + data_len = head[6]; + data_len = (data_len << 8) + head[7]; + + ETH_read_data(s, ptr, buf, data_len); + ptr += data_len; + + ETH_writeSnRX_RD(s, ptr); + break; + + case ETH_SMR_IPRAW : + ETH_read_data(s, ptr, head, 0x06); + ptr += 6; + + addr[0] = head[0]; + addr[1] = head[1]; + addr[2] = head[2]; + addr[3] = head[3]; + data_len = head[4]; + data_len = (data_len << 8) + head[5]; + + ETH_read_data(s, ptr, buf, data_len); + ptr += data_len; + + ETH_writeSnRX_RD(s, ptr); + break; + + case ETH_SMR_MACRAW: + ETH_read_data(s, ptr, head, 2); + ptr+=2; + data_len = head[0]; + data_len = (data_len<<8) + head[1] - 2; + + ETH_read_data(s, ptr, buf, data_len); + ptr += data_len; + ETH_writeSnRX_RD(s, ptr); + break; + + default : + break; + } + ETH_exec_socket_cmd(s, ETH_SCR_RECV); + } + return data_len; +} + +void ETH_flush(SOCKET s) +{ + // TODO +} + +uint16_t ETH_igmpsend(SOCKET s, const uint8_t * buf, uint16_t len) +{ + uint16_t ret=0; + + if (len > ETH_SSIZE) + ret = ETH_SSIZE; + else + ret = len; + + if (ret == 0) + return 0; + + // Process and send IGMP data + ETH_send_data_processing(s, (uint8_t *)buf, ret); + ETH_exec_socket_cmd(s, ETH_SCR_SEND); + + // Wairt for data to be sent or timeout + while ((ETH_readSnIR(s) & ETH_SIR_SEND_OK) != ETH_SIR_SEND_OK) + { + if (ETH_readSnIR(s) & ETH_SIR_TIMEOUT) + { + ETH_close(s); + return 0; + } + } + + // Reset interrupt + ETH_writeSnIR(s, ETH_SIR_SEND_OK); + return ret; +} + +int ETH_start_UDP(SOCKET s, uint8_t* addr, uint16_t port) +{ + // Check if address and port are valid + if (is_net_addr(addr) || !is_valid_port(port)) + { + return 0; + } + else + { + // Write IP and port + ETH_writeSnDIPR(s, addr); + ETH_writeSnDPORT(s, port); + return 1; + } +} + +uint16_t ETH_buffer_data(SOCKET s, uint16_t offset, const uint8_t* buf, uint16_t len) +{ + uint16_t ret = 0; + if (len > ETH_get_TX_free_size(s)) + { + ret = ETH_get_TX_free_size(s); + } + else + { + ret = len; + } + ETH_send_data_processing_offset(s, offset, buf, ret); + return ret; +} + +int ETH_send_UDP(SOCKET s) +{ + ETH_exec_socket_cmd(s, ETH_SCR_SEND); + + while ((ETH_readSnIR(s) & ETH_SIR_SEND_OK) != ETH_SIR_SEND_OK) + { + if (ETH_readSnIR(s) & ETH_SIR_TIMEOUT) + { + ETH_writeSnIR(s, (ETH_SIR_SEND_OK | ETH_SIR_TIMEOUT)); + return 0; + } + } + + ETH_writeSnIR(s, ETH_SIR_SEND_OK); + return 1; +} diff --git a/Mieke/SW/ETH/socket.h b/Mieke/SW/ETH/socket.h new file mode 100644 index 0000000..4ff6c4f --- /dev/null +++ b/Mieke/SW/ETH/socket.h @@ -0,0 +1,45 @@ +/* + Ethernet shield test program + Copyright (C) 2018 Andreas Mieke + Copyright (C) 2010 Arduino LLC + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __SOCKET_H +#define __SOCKET_H + +#include "w5100.h" + +uint8_t ETH_socket(SOCKET s, uint8_t protocol, uint16_t port, uint8_t flag); +uint8_t ETH_socket_status(SOCKET s); +void ETH_close(SOCKET s); +uint8_t ETH_connect(SOCKET s, uint8_t * addr, uint16_t port); +void ETH_disconnect(SOCKET s); +uint8_t ETH_listen(SOCKET s); +uint16_t ETH_send(SOCKET s, const uint8_t * buf, uint16_t len); +int16_t ETH_recv(SOCKET s, uint8_t * buf, int16_t len); +int16_t ETH_recv_available(SOCKET s); +uint16_t ETH_peek(SOCKET s, uint8_t *buf); +uint16_t ETH_sendto(SOCKET s, const uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t port); +uint16_t ETH_recvfrom(SOCKET s, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t *port); +void ETH_flush(SOCKET s); + +uint16_t ETH_igmpsend(SOCKET s, const uint8_t * buf, uint16_t len); + +int ETH_start_UDP(SOCKET s, uint8_t* addr, uint16_t port); +uint16_t ETH_buffer_data(SOCKET s, uint16_t offset, const uint8_t* buf, uint16_t len); +int ETH_send_UDP(SOCKET s); + +#endif diff --git a/Mieke/SW/ETH/w5100.c b/Mieke/SW/ETH/w5100.c new file mode 100644 index 0000000..253b221 --- /dev/null +++ b/Mieke/SW/ETH/w5100.c @@ -0,0 +1,256 @@ +/* + Ethernet shield test program + Copyright (C) 2018 Andreas Mieke + Copyright (C) 2010 Arduino LLC + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "w5100.h" + +uint16_t ETH_SBASE[ETH_SOCKETS]; // Tx buffer base address +uint16_t ETH_RBASE[ETH_SOCKETS]; // Rx buffer base address + +#define TX_RX_MAX_BUF_SIZE 2048 +#define TX_BUF 0x1100 +#define RX_BUF (TX_BUF + TX_RX_MAX_BUF_SIZE) + +#define ETH_TXBUF_BASE 0x4000 +#define ETH_RXBUF_BASE 0x6000 + +void SPI_SetSS(void) +{ + GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_RESET); +} + +void SPI_ResetSS(void) +{ + while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) != RESET); + GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET); +} + +uint8_t SPI_WriteRead(uint8_t write) +{ + while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); + SPI1->DR = write; + while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); + return SPI1->DR; +} + +void ETH_init(void) +{ + // Port init stuff + // PA5 -> SCK + // PA6 -> MISO + // PA7 -> MOSI + // PC8 -> /SS + + // Set RCC Registers for GPIOA/C + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); + RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); + + // Init gpio structure to default values + GPIO_InitTypeDef gpio; + GPIO_StructInit(&gpio); + + // Set PA5 to alternate function PushPull + gpio.GPIO_Mode = GPIO_Mode_AF_PP; + gpio.GPIO_Pin = GPIO_Pin_5; + GPIO_Init(GPIOA, &gpio); + + // Set PA7 to alternate function push pull + gpio.GPIO_Pin = GPIO_Pin_7; + GPIO_Init(GPIOA, &gpio); + + // Set PC8 to push pull + gpio.GPIO_Mode = GPIO_Mode_Out_PP; + gpio.GPIO_Pin = GPIO_Pin_8; + GPIO_Init(GPIOC, &gpio); + GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET); + + // Set PA6 to input floating + gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING; + gpio.GPIO_Pin = GPIO_Pin_6; + GPIO_Init(GPIOA, &gpio); + + // Init SPI engine + // Init struct to default values + SPI_InitTypeDef spi; + SPI_StructInit(&spi); + + // We are master + spi.SPI_Mode = SPI_Mode_Master; + spi.SPI_NSS = SPI_NSS_Soft; + //spi.SPI_CPHA = SPI_CPHA_2Edge; + spi.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; + + // Write the registers + SPI_Init(SPI1, &spi); + + // Enable SPI1 + SPI_Cmd(SPI1, ENABLE); + + for(uint16_t i = 0; i < 65535; i++); + + ETH_writeMR(ETH_MR_RST); + ETH_writeRMSR(0x55); + ETH_writeTMSR(0x55); + + for (int i=0; i> 8); + SPI_WriteRead(addr & 0xFF); + SPI_WriteRead(data); + SPI_ResetSS(); + return 8; +} + +uint16_t ETH_write_n(uint16_t addr, const uint8_t *buf, uint16_t len) +{ + for (uint16_t i = 0; i < len; i++) { + SPI_SetSS(); + SPI_WriteRead(0xF0); + SPI_WriteRead(addr >> 8); + SPI_WriteRead(addr & 0xFF); + addr++; + SPI_WriteRead(buf[i]); + SPI_ResetSS(); + } + return len; +} + +uint8_t ETH_read_8(uint16_t addr) +{ + uint8_t data; + SPI_SetSS(); + SPI_WriteRead(0x0F); + SPI_WriteRead(addr >> 8); + SPI_WriteRead(addr & 0xFF); + data = SPI_WriteRead(0x00); + SPI_ResetSS(); + return data; +} + +uint16_t ETH_read_n(uint16_t addr, uint8_t *buf, uint16_t len) +{ + for (uint16_t i = 0; i < len; i++) { + SPI_SetSS(); + SPI_WriteRead(0x0F); + SPI_WriteRead(addr >> 8); + SPI_WriteRead(addr & 0xFF); + addr++; + buf[i] = SPI_WriteRead(0x00); + SPI_ResetSS(); + } + return len; +} + +uint16_t ETH_get_TX_free_size(SOCKET s) +{ + uint16_t val=0, val1=0; + do { + val1 = ETH_readSnTX_FSR(s); + if (val1 != 0) + val = ETH_readSnTX_FSR(s); + } + while (val != val1); + return val; +} + +uint16_t ETH_get_RX_received_size(SOCKET s) +{ + uint16_t val=0,val1=0; + do { + val1 = ETH_readSnRX_RSR(s); + if (val1 != 0) + val = ETH_readSnRX_RSR(s); + } + while (val != val1); + return val; +} + +void ETH_read_data(SOCKET s, volatile uint16_t src, volatile uint8_t * dst, uint16_t len) +{ + uint16_t size; + uint16_t src_mask; + uint16_t src_ptr; + + src_mask = src & ETH_RMASK; + src_ptr = ETH_RBASE[s] + src_mask; + + if( (src_mask + len) > ETH_RSIZE ) + { + size = ETH_RSIZE - src_mask; + ETH_read_n(src_ptr, (uint8_t *)dst, size); + dst += size; + ETH_read_n(ETH_RBASE[s], (uint8_t *) dst, len - size); + } + else + ETH_read_n(src_ptr, (uint8_t *) dst, len); +} + +void ETH_send_data_processing(SOCKET s, const uint8_t *data, uint16_t len) +{ + ETH_send_data_processing_offset(s, 0, data, len); +} + +void ETH_send_data_processing_offset(SOCKET s, uint16_t data_offset, const uint8_t *data, uint16_t len) +{ + uint16_t ptr = ETH_readSnTX_WR(s); + ptr += data_offset; + uint16_t offset = ptr & ETH_SMASK; + uint16_t dstAddr = offset + ETH_SBASE[s]; + + if (offset + len > ETH_SSIZE) + { + uint16_t size = ETH_SSIZE - offset; + ETH_write_n(dstAddr, data, size); + ETH_write_n(ETH_SBASE[s], data + size, len - size); + } + else { + ETH_write_n(dstAddr, data, len); + } + + ptr += len; + ETH_writeSnTX_WR(s, ptr); +} + +void ETH_recv_data_processing(SOCKET s, uint8_t *data, uint16_t len, uint8_t peek) +{ + uint16_t ptr; + ptr = ETH_readSnRX_RD(s); + ETH_read_data(s, ptr, data, len); + if (!peek) + { + ptr += len; + ETH_writeSnRX_RD(s, ptr); + } +} + +void ETH_exec_socket_cmd(SOCKET s, uint8_t cmd) +{ + ETH_writeSnCR(s, cmd); + while(ETH_readSnCR(s)) { + // Wait for command to be executed + } +} diff --git a/Mieke/SW/ETH/w5100.h b/Mieke/SW/ETH/w5100.h new file mode 100644 index 0000000..7e08347 --- /dev/null +++ b/Mieke/SW/ETH/w5100.h @@ -0,0 +1,302 @@ +/* + Ethernet shield test program + Copyright (C) 2018 Andreas Mieke + Copyright (C) 2010 Arduino LLC + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __W5100_H +#define __W5100_H + +#include "stm32f10x.h" // Device header +#include "stm32f10x_rcc.h" // Keil::Device:StdPeriph Drivers:RCC +#include "stm32f10x_gpio.h" // Keil::Device:StdPeriph Drivers:GPIO +#include "stm32f10x_spi.h" // Keil::Device:StdPeriph Drivers:SPI + +#define MAX_SOCK_NUM 4 +typedef uint8_t SOCKET; + +enum { + ETH_MR_RST = 0x80, + ETH_MR_PB = 0x10, + ETH_MR_PPPoE = 0x08, + ETH_MR_AI = 0x02, + ETH_MR_IND = 0x01 +}; + +enum { + ETH_IR_CONFLICT = 0x80, + ETH_IR_UNREACH = 0x40, + ETH_IR_PPPoE = 0x20, + ETH_IR_S3_INT = 0x08, + ETH_IR_S2_INT = 0x04, + ETH_IR_S1_INT = 0x02, + ETH_IR_S0_INT = 0x01 +}; + +enum { + ETH_IMR_IR7 = 0x80, + ETH_IMR_IR6 = 0x40, + ETH_IMR_IR5 = 0x20, + ETH_IMR_IR3 = 0x08, + ETH_IMR_IR2 = 0x04, + ETH_IMR_IR1 = 0x02, + ETH_IMR_IR0 = 0x01 +}; + +enum { + ETH_SMR_MULTI = 0x80, + ETH_SMR_ND = 0x20, + ETH_SMR_CLOSED = 0x00, + ETH_SMR_TCP = 0x01, + ETH_SMR_UDP = 0x02, + ETH_SMR_IPRAW = 0x03, + ETH_SMR_MACRAW = 0x04, + ETH_SMR_PPPoE = 0x05 +}; + +enum { + ETH_SCR_OPEN = 0x01, + ETH_SCR_LISTEN = 0x02, + ETH_SCR_CONNECT = 0x04, + ETH_SCR_DISCON = 0x08, + ETH_SCR_CLOSE = 0x10, + ETH_SCR_SEND = 0x20, + ETH_SCR_SEND_MAC = 0x21, + ETH_SCR_SEND_KEEP = 0x22, + ETH_SCR_RECV = 0x40 +}; + +enum { + ETH_SIR_SEND_OK = 0x10, + ETH_SIR_TIMEOUT = 0x08, + ETH_SIR_RECV = 0x04, + ETH_SIR_DISCON = 0x02, + ETH_SIR_CON = 0x01 +}; + +enum { + ETH_SSR_CLOSED = 0x00, + ETH_SSR_INIT = 0x12, // Actually 13, Cortex doesn't seem to register the LSB + ETH_SSR_LISTEN = 0x14, + ETH_SSR_SYNSENT = 0x15, + ETH_SSR_SYNRECV = 0x16, + ETH_SSR_ESTABLISHED = 0x17, + ETH_SSR_FIN_WAIT = 0x18, + ETH_SSR_CLOSING = 0x1A, + ETH_SSR_TIME_WAIT = 0x1B, + ETH_SSR_CLOSE_WAIT = 0x1C, + ETH_SSR_LAST_ACK = 0x1D, + ETH_SSR_UDP = 0x22, + ETH_SSR_IPRAW = 0x32, + ETH_SSR_MACRAW = 0x42, + ETH_SSR_PPPoE = 0x5F +}; + +enum { + ETH_SPROTO_IP = 0, + ETH_SPROTO_ICMP = 1, + ETH_SPROTO_IGMP = 2, + ETH_SPROTO_GGP = 3, + ETH_SPROTO_TCP = 6, + ETH_SPROTO_PUP = 12, + ETH_SPROTO_UDP = 17, + ETH_SPROTO_IDP = 22, + ETH_SPROTO_ND = 77, + ETH_SPROTO_RAW = 255 +}; + +void ETH_init(void); +void ETH_read_data(SOCKET s, volatile uint16_t src, volatile uint8_t * dst, uint16_t len); +void ETH_send_data_processing(SOCKET s, const uint8_t *data, uint16_t len); +void ETH_send_data_processing_offset(SOCKET s, uint16_t data_offset, const uint8_t *data, uint16_t len); +void ETH_recv_data_processing(SOCKET s, uint8_t *data, uint16_t len, uint8_t peek); + +inline void ETH_set_gateway_IP(uint8_t *addr); +inline void ETH_get_gateway_IP(uint8_t *addr); + +inline void ETH_set_subnet_mask(uint8_t *addr); +inline void ETH_get_subnet_mask(uint8_t *addr); + +inline void ETH_set_mac(uint8_t * addr); +inline void ETH_get_mac(uint8_t * addr); + +inline void ETH_set_IP(uint8_t * addr); +inline void ETH_get_IP(uint8_t * addr); + +inline void ETH_set_retransmission_time(uint16_t timeout); +inline void ETH_set_retransmission_count(uint8_t retry); + +inline void ETH_exec_socket_cmd(SOCKET s, uint8_t cmd); + +uint16_t ETH_get_TX_free_size(SOCKET s); +uint16_t ETH_get_RX_received_size(SOCKET s); + +uint8_t ETH_write_8(uint16_t addr, uint8_t data); +uint16_t ETH_write_n(uint16_t addr, const uint8_t *buf, uint16_t len); + +uint8_t ETH_read_8(uint16_t addr); +uint16_t ETH_read_n(uint16_t addr, uint8_t *buf, uint16_t len); + +#define __GP_REGISTER8(name, address) \ + static inline void ETH_write##name(uint8_t _data) { \ + ETH_write_8(address, _data); \ + } \ + static inline uint8_t ETH_read##name() { \ + return ETH_read_8(address); \ + } +#define __GP_REGISTER16(name, address) \ + static void ETH_write##name(uint16_t _data) { \ + ETH_write_8(address, _data >> 8); \ + ETH_write_8(address+1, _data & 0xFF); \ + } \ + static uint16_t ETH_read##name() { \ + uint16_t res = ETH_read_8(address); \ + res = (res << 8) + ETH_read_8(address + 1); \ + return res; \ + } +#define __GP_REGISTER_N(name, address, size) \ + static uint16_t ETH_write##name(uint8_t *_buff) { \ + return ETH_write_n(address, _buff, size); \ + } \ + static uint16_t ETH_read##name(uint8_t *_buff) { \ + return ETH_read_n(address, _buff, size); \ + } + +__GP_REGISTER8 (MR, 0x0000); // Mode +__GP_REGISTER_N(GAR, 0x0001, 4); // Gateway IP address +__GP_REGISTER_N(SUBR, 0x0005, 4); // Subnet mask address +__GP_REGISTER_N(SHAR, 0x0009, 6); // Source MAC address +__GP_REGISTER_N(SIPR, 0x000F, 4); // Source IP address +__GP_REGISTER8 (IR, 0x0015); // Interrupt +__GP_REGISTER8 (IMR, 0x0016); // Interrupt Mask +__GP_REGISTER16(RTR, 0x0017); // Timeout address +__GP_REGISTER8 (RCR, 0x0019); // Retry count +__GP_REGISTER8 (RMSR, 0x001A); // Receive memory size +__GP_REGISTER8 (TMSR, 0x001B); // Transmit memory size +__GP_REGISTER8 (PATR, 0x001C); // Authentication type address in PPPoE mode +__GP_REGISTER8 (PTIMER, 0x0028); // PPP LCP Request Timer +__GP_REGISTER8 (PMAGIC, 0x0029); // PPP LCP Magic Number +__GP_REGISTER_N(UIPR, 0x002A, 4); // Unreachable IP address in UDP mode +__GP_REGISTER16(UPORT, 0x002E); // Unreachable Port address in UDP mode + +#undef __GP_REGISTER8 +#undef __GP_REGISTER16 +#undef __GP_REGISTER_N + +static inline uint8_t ETH_sock_read_8(SOCKET _s, uint16_t _addr); +static inline uint8_t ETH_sock_write_8(SOCKET _s, uint16_t _addr, uint8_t _data); +static inline uint16_t ETH_sock_read_n(SOCKET _s, uint16_t _addr, uint8_t *_buf, uint16_t len); +static inline uint16_t ETH_sock_write_n(SOCKET _s, uint16_t _addr, uint8_t *_buf, uint16_t len); + +static const uint16_t ETH_CH_BASE = 0x0400; +static const uint16_t ETH_CH_SIZE = 0x0100; + +#define __SOCKET_REGISTER8(name, address) \ + static inline void ETH_write##name(SOCKET _s, uint8_t _data) { \ + ETH_sock_write_8(_s, address, _data); \ + } \ + static inline uint8_t ETH_read##name(SOCKET _s) { \ + return ETH_sock_read_8(_s, address); \ + } +#define __SOCKET_REGISTER16(name, address) \ + static void ETH_write##name(SOCKET _s, uint16_t _data) { \ + ETH_sock_write_8(_s, address, _data >> 8); \ + ETH_sock_write_8(_s, address+1, _data & 0xFF); \ + } \ + static uint16_t ETH_read##name(SOCKET _s) { \ + uint16_t res = ETH_sock_read_8(_s, address); \ + uint16_t res2 = ETH_sock_read_8(_s,address + 1); \ + res = res << 8; \ + res2 = res2 & 0xFF; \ + res = res | res2; \ + return res; \ + } +#define __SOCKET_REGISTER_N(name, address, size) \ + static uint16_t ETH_write##name(SOCKET _s, uint8_t *_buff) { \ + return ETH_sock_write_n(_s, address, _buff, size); \ + } \ + static uint16_t ETH_read##name(SOCKET _s, uint8_t *_buff) { \ + return ETH_sock_read_n(_s, address, _buff, size); \ + } + +__SOCKET_REGISTER8(SnMR, 0x0000) // Mode +__SOCKET_REGISTER8(SnCR, 0x0001) // Command +__SOCKET_REGISTER8(SnIR, 0x0002) // Interrupt +__SOCKET_REGISTER8(SnSR, 0x0003) // Status +__SOCKET_REGISTER16(SnPORT, 0x0004) // Source Port +__SOCKET_REGISTER_N(SnDHAR, 0x0006, 6) // Destination Hardw Addr +__SOCKET_REGISTER_N(SnDIPR, 0x000C, 4) // Destination IP Addr +__SOCKET_REGISTER16(SnDPORT, 0x0010) // Destination Port +__SOCKET_REGISTER16(SnMSSR, 0x0012) // Max Segment Size +__SOCKET_REGISTER8(SnPROTO, 0x0014) // Protocol in IP RAW Mode +__SOCKET_REGISTER8(SnTOS, 0x0015) // IP TOS +__SOCKET_REGISTER8(SnTTL, 0x0016) // IP TTL +__SOCKET_REGISTER16(SnTX_FSR, 0x0020) // TX Free Size +__SOCKET_REGISTER16(SnTX_RD, 0x0022) // TX Read Pointer +__SOCKET_REGISTER16(SnTX_WR, 0x0024) // TX Write Pointer +__SOCKET_REGISTER16(SnRX_RSR, 0x0026) // RX Free Size +__SOCKET_REGISTER16(SnRX_RD, 0x0028) // RX Read Pointer +__SOCKET_REGISTER16(SnRX_WR, 0x002A) // RX Write Pointer (supported?) + +#undef __SOCKET_REGISTER8 +#undef __SOCKET_REGISTER16 +#undef __SOCKET_REGISTER_N + +static const int ETH_SOCKETS = 4; +static const uint16_t ETH_SMASK = 0x07FF; // Tx buffer MASK +static const uint16_t ETH_RMASK = 0x07FF; // Rx buffer MASK +static const uint16_t ETH_SSIZE = 2048; // Max Tx buffer size +static const uint16_t ETH_RSIZE = 2048; // Max Rx buffer size + +extern uint16_t ETH_SBASE[ETH_SOCKETS]; // Tx buffer base address +extern uint16_t ETH_RBASE[ETH_SOCKETS]; // Rx buffer base address + +uint8_t ETH_sock_read_8(SOCKET s, uint16_t addr) +{ + return ETH_read_8(ETH_CH_BASE + s * ETH_CH_SIZE + addr); +} + +uint8_t ETH_sock_write_8(SOCKET s, uint16_t addr, uint8_t data) +{ + return ETH_write_8(ETH_CH_BASE + s * ETH_CH_SIZE + addr, data); +} + +uint16_t ETH_sock_read_n(SOCKET s, uint16_t addr, uint8_t *buf, uint16_t len) +{ + return ETH_read_n(ETH_CH_BASE + s * ETH_CH_SIZE + addr, buf, len); +} + +uint16_t ETH_sock_write_n(SOCKET s, uint16_t addr, uint8_t *buf, uint16_t len) +{ + return ETH_write_n(ETH_CH_BASE + s * ETH_CH_SIZE + addr, buf, len); +} + +void ETH_set_gateway_IP(uint8_t *addr) { ETH_writeGAR(addr); } +void ETH_get_gateway_IP(uint8_t *addr) { ETH_readGAR(addr); } + +void ETH_set_subnet_mask(uint8_t *addr) { ETH_writeSUBR(addr); } +void ETH_get_subnet_mask(uint8_t *addr) { ETH_readSUBR(addr); } + +void ETH_set_mac(uint8_t * addr) { ETH_writeSHAR(addr); } +void ETH_get_mac(uint8_t * addr) { ETH_readSHAR(addr); } + +void ETH_set_IP(uint8_t * addr) { ETH_writeSIPR(addr); } +void ETH_get_IP(uint8_t * addr) { ETH_readSIPR(addr); } + +void ETH_set_retransmission_time(uint16_t timeout) { ETH_writeRTR(timeout); } +void ETH_set_retransmission_count(uint8_t retry) { ETH_writeRCR(retry); } + +#endif diff --git a/Mieke/Software.tex b/Mieke/Software.tex index 70c59e0..184dc22 100644 --- a/Mieke/Software.tex +++ b/Mieke/Software.tex @@ -83,4 +83,29 @@ Der Piezo Test gibt einen ton auf eben diesem aus. \subsubsection{display.c} Um das Display zu testen, wird mehrmals der Screen gelöscht und in einer anderen Farbe gezeichnet. Dieser Befehl sollte auch ohne extra Test-Programm, unabhänging von dem zur Zeit geflashten Programm, auf dem Display funktionieren. -\lstinputlisting[language={[ANSI]C}, caption=Tests: Display Implementation, label=lst:sw-mt-displayc]{Mieke/SW/MT/display.c} \ No newline at end of file +\lstinputlisting[language={[ANSI]C}, caption=Tests: Display Implementation, label=lst:sw-mt-displayc]{Mieke/SW/MT/display.c} + +\subsection{Ethernet} +Ein Ziel dieser Diplomarbeit war es eine Library für das Arduino Ethernet Shield (Version 1.0) zu entwickeln, dies ist bis dato nicht 100\%ig gelungen, da die SPI Peripherie des \gls{cpu} Probleme bereitet und in manchen Situationen das \gls{LSb} ignoriert. Zum Beispiel, wenn der Ethernet Controller den Status \texttt{0x13} sendet, beinhaltet das Datenregister nur \texttt{0x12}, das \gls{LSb} ist also immer \texttt{0}. Das derzeitige Hauptaugenmerk liegt beim \gls{Debugging} dieses Fehlers. Eine mögliche Lösung wäre das SPI Interface selbst zu implementieren, und nicht die vorhandene Peripherieeinheit zu verwenden. Der nun folgende Programmcode ist deshalb keine Library, sondern ein eigenständiges Test-Programm, welches noch nicht 100\% funktioniert. + +\subsubsection{main.c} +Das Hauptpgrogramm initsialisiert das Ethernet Modul und setzt die entsprechenden IP- und MAC-Adressen, danach wird ein TCP Server auf Port 80 gestartet, und übermittelte Pakete über UART ausgegeben. +\lstinputlisting[language={[ANSI]C}, caption=Ethernet: Hauptpgrogramm, label=lst:sw-eth-mainc]{Mieke/SW/ETH/main.c} + +\subsubsection{socket.c} +In \texttt{socket.c} wird eine Socket API, welche der POSIX Socket API ähnlich ist, zur Verfügung gestellt. Das Headerfile \texttt{socket.h} enthält hierbei nur die Funktionsdeklarationen. Anders als bei der POSIX Socket API, muss hier jeder Socket manuell eine ID \textbf{vor} dem Erstellen erhalten. Diese ID muss zwischen 1 und 4 liegen, und repräsentiert die 4 Sockets (Register und Speicher), welche am Ethernet-Controller vorhanden sind. + +Der Code wurde größtenteils von der entsprechenden Arduino Library \cite{arduino:ethernet} übernommen und auf C adaptiert. +\lstinputlisting[language={[ANSI]C}, caption=Ethernet: Socket API, label=lst:sw-eth-socketc]{Mieke/SW/ETH/socket.c} + +\subsubsection{w5100.h} +Der Ethernet Controller selbst ist ein W5100 \cite{arduino:ethernetchip}. Dieses Headerfile enthält sämtliche Funktionsdeklarationen für diesen Controller, aber auch structs und enums welche die verschiedenen Register und Memory Bereiche spezifizieren. + +Der Code wurde größtenteils von der entsprechenden Arduino Library \cite{arduino:ethernet} übernommen und auf C adaptiert. +\lstinputlisting[language={[ANSI]C}, caption=Ethernet: W5100 Header, label=lst:sw-eth-w5100h]{Mieke/SW/ETH/w5100.h} + +\subsubsection{w5100.c} +Der Ethernet Controller selbst ist ein W5100 \cite{arduino:ethernetchip}. Diese Implementation enthält sämtliche Funktionen für diesen Controller. + +Der Code wurde größtenteils von der entsprechenden Arduino Library \cite{arduino:ethernet} übernommen und auf C adaptiert. +\lstinputlisting[language={[ANSI]C}, caption=Ethernet: W5100 Implementation, label=lst:sw-eth-w5100c]{Mieke/SW/ETH/w5100.c} \ No newline at end of file diff --git a/literatur.bib b/literatur.bib index e098d78..bc0c77f 100644 --- a/literatur.bib +++ b/literatur.bib @@ -349,7 +349,7 @@ } @manual{ basis:rgbdriver, - organization = " Semiconductor Components Industries, LLC", + organization = "Semiconductor Components Industries, LLC", title = "Triple Output I2C Controlled RGB LED Driver", number = "NCP5623", year = "2008", @@ -357,4 +357,26 @@ note = "Rev. 6", url = "https://www.onsemi.com/pub/Collateral/NCP5623-D.PDF", urldate = "2018-03-18" +} + +@misc{ arduino:ethernet, + author = {Tinitigan, Dino}, + title = {Arduino Ethernet Library}, + year = {2016}, + publisher = {GitHub}, + journal = {GitHub repository}, + url = {https://github.com/arduino/Arduino/tree/master/libraries/Ethernet/src/utility}, + commit = {0873b36aa53590e2ace81a3b0535ead7c668bcbb}, + urldate = "2018-04-03" +} + +@manual{ arduino:ethernetchip, + organization = "{WIZnet Co., Inc.}", + title = "W5100 Datasheet", + number = "W5100", + year = "2008", + month = "1", + note = "Version 1.1.6", + url = "https://www.sparkfun.com/datasheets/DevTools/Arduino/W5100_Datasheet_v1_1_6.pdf", + urldate = "2018-04-03" } \ No newline at end of file