Compare commits

...

13 Commits

Author SHA1 Message Date
64bb06f292 fix: idk 2025-12-12 23:05:27 +06:00
537e7bfe10 rewrite ethernetif to use ch32v208_eth.hg 2025-11-13 02:16:21 +06:00
fe6cc2ebb9 add mqtt, decouple i2c and sensor 2025-11-10 22:40:21 +06:00
80478c7400 gxht30 2025-11-10 12:34:10 +06:00
e448282aed fix: correctly deinit rcc before hse sw 2025-11-10 12:00:21 +06:00
6e29b34542 120mhz clock 2025-11-10 02:55:12 +06:00
7877bb42f4 clean up header file 2025-11-09 21:08:08 +06:00
fd3d66e424 tweak lwipopts.h 2025-11-09 20:27:31 +06:00
a0ca17e68f 48k -> 20k RAM 2025-11-09 13:00:16 +06:00
5445c27271 fix: add tx queue 2025-11-09 12:15:13 +06:00
06012c613a proper RX DMA 2025-11-08 21:14:56 +06:00
e814012f09 get RXIF to work 2025-11-08 21:01:04 +06:00
fcd60d1eb8 readme 2025-11-08 20:39:52 +06:00
16 changed files with 920 additions and 344 deletions

71
.vscode/settings.json vendored
View File

@@ -38,6 +38,73 @@
"vector": "c",
"memory_resource": "c",
"__config": "c",
"string": "c"
}
"string": "c",
"atomic": "c",
"__bit_reference": "c",
"err.h": "c",
"httpd.h": "c",
"random": "c",
"limits": "c",
"tuple": "c",
"init.h": "c",
"hw_i2c.h": "c",
"chrono": "c",
"stop_token": "c",
"__locale": "c",
"stdint.h": "c",
"ch32v208_eth.h": "c",
"bit": "c",
"any": "c",
"array": "c",
"hash_map": "c",
"strstream": "c",
"charconv": "c",
"cmath": "c",
"codecvt": "c",
"complex": "c",
"concepts": "c",
"condition_variable": "c",
"coroutine": "c",
"cstddef": "c",
"unordered_map": "c",
"unordered_set": "c",
"exception": "c",
"memory": "c",
"numeric": "c",
"optional": "c",
"ratio": "c",
"string_view": "c",
"system_error": "c",
"type_traits": "c",
"algorithm": "c",
"iomanip": "c",
"mutex": "c",
"ostream": "c",
"semaphore": "c",
"shared_mutex": "c",
"span": "c",
"stacktrace": "c",
"text_encoding": "c",
"thread": "c",
"typeindex": "c",
"typeinfo": "c",
"utility": "c",
"valarray": "c",
"__assert": "c",
"__split_buffer": "c",
"ios": "c",
"map": "c",
"new": "c",
"queue": "c",
"set": "c",
"stack": "c",
"stdexcept": "c",
"__node_handle": "c",
"execution": "c",
"numbers": "c",
"print": "c",
"ha_mqtt.h": "c",
"ethernetif.h": "c"
},
"cmake.sourceDirectory": "/home/mira/src/embedded/ch32v208_sens/lwip"
}

View File

@@ -21,11 +21,12 @@ LWIP_C_FILES += $(COREFILES)
LWIP_C_FILES += $(CORE4FILES)
LWIP_C_FILES += $(NETIFFILES)
LWIP_C_FILES += $(HTTPFILES)
LWIP_C_FILES += $(MQTTFILES)
LWIP_C_FILES_WITH_PATH := $(LWIP_C_FILES)
LWIP_PORT_FILES := $(wildcard $(PORT_DIR)/*.c)
LWIP_PORT_FILES := $(wildcard $(PORT_DIR)/*.c $(PORT_DIR)/arch/*.c)
INCLUDE_DIRS ?= \
INCLUDE_DIRS += \
-I./inc \
-I$(LWIP_DIR)/src/include \
-I$(PORT_DIR)

36
README.md Normal file
View File

@@ -0,0 +1,36 @@
# lwIP Ethernet Driver for CH32V208
This is a simple ethernetif.c driver to get lwIP working on the WCH CH32V208 MCU using the ch32fun lib.
It uses the chip's internal 10Mbps Ethernet MAC. The MAC address is pulled from the chip's 6-byte unique ID.
The provided main.c is an example that starts up, gets an IP address via DHCP, and runs a small HTTP server.
## Usage
1. Initialize lwIP and add the network interface.
2. Poll the driver and service lwIP's timers in your main loop.
```c
netif_add(&g_netif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &ethernet_input);
netif_set_default(&g_netif);
netif_set_up(&g_netif);
dhcp_start(&g_netif);
while (1) {
// poll for incoming packets
ethernetif_input(&g_netif);
// handle lwIP timers (for TCP, DHCP, etc.)
sys_check_timeouts();
// poll link for link up/down cb
ethernetif_link_poll(&g_netif);
}
```
## Impl note
This driver is kinda functional but not optimized
- **Packet RX:** ~~This is done by polling~~ RXIF works now. You must call `ethernetif_input()` continuously in your main loop to check for and process incoming packets
- **Packet TX:** TX isn't exactly typical DMA? The CPU has to copy the packet into a single transmit buffer and then manually start the transmission. An ISR will signal when the buffer is free to send the next packet

Submodule ch32fun updated: 08885a5ea4...b0764004f5

View File

@@ -1,9 +1,9 @@
#ifndef _FUNCONFIG_H
#define _FUNCONFIG_H
#define FUNCONF_USE_HSE 1
// #define FUNCONF_USE_HSE 1
#define FUNCONF_SYSTEM_CORE_CLOCK 120000000
#define FUNCONF_PLL_MULTIPLIER 15
// #define FUNCONF_PLL_MULTIPLIER 15
#define FUNCONF_SYSTICK_USE_HCLK 1
#endif

199
ha_mqtt.c Normal file
View File

@@ -0,0 +1,199 @@
// ha_mqtt.c
#include "ha_mqtt.h"
#include <stdio.h>
#include <string.h>
#include "lwip/apps/mqtt.h"
#include "lwip/ip4_addr.h"
#include "lwip/netif.h"
#include "systick.h"
// config
#define MQTT_BROKER_IP_A 192
#define MQTT_BROKER_IP_B 168
#define MQTT_BROKER_IP_C 102
#define MQTT_BROKER_IP_D 1
#define MQTT_BROKER_PORT 1883
#define MQTT_CLIENT_ID "gxht30_sensor_node"
#define MQTT_RECONNECT_INTERVAL_MS 5000
#define DEVICE_UNIQUE_ID "gxht30_board_01"
#define DEVICE_NAME "Office Climate Sensor"
#define DEVICE_MANUFACTURER "me"
#define DEVICE_MODEL "GXHT30-ETH-v1"
// home assistant topics
#define HA_DISCOVERY_PREFIX "homeassistant"
#define TEMP_UNIQUE_ID DEVICE_UNIQUE_ID "_temperature"
#define TEMP_CONFIG_TOPIC \
HA_DISCOVERY_PREFIX "/sensor/" TEMP_UNIQUE_ID "/config"
#define TEMP_STATE_TOPIC "devices/" DEVICE_UNIQUE_ID "/temperature/state"
#define TEMP_NAME "Temperature"
#define HUM_UNIQUE_ID DEVICE_UNIQUE_ID "_humidity"
#define HUM_CONFIG_TOPIC HA_DISCOVERY_PREFIX "/sensor/" HUM_UNIQUE_ID "/config"
#define HUM_STATE_TOPIC "devices/" DEVICE_UNIQUE_ID "/humidity/state"
#define HUM_NAME "Humidity"
#define MQTT_RECONNECT_INTERVAL_MS 5000
static mqtt_client_t* s_mqtt_client;
static ip_addr_t s_mqtt_broker_ip;
static uint8_t s_mqtt_connected = 0;
static uint32_t s_last_mqtt_retry_time = 0;
extern struct netif g_netif;
static void publish_ha_discovery_configs(mqtt_client_t* client);
static void mqtt_pub_request_cb(void* arg, err_t err);
static void mqtt_connection_cb(mqtt_client_t* client, void* arg,
mqtt_connection_status_t status);
static void publish_sensor_value(const char* topic, int32_t value_x100);
// public fns
void ha_mqtt_init(void) {
s_mqtt_client = mqtt_client_new();
if (s_mqtt_client == NULL) {
printf("error: failed to create mqtt client\n");
return;
}
IP4_ADDR(&s_mqtt_broker_ip, MQTT_BROKER_IP_A, MQTT_BROKER_IP_B,
MQTT_BROKER_IP_C, MQTT_BROKER_IP_D);
ha_mqtt_check_and_reconnect();
}
void ha_mqtt_publish_sensor_data(int32_t temp_x100, int32_t hum_x100) {
if (!s_mqtt_connected) {
return;
}
publish_sensor_value(TEMP_STATE_TOPIC, temp_x100);
publish_sensor_value(HUM_STATE_TOPIC, hum_x100);
}
void ha_mqtt_check_and_reconnect(void) {
if (s_mqtt_connected || !netif_is_up(&g_netif) ||
!netif_is_link_up(&g_netif) || netif_ip4_addr(&g_netif)->addr == 0) {
return;
}
if (millis() - s_last_mqtt_retry_time > MQTT_RECONNECT_INTERVAL_MS) {
printf("attempting to connect to mqtt broker\n");
const struct mqtt_connect_client_info_t client_info = {
.client_id = MQTT_CLIENT_ID,
.keep_alive = 60,
// .client_user = "user", // authentication
// .client_pass = "pass",
};
err_t err =
mqtt_client_connect(s_mqtt_client, &s_mqtt_broker_ip, MQTT_BROKER_PORT,
mqtt_connection_cb, NULL, &client_info);
if (err != ERR_OK) {
printf("mqtt connect failed with immediate error: %d\n", err);
}
s_last_mqtt_retry_time = millis();
}
}
uint8_t ha_mqtt_is_connected(void) { return s_mqtt_connected; }
// static fns
/**
* @brief Publishes a sensor value after formatting it from a fixed-point
* int
* @param topic The MQTT topic to publish to.
* @param value_x100 The sensor value, scaled by 100.
*/
static void publish_sensor_value(const char* topic, int32_t value_x100) {
char payload_buf[16];
// format the fixed-point value (e.g., 2345) into a decimal string ("23.45")
int32_t val_int = value_x100 / 100;
int32_t val_frac = value_x100 % 100;
if (val_frac < 0) {
val_frac = -val_frac;
}
snprintf(payload_buf, sizeof(payload_buf), "%ld.%02ld", val_int, val_frac);
err_t err = mqtt_publish(s_mqtt_client, topic, payload_buf,
strlen(payload_buf), 1, 0, NULL, NULL);
if (err != ERR_OK) {
printf("failed to publish to topic %s: %d\n", topic, err);
}
}
static void publish_ha_discovery_configs(mqtt_client_t* client) {
char payload[512];
err_t err;
// publish temperature sensor config
snprintf(
payload, sizeof(payload),
"{"
"\"name\":\"%s\",\"unique_id\":\"%s\",\"stat_t\":\"%s\","
"\"dev_cla\":\"temperature\",\"unit_of_meas\":\"°C\","
"\"val_tpl\":\"{{ value|float(2) }}\","
"\"dev\":{\"ids\":[\"%s\"],\"name\":\"%s\",\"mf\":\"%s\",\"mdl\":\"%s\"}"
"}",
TEMP_NAME, TEMP_UNIQUE_ID, TEMP_STATE_TOPIC, DEVICE_UNIQUE_ID,
DEVICE_NAME, DEVICE_MANUFACTURER, DEVICE_MODEL);
printf("publishing ha discovery for temperature...\n");
err = mqtt_publish(client, TEMP_CONFIG_TOPIC, payload, strlen(payload), 1, 1,
mqtt_pub_request_cb, NULL);
if (err != ERR_OK) {
printf("failed to publish temp config: %d\n", err);
}
// publish humidity sensor config
snprintf(
payload, sizeof(payload),
"{"
"\"name\":\"%s\",\"unique_id\":\"%s\",\"stat_t\":\"%s\","
"\"dev_cla\":\"humidity\",\"unit_of_meas\":\"%%\","
"\"val_tpl\":\"{{ value|float(2) }}\","
"\"dev\":{\"ids\":[\"%s\"],\"name\":\"%s\",\"mf\":\"%s\",\"mdl\":\"%s\"}"
"}",
HUM_NAME, HUM_UNIQUE_ID, HUM_STATE_TOPIC, DEVICE_UNIQUE_ID, DEVICE_NAME,
DEVICE_MANUFACTURER, DEVICE_MODEL);
printf("publishing ha discovery for humidity...\n");
err = mqtt_publish(client, HUM_CONFIG_TOPIC, payload, strlen(payload), 1, 1,
mqtt_pub_request_cb, NULL);
if (err != ERR_OK) {
printf("failed to publish hum config: %d\n", err);
}
}
static void mqtt_pub_request_cb(void* arg, err_t err) {
(void)arg;
if (err != ERR_OK) {
printf("mqtt publish failed with error: %d\n", err);
}
}
static void mqtt_connection_cb(mqtt_client_t* client, void* arg,
mqtt_connection_status_t status) {
(void)arg;
if (status == MQTT_CONNECT_ACCEPTED) {
printf("mqtt connection successful\n");
s_mqtt_connected = 1;
// on connect, publish the discovery configuration messages
publish_ha_discovery_configs(client);
} else {
printf("mqtt connection failed, status: %d. will retry.\n", status);
s_mqtt_connected = 0;
}
}

159
inc/gxht30_hw_i2c.h Normal file
View File

@@ -0,0 +1,159 @@
#ifndef _GXHT30_HW_I2C_H
#define _GXHT30_HW_I2C_H
#include <stdbool.h>
#include <stdint.h>
#include "hw_i2c.h"
// GXHT30 I2C addresses
#define GXHT30_I2C_ADDR_DEFAULT 0x44
#define GXHT30_I2C_ADDR_ALT 0x45
// GXHT30 cmds
#define GXHT30_CMD_MEAS_MSB 0x2C
#define GXHT30_CMD_MEAS_LSB 0x06
#define GXHT30_CMD_SOFT_RESET_MSB 0x30
#define GXHT30_CMD_SOFT_RESET_LSB 0xA2
#define GXHT30_CMD_HEATER_ON_MSB 0x30
#define GXHT30_CMD_HEATER_ON_LSB 0x6D
#define GXHT30_CMD_HEATER_OFF_MSB 0x30
#define GXHT30_CMD_HEATER_OFF_LSB 0x66
#define GXHT30_CMD_STATUS_MSB 0xF3
#define GXHT30_CMD_STATUS_LSB 0x2D
#define GXHT30_CMD_CLEAR_STATUS_MSB 0x30
#define GXHT30_CMD_CLEAR_STATUS_LSB 0x41
// conversion constants
#define GXHT30_TEMP_MULTIPLIER 2670
#define GXHT30_TEMP_OFFSET 45000000L
#define GXHT30_HUM_MULTIPLIER 1526
#define GXHT30_SCALE_DIVISOR 10000
typedef struct {
int32_t temperature_x100; // Temperature in hundredths of a degree C
int32_t humidity_x100; // Humidity in hundredths of a percent RH
uint8_t error; // Last error code
} GXHT30_Data;
typedef struct {
uint8_t alert_pending; // Bit 15: Alert status
uint8_t heater_on; // Bit 13: Heater status
uint8_t humidity_alert; // Bit 11: Humidity tracking alert
uint8_t temperature_alert; // Bit 10: Temperature tracking alert
uint8_t reset_detected; // Bit 4: System reset detected
uint8_t command_status; // Bit 1: Last command execution status
uint8_t crc_status; // Bit 0: Write data CRC checksum status
uint16_t raw_status; // Raw 16-bit status value
} GXHT30_Status;
enum GXHT30_Error {
GXHT30_OK = 0,
GXHT30_ERR_TIMEOUT,
GXHT30_ERR_CRC,
GXHT30_ERR_I2C
};
static inline bool gxht30_crc8_check(uint8_t msb, uint8_t lsb, uint8_t crc) {
uint8_t calc_crc = 0xFF;
uint8_t data[2] = {msb, lsb};
for (uint8_t byte = 0; byte < 2; byte++) {
calc_crc ^= data[byte];
for (uint8_t i = 0; i < 8; i++) {
calc_crc = (calc_crc & 0x80) ? (calc_crc << 1) ^ 0x31 : (calc_crc << 1);
}
}
return calc_crc == crc;
}
static inline uint8_t gxht30_send_command(uint8_t addr, uint8_t cmd_msb,
uint8_t cmd_lsb) {
uint8_t cmd[2] = {cmd_msb, cmd_lsb};
uint8_t err = i2c_write(addr, cmd, 2);
return (err == I2C_OK) ? GXHT30_OK : GXHT30_ERR_I2C;
}
static inline uint8_t gxht30_read_data(uint8_t addr, GXHT30_Data* data) {
uint8_t cmd[2] = {GXHT30_CMD_MEAS_MSB, GXHT30_CMD_MEAS_LSB};
uint8_t rx_data[6];
uint8_t err;
// send measurement command and read
if ((err = i2c_write_read(addr, cmd, 2, rx_data, 6)) != I2C_OK) {
data->error = GXHT30_ERR_I2C;
return GXHT30_ERR_I2C;
}
// verify CRC for both temp and humidity
if (!gxht30_crc8_check(rx_data[0], rx_data[1], rx_data[2]) ||
!gxht30_crc8_check(rx_data[3], rx_data[4], rx_data[5])) {
data->error = GXHT30_ERR_CRC;
return GXHT30_ERR_CRC;
}
// calc the values
uint16_t temp_raw = (rx_data[0] << 8) | rx_data[1];
uint16_t hum_raw = (rx_data[3] << 8) | rx_data[4];
data->temperature_x100 =
(((int64_t)temp_raw * GXHT30_TEMP_MULTIPLIER) - GXHT30_TEMP_OFFSET) /
GXHT30_SCALE_DIVISOR;
data->humidity_x100 =
((int64_t)hum_raw * GXHT30_HUM_MULTIPLIER) / GXHT30_SCALE_DIVISOR;
data->error = GXHT30_OK;
return GXHT30_OK;
}
static inline uint8_t gxht30_read_status(uint8_t addr, GXHT30_Status* status) {
uint8_t cmd[2] = {GXHT30_CMD_STATUS_MSB, GXHT30_CMD_STATUS_LSB};
uint8_t rx_data[3];
uint8_t err;
if ((err = i2c_write_read(addr, cmd, 2, rx_data, 3)) != I2C_OK) {
return GXHT30_ERR_I2C;
}
// verify CRC
if (!gxht30_crc8_check(rx_data[0], rx_data[1], rx_data[2])) {
return GXHT30_ERR_CRC;
}
// parse status register
uint16_t raw = (rx_data[0] << 8) | rx_data[1];
status->raw_status = raw;
status->alert_pending = (raw >> 15) & 0x01;
status->heater_on = (raw >> 13) & 0x01;
status->humidity_alert = (raw >> 11) & 0x01;
status->temperature_alert = (raw >> 10) & 0x01;
status->reset_detected = (raw >> 4) & 0x01;
status->command_status = (raw >> 1) & 0x01;
status->crc_status = raw & 0x01;
return GXHT30_OK;
}
static inline uint8_t gxht30_soft_reset(uint8_t addr) {
return gxht30_send_command(addr, GXHT30_CMD_SOFT_RESET_MSB,
GXHT30_CMD_SOFT_RESET_LSB);
}
static inline uint8_t gxht30_heater_on(uint8_t addr) {
return gxht30_send_command(addr, GXHT30_CMD_HEATER_ON_MSB,
GXHT30_CMD_HEATER_ON_LSB);
}
static inline uint8_t gxht30_heater_off(uint8_t addr) {
return gxht30_send_command(addr, GXHT30_CMD_HEATER_OFF_MSB,
GXHT30_CMD_HEATER_OFF_LSB);
}
static inline uint8_t gxht30_clear_status(uint8_t addr) {
return gxht30_send_command(addr, GXHT30_CMD_CLEAR_STATUS_MSB,
GXHT30_CMD_CLEAR_STATUS_LSB);
}
#endif // _GXHT30_HW_I2C_H

13
inc/ha_mqtt.h Normal file
View File

@@ -0,0 +1,13 @@
// ha_mqtt.h
#ifndef HA_MQTT_H
#define HA_MQTT_H
#include <stdint.h>
void ha_mqtt_init(void);
void ha_mqtt_publish_sensor_data(int32_t temp_x100, int32_t hum_x100);
void ha_mqtt_check_and_reconnect(void);
uint8_t ha_mqtt_is_connected(void);
#endif // HA_MQTT_H

181
inc/hw_i2c.h Normal file
View File

@@ -0,0 +1,181 @@
#ifndef _HW_I2C_H
#define _HW_I2C_H
#include <stdint.h>
#include "ch32fun.h"
#include "ch32v20xhw.h"
// config
#define I2C_CLKRATE 400000
#define I2C_PRERATE 2000000
#define I2C_TIMEOUT_MAX 250000
#define I2C_DIRECTION_TX 0
#define I2C_DIRECTION_RX 1
enum I2C_Error { I2C_OK = 0, I2C_ERR_TIMEOUT, I2C_ERR_BUSY, I2C_ERR_NACK };
static inline uint8_t i2c_check_event(uint32_t event_mask) {
uint32_t status = I2C1->STAR1 | (I2C1->STAR2 << 16);
return (status & event_mask) == event_mask;
}
static inline uint8_t i2c_wait_event(uint32_t event_mask) {
int32_t timeout = I2C_TIMEOUT_MAX;
while (!i2c_check_event(event_mask) && (timeout-- > 0));
return timeout > 0 ? I2C_OK : I2C_ERR_TIMEOUT;
}
static inline uint8_t i2c_wait_flag(uint32_t flag) {
int32_t timeout = I2C_TIMEOUT_MAX;
while (!(I2C1->STAR1 & flag) && (timeout-- > 0));
return timeout > 0 ? I2C_OK : I2C_ERR_TIMEOUT;
}
static inline uint8_t i2c_start(uint8_t addr, uint8_t direction) {
// wait until bus is not busy
int32_t timeout = I2C_TIMEOUT_MAX;
while ((I2C1->STAR2 & I2C_STAR2_BUSY) && (timeout-- > 0));
if (timeout <= 0) return I2C_ERR_TIMEOUT;
// gen START
I2C1->CTLR1 |= I2C_CTLR1_START;
if (i2c_wait_event(I2C_EVENT_MASTER_MODE_SELECT) != I2C_OK)
return I2C_ERR_TIMEOUT;
// send address
I2C1->DATAR = (addr << 1) | direction;
uint32_t event = (direction == I2C_DIRECTION_TX)
? I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED
: I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED;
return i2c_wait_event(event);
}
static inline void i2c_stop(void) { I2C1->CTLR1 |= I2C_CTLR1_STOP; }
static inline uint8_t i2c_write_byte(uint8_t data) {
I2C1->DATAR = data;
return i2c_wait_flag(I2C_STAR1_TXE);
}
static inline uint8_t i2c_write(uint8_t addr, const uint8_t* data,
uint8_t length) {
uint8_t err;
if ((err = i2c_start(addr, I2C_DIRECTION_TX)) != I2C_OK) return err;
for (uint8_t i = 0; i < length; i++) {
if ((err = i2c_write_byte(data[i])) != I2C_OK) return err;
}
if (i2c_wait_event(I2C_EVENT_MASTER_BYTE_TRANSMITTED) != I2C_OK)
return I2C_ERR_TIMEOUT;
i2c_stop();
return I2C_OK;
}
static inline uint8_t i2c_read(uint8_t addr, uint8_t* buffer, uint8_t length) {
uint8_t err;
if ((err = i2c_start(addr, I2C_DIRECTION_RX)) != I2C_OK) return err;
for (uint8_t i = 0; i < length; i++) {
if (i == length - 1) {
I2C1->CTLR1 &= ~I2C_CTLR1_ACK; // NACK last byte
}
if (i2c_wait_flag(I2C_STAR1_RXNE) != I2C_OK) {
I2C1->CTLR1 |= I2C_CTLR1_ACK;
return I2C_ERR_TIMEOUT;
}
buffer[i] = I2C1->DATAR;
}
I2C1->CTLR1 |= I2C_CTLR1_ACK; // re-enable ACK
i2c_stop();
return I2C_OK;
}
static inline uint8_t i2c_write_read(uint8_t addr, const uint8_t* tx_data,
uint8_t tx_len, uint8_t* rx_data,
uint8_t rx_len) {
uint8_t err;
// Write phase
if ((err = i2c_start(addr, I2C_DIRECTION_TX)) != I2C_OK) return err;
for (uint8_t i = 0; i < tx_len; i++) {
if ((err = i2c_write_byte(tx_data[i])) != I2C_OK) return err;
}
if (i2c_wait_event(I2C_EVENT_MASTER_BYTE_TRANSMITTED) != I2C_OK)
return I2C_ERR_TIMEOUT;
// repeated START for read phase
I2C1->CTLR1 |= I2C_CTLR1_START;
if (i2c_wait_event(I2C_EVENT_MASTER_MODE_SELECT) != I2C_OK)
return I2C_ERR_TIMEOUT;
I2C1->DATAR = (addr << 1) | I2C_DIRECTION_RX;
if (i2c_wait_event(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != I2C_OK)
return I2C_ERR_TIMEOUT;
// read phase
for (uint8_t i = 0; i < rx_len; i++) {
if (i == rx_len - 1) {
I2C1->CTLR1 &= ~I2C_CTLR1_ACK; // NACK last byte
}
if (i2c_wait_flag(I2C_STAR1_RXNE) != I2C_OK) {
I2C1->CTLR1 |= I2C_CTLR1_ACK;
return I2C_ERR_TIMEOUT;
}
rx_data[i] = I2C1->DATAR;
}
I2C1->CTLR1 |= I2C_CTLR1_ACK; // re-enable ACK
i2c_stop();
return I2C_OK;
}
static inline void i2c_init(void) {
uint16_t tempreg;
RCC->APB2PCENR |= RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO;
RCC->APB1PCENR |= RCC_APB1Periph_I2C1;
AFIO->PCFR1 |= AFIO_PCFR1_I2C1_REMAP; // I2C1_RM: 1
// PB8 (SCL) and PB9 (SDA)
funPinMode(PB8, GPIO_CFGLR_OUT_10Mhz_AF_OD);
funPinMode(PB9, GPIO_CFGLR_OUT_10Mhz_AF_OD);
// Reset I2C1 to init all regs
RCC->APB1PRSTR |= RCC_APB1Periph_I2C1;
RCC->APB1PRSTR &= ~RCC_APB1Periph_I2C1;
I2C1->CTLR1 |= I2C_CTLR1_SWRST;
I2C1->CTLR1 &= ~I2C_CTLR1_SWRST;
// set I2C freq
tempreg = I2C1->CTLR2;
tempreg &= ~I2C_CTLR2_FREQ;
tempreg |= (FUNCONF_SYSTEM_CORE_CLOCK / I2C_PRERATE) & I2C_CTLR2_FREQ;
I2C1->CTLR2 = tempreg;
// Fast Mode 400kHz
tempreg = (FUNCONF_SYSTEM_CORE_CLOCK / (3 * I2C_CLKRATE)) & I2C_CKCFGR_CCR;
tempreg |= I2C_CKCFGR_FS;
I2C1->CKCFGR = tempreg;
// en I2C and ACK
I2C1->CTLR1 |= I2C_CTLR1_PE | I2C_CTLR1_ACK;
}
#endif // _HW_I2C_H

141
main.c
View File

@@ -3,6 +3,9 @@
#include "ch32fun.h"
#include "ch32v20xhw.h"
#include "ethernetif.h"
#include "gxht30_hw_i2c.h"
#include "ha_mqtt.h"
#include "hw_i2c.h"
#include "lwip/apps/httpd.h"
#include "lwip/dhcp.h"
#include "lwip/init.h"
@@ -17,7 +20,7 @@
#define HSE_STARTUP_TIMEOUT 10000
#define PLL_LOCK_TIMEOUT 10000
#define LED_TOGGLE_INTERVAL_MS 500
#define LINK_POLL_INTERVAL_MS 500
#define LINK_POLL_INTERVAL_MS 100
#define RCC_PREDIV1_OFFSET 0
#define HSE_CLOCK_MHZ 32
@@ -25,49 +28,68 @@
#define PLL_MULTIPLIER 15
#define STATS_PRINT_INTERVAL_MS 10000
#define SENSOR_READ_INTERVAL_MS 60000
struct netif g_netif;
static volatile int g_httpd_is_initialized = 0;
int clock_init(void);
void led_init(void);
void lwip_stack_init(void);
int clock_init(void) {
RCC->INTR = 0x009f0000;
RCC->CTLR &= ~(RCC_HSE_ON | RCC_PLLON);
RCC->CFGR0 = 0x00000000;
static void set_sysclk_to_120mhz_from_hse(void) {
uint32_t startup_counter = 0;
RCC->CTLR |= RCC_HSE_ON;
for (int timeout = HSE_STARTUP_TIMEOUT; timeout > 0; timeout--) {
if (RCC->CTLR & RCC_HSERDY) break;
if (timeout == 1) {
printf("Error: HSE failed to start\n");
return -1;
RCC->INTR = 0x009F0000; // clear PLL, CSSC, HSE, HSI and LSI ready flags.
// switch processor back to HSI so we don't eat dirt.
RCC->CFGR0 = 0;
// disable PLL so we can play with it.
RCC->CTLR &= ~RCC_PLLON;
// not sure why, need to reset here, otherwise PLLXTPRE is set.
RCC->CFGR0 = RCC_PLLSRC;
// enable HSE
RCC->CTLR |= RCC_HSEON;
do {
startup_counter++;
} while (!(RCC->CTLR & RCC_HSERDY) &&
(startup_counter < HSE_STARTUP_TIMEOUT));
if (RCC->CTLR & RCC_HSERDY) {
/*
* HCLK (AHB) = SYSCLK / 2
* PCLK2 (APB2) = HCLK / 1
* PCLK1 (APB1) = HCLK / 2
* USB Clock = PLLCLK / 5 (to get 48MHz for USB)
*/
RCC->CFGR0 |=
RCC_HPRE_DIV2 | RCC_PPRE2_DIV1 | RCC_PPRE1_DIV2 | RCC_USBPRE_DIV5;
/*
* When RCC_USBPRE is 3 it changes HPE div to /2 instead of /4, so:
* PLL Source: HSE
* HSE Divider for PLL: /2
* PLL Multiplier: x15
* PLL Clock = (32MHz / 2) * 15 = 240 MHz
*/
uint32_t pll_config = RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL15;
RCC->CFGR0 =
(RCC->CFGR0 & ~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL)) | pll_config;
RCC->CTLR |= RCC_PLLON;
// wait for pll to lock
while (!(RCC->CTLR & RCC_PLLRDY)) {
}
}
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
RCC->CFGR2 = (PREDIV1_DIVISOR - 1);
RCC->CFGR0 |= RCC_PLLSource_HSE_Div1 | RCC_PLLMul_15;
// PLL as the system clock src
RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_PLL;
RCC->CTLR |= RCC_PLLON;
printf("Main PLL en. Waiting for lock...\n");
for (int timeout = PLL_LOCK_TIMEOUT; timeout > 0; timeout--) {
if (RCC->CTLR & RCC_PLLRDY) break;
if (timeout == 1) {
printf("Error: Main PLL lock failed\n");
return -1;
while ((RCC->CFGR0 & RCC_SWS) != RCC_SWS_PLL) {
}
} else {
printf("HSE failed to start\n");
}
printf("Main PLL Locked\n");
RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_PLL;
while ((RCC->CFGR0 & RCC_SWS) != RCC_SWS_PLL);
printf("System clock set to %dMHz.\n",
(HSE_CLOCK_MHZ / PREDIV1_DIVISOR) * PLL_MULTIPLIER);
return 0;
}
void led_init(void) {
@@ -159,15 +181,17 @@ void ethernetif_print_stats(void) {
int main() {
SystemInit();
if (clock_init() != 0) {
// eating dirt?
while (1);
}
set_sysclk_to_120mhz_from_hse();
systick_init();
led_init();
lwip_stack_init();
i2c_init();
gxht30_soft_reset(GXHT30_I2C_ADDR_DEFAULT);
Delay_Ms(10);
ha_mqtt_init();
uint32_t last_led_toggle_time = 0;
uint32_t last_link_poll_time = 0;
@@ -176,6 +200,13 @@ int main() {
#endif
int led_state = 0;
uint32_t last_sensor_read_time = 0;
uint32_t last_status_read_time = 0;
GXHT30_Data sensor_data = {0};
printf("System initialized. Main loop starting.\n");
while (1) {
ethernetif_input(&g_netif);
sys_check_timeouts();
@@ -192,15 +223,39 @@ int main() {
}
#endif
uint32_t now = millis();
if (now - last_led_toggle_time > LED_TOGGLE_INTERVAL_MS) {
if (led_state) {
GPIOA->BSHR = (1 << LED1_PIN);
ha_mqtt_check_and_reconnect();
if (millis() - last_sensor_read_time > SENSOR_READ_INTERVAL_MS) {
if (gxht30_read_data(GXHT30_I2C_ADDR_DEFAULT, &sensor_data) ==
GXHT30_OK) {
// int32_t temp_int = sensor_data.temperature_x100 / 100;
// int32_t temp_frac = sensor_data.temperature_x100 % 100;
// if (temp_frac < 0) temp_frac = -temp_frac;
// int32_t hum_int = sensor_data.humidity_x100 / 100;
// int32_t hum_frac = sensor_data.humidity_x100 % 100;
// printf("Read Sensor -> Temp: %ld.%02ld C, Hum: %ld.%02ld %%\n",
// temp_int, temp_frac, hum_int, hum_frac);
ha_mqtt_publish_sensor_data(sensor_data.temperature_x100,
sensor_data.humidity_x100);
} else {
GPIOA->BSHR = (1 << (LED1_PIN + 16));
printf("Failed to read sensor. Error: %d\n", sensor_data.error);
}
led_state = !led_state;
last_led_toggle_time = now;
last_sensor_read_time = millis();
}
// uint32_t now = millis();
// if (now - last_led_toggle_time > LED_TOGGLE_INTERVAL_MS) {
// if (led_state) {
// GPIOA->BSHR = (1 << LED1_PIN);
// } else {
// GPIOA->BSHR = (1 << (LED1_PIN + 16));
// }
// led_state = !led_state;
// last_led_toggle_time = now;
// }
}
}

View File

@@ -32,4 +32,6 @@
#define LWIP_RAND() ((u32_t)rand())
#include "arch/sys_arch.h"
#endif /* LWIP_ARCH_CC_H */

View File

@@ -1,8 +1,9 @@
#include "sys_arch.h"
#include "ch32fun.h"
#include "lwip/def.h"
#include "lwip/sys.h"
#include "systick.h"
typedef uint32_t sys_prot_t;
static unsigned long next = 1;
int rand(void) {
@@ -12,14 +13,14 @@ int rand(void) {
void srand(unsigned int seed) { next = seed; }
uint32_t sys_now(void) { return systick_millis; }
uint32_t sys_now(void) { return millis(); }
sys_prot_t sys_arch_protect(void) {
unsigned int old_mstatus;
__asm__ volatile("csrrci %0, mstatus, 8" : "=r"(old_mstatus));
return old_mstatus;
__disable_irq();
return 1;
}
void sys_arch_unprotect(sys_prot_t pval) {
__asm__ volatile("csrw mstatus, %0" : : "r"(pval));
(void)pval;
__enable_irq();
}

8
port/arch/sys_arch.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef LWIP_ARCH_SYS_ARCH_H
#define LWIP_ARCH_SYS_ARCH_H
#include <stdint.h>
typedef uint32_t sys_prot_t;
#endif /* LWIP_ARCH_SYS_ARCH_H */

View File

@@ -8,41 +8,24 @@
#include "ch32v20xhw.h"
#include "lwip/etharp.h"
#include "lwip/snmp.h"
#include "systick.h"
#define CH32V208_ETH_IMPLEMENTATION
#define ETH_RX_BUF_COUNT 4
#define ETH_TX_BUF_COUNT 2
#include "ch32v208_eth.h"
#define IFNAME0 'e'
#define IFNAME1 'n'
#define ETH_RXBUFNB 4
#define ETH_TXBUFNB 1
#define ETH_RX_BUF_SZE ETH_MAX_PACKET_SIZE
#define ETH_TX_BUF_SZE ETH_MAX_PACKET_SIZE
static volatile bool g_link_changed = false;
struct ethernetif {
ETH_DMADESCTypeDef* DMARxDescToGet;
ETH_DMADESCTypeDef* DMATxDescToSet;
};
__attribute__((aligned(4))) ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB];
__attribute__((aligned(4))) ETH_DMADESCTypeDef DMATxDscrTab[ETH_TXBUFNB];
__attribute__((aligned(4))) uint8_t MACRxBuf[ETH_RXBUFNB * ETH_RX_BUF_SZE];
__attribute__((aligned(4))) uint8_t MACTxBuf[ETH_TXBUFNB * ETH_TX_BUF_SZE];
static volatile bool g_link_interrupt_flag = false;
static struct ethernetif eth_state;
static void low_level_init(struct netif* netif);
static void eth_link_callback(bool link_up);
static err_t low_level_output(struct netif* netif, struct pbuf* p);
static struct pbuf* low_level_input(struct netif* netif);
void WritePHYReg(uint8_t reg_add, uint16_t reg_val);
uint16_t ReadPHYReg(uint8_t reg_add);
static void eth_get_mac_in_uc(uint8_t* mac) {
// Mac is backwards.
const uint8_t* macaddr = (const uint8_t*)(ROM_CFG_USERADR_ID + 5);
for (int i = 0; i < 6; i++) {
mac[i] = *(macaddr--);
}
static void eth_link_callback(bool link_up) {
(void)link_up;
g_link_changed = true;
}
err_t ethernetif_init(struct netif* netif) {
@@ -50,7 +33,7 @@ err_t ethernetif_init(struct netif* netif) {
netif->hostname = "lwip-ch32";
#endif
netif->state = &eth_state;
netif->state = &g_eth_state;
netif->name[0] = IFNAME0;
netif->name[1] = IFNAME1;
@@ -58,217 +41,106 @@ err_t ethernetif_init(struct netif* netif) {
netif->linkoutput = low_level_output;
MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, 10000000); // 10Mbps
netif->mtu = 1500;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
eth_config_t eth_cfg = {.mac_addr = NULL, // we'll use part uuid MAC
.rx_callback = NULL, // no cb, polling API
.link_callback = eth_link_callback,
.promiscuous_mode = false,
.broadcast_filter = true,
.multicast_filter = true};
if (eth_init(&eth_cfg) != 0) {
printf("ERROR: Ethernet initialization failed\n");
return ERR_IF;
}
// get MAC from driver
netif->hwaddr_len = ETH_HWADDR_LEN;
eth_get_mac_in_uc(netif->hwaddr);
eth_get_mac_address(netif->hwaddr);
printf("MAC Address: %02X:%02X:%02X:%02X:%02X:%02X\n", netif->hwaddr[0],
netif->hwaddr[1], netif->hwaddr[2], netif->hwaddr[3], netif->hwaddr[4],
netif->hwaddr[5]);
netif->mtu = 1500;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
low_level_init(netif);
return ERR_OK;
}
static void low_level_init(struct netif* netif) {
struct ethernetif* ethernetif = netif->state;
// clocks
RCC->APB2PCENR |= RCC_APB2Periph_AFIO;
RCC->CFGR0 |= RCC_ETHPRE; // div 2
EXTEN->EXTEN_CTR |= EXTEN_ETH_10M_EN;
// reset mac rx and tx
ETH10M->ECON1 = RB_ETH_ECON1_TXRST | RB_ETH_ECON1_RXRST;
ETH10M->ECON1 = 0;
// mac regs
ETH10M->ERXFCON = RB_ETH_ERXFCON_BCEN | RB_ETH_ERXFCON_MCEN;
ETH10M->MACON1 = RB_ETH_MACON1_MARXEN;
ETH10M->MACON2 = PADCFG_AUTO_3 | RB_ETH_MACON2_TXCRCEN;
ETH10M->MAMXFL = ETH_MAX_PACKET_SIZE;
R8_ETH_MAADRL1 = netif->hwaddr[5];
R8_ETH_MAADRL2 = netif->hwaddr[4];
R8_ETH_MAADRL3 = netif->hwaddr[3];
R8_ETH_MAADRL4 = netif->hwaddr[2];
R8_ETH_MAADRL5 = netif->hwaddr[1];
R8_ETH_MAADRL6 = netif->hwaddr[0];
// PHY analog block
ETH10M->ECON2 = RB_ETH_ECON2_DEFAULT;
// init TX descriptors
ethernetif->DMATxDescToSet = DMATxDscrTab;
for (int i = 0; i < ETH_TXBUFNB; i++) {
DMATxDscrTab[i].Status = 0;
DMATxDscrTab[i].Buffer1Addr = (uint32_t)&MACTxBuf[i * ETH_TX_BUF_SZE];
DMATxDscrTab[i].Buffer2NextDescAddr =
(uint32_t)&DMATxDscrTab[(i + 1) % ETH_TXBUFNB];
}
// init RX descriptors
ethernetif->DMARxDescToGet = DMARxDscrTab;
for (int i = 0; i < ETH_RXBUFNB; i++) {
DMARxDscrTab[i].Status = 0;
DMARxDscrTab[i].Buffer1Addr = (uint32_t)&MACRxBuf[i * ETH_RX_BUF_SZE];
DMARxDscrTab[i].Buffer2NextDescAddr =
(uint32_t)&DMARxDscrTab[(i + 1) % ETH_RXBUFNB];
}
// set RX buffer start and enable receiver
ETH10M->ERXST = ethernetif->DMARxDescToGet->Buffer1Addr;
ETH10M->ECON1 = RB_ETH_ECON1_RXEN;
WritePHYReg(PHY_BMCR, PHY_BMCR_RESET);
Delay_Ms(200);
WritePHYReg(PHY_BMCR, PHY_BMCR_FULL_DUPLEX);
ETH10M->EIR = 0xFF; // clear all interrupt flags
ETH10M->EIE = RB_ETH_EIE_INTIE | RB_ETH_EIE_TXIE | RB_ETH_EIE_LINKIE |
RB_ETH_EIE_TXERIE | RB_ETH_EIE_RXERIE | RB_ETH_EIE_R_EN50;
NVIC_EnableIRQ(ETH_IRQn);
}
static err_t low_level_output(struct netif* netif, struct pbuf* p) {
(void)netif;
if (DMATxDscrTab[0].Status & ETH_DMATxDesc_OWN) {
LINK_STATS_INC(link.drop);
return ERR_BUF;
// single-segment pbuf
if (p->next == NULL && p->len <= ETH_TX_BUF_SIZE) {
if (eth_send_packet(p->payload, p->len) == 0) {
LINK_STATS_INC(link.xmit);
return ERR_OK;
}
}
uint32_t len = 0;
uint8_t* tx_buf_ptr = (uint8_t*)DMATxDscrTab[0].Buffer1Addr;
// chain of pbufs
static uint8_t tx_buffer[ETH_TX_BUF_SIZE];
uint32_t total_len = 0;
for (struct pbuf* q = p; q != NULL; q = q->next) {
memcpy(&tx_buf_ptr[len], q->payload, q->len);
len += q->len;
}
ETH10M->ETXLN = len;
ETH10M->ETXST = (uint32_t)tx_buf_ptr;
DMATxDscrTab[0].Status |= ETH_DMATxDesc_OWN;
ETH10M->ECON1 |= RB_ETH_ECON1_TXRTS;
LINK_STATS_INC(link.xmit);
MIB2_STATS_NETIF_ADD(netif, ifoutoctets, len);
return ERR_OK;
}
static struct pbuf* low_level_input(struct netif* netif) {
struct ethernetif* ethernetif = netif->state;
if (!(ETH10M->EIR & RB_ETH_EIR_RXIF)) {
return NULL;
}
uint16_t len = ETH10M->ERXLN;
if (len < MIN_ETH_FRAME_SIZE || len > ETH_MAX_PACKET_SIZE) {
LINK_STATS_INC(link.lenerr);
ETH10M->EIR = RB_ETH_EIR_RXIF;
ETH10M->ECON1 |= RB_ETH_ECON1_RXEN;
return NULL;
}
uint8_t* current_rx_buffer_ptr =
(uint8_t*)ethernetif->DMARxDescToGet->Buffer1Addr;
struct pbuf* p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
if (p != NULL) {
uint32_t offset = 0;
for (struct pbuf* q = p; q != NULL; q = q->next) {
memcpy(q->payload, current_rx_buffer_ptr + offset, q->len);
offset += q->len;
if (total_len + q->len > ETH_TX_BUF_SIZE) {
LINK_STATS_INC(link.err);
return ERR_BUF;
}
LINK_STATS_INC(link.recv);
MIB2_STATS_NETIF_ADD(netif, ifinoctets, len);
} else {
LINK_STATS_INC(link.memerr);
LINK_STATS_INC(link.drop);
MIB2_STATS_NETIF_INC(netif, ifindiscards);
memcpy(&tx_buffer[total_len], q->payload, q->len);
total_len += q->len;
}
// move to next descriptor
ethernetif->DMARxDescToGet =
(ETH_DMADESCTypeDef*)ethernetif->DMARxDescToGet->Buffer2NextDescAddr;
ETH10M->ERXST = (uint32_t)ethernetif->DMARxDescToGet->Buffer1Addr;
ETH10M->EIR = RB_ETH_EIR_RXIF;
ETH10M->ECON1 |= RB_ETH_ECON1_RXEN;
if (eth_send_packet(tx_buffer, total_len) == 0) {
LINK_STATS_INC(link.xmit);
return ERR_OK;
}
return p;
LINK_STATS_INC(link.drop);
return ERR_BUF;
}
void ethernetif_input(struct netif* netif) {
struct pbuf* p;
while ((p = low_level_input(netif)) != NULL) {
if (netif->input(p, netif) != ERR_OK) {
pbuf_free(p);
uint16_t length;
const uint8_t* packet;
// process all pending packets using polling API
while ((packet = eth_get_rx_packet(&length)) != NULL) {
struct pbuf* p = pbuf_alloc(PBUF_RAW, length, PBUF_POOL);
if (p != NULL) {
// usually contiguous in PBUF_POOL
pbuf_take(p, packet, length);
LINK_STATS_INC(link.recv);
// pass to lwIP
if (netif->input(p, netif) != ERR_OK) {
pbuf_free(p);
}
} else {
// oom
LINK_STATS_INC(link.memerr);
LINK_STATS_INC(link.drop);
}
// release packet back to driver
eth_release_rx_packet();
}
}
void ethernetif_link_poll(struct netif* netif) {
if (!g_link_interrupt_flag) return;
g_link_interrupt_flag = false;
// driver does PHY polling and autoneg
eth_poll_link();
// supposedly, first read latches link status 2nd get cur val
(void)ReadPHYReg(PHY_BMSR);
uint16_t bmsr = ReadPHYReg(PHY_BMSR);
if (g_link_changed) {
g_link_changed = false;
if (bmsr & PHY_BMSR_LINK_STATUS) {
if (!netif_is_link_up(netif)) {
ETH10M->MACON2 |= RB_ETH_MACON2_FULDPX;
bool link_up = eth_is_link_up();
if (link_up && !netif_is_link_up(netif)) {
netif_set_link_up(netif);
}
} else {
if (netif_is_link_up(netif)) {
} else if (!link_up && netif_is_link_up(netif)) {
netif_set_link_down(netif);
}
}
}
void ETH_IRQHandler(void) __attribute__((interrupt)) __attribute__((used));
void ETH_IRQHandler(void) {
uint32_t flags = ETH10M->EIR;
if (flags & RB_ETH_EIR_TXIF) {
DMATxDscrTab[0].Status &= ~ETH_DMATxDesc_OWN;
ETH10M->EIR = RB_ETH_EIR_TXIF;
}
if (flags & RB_ETH_EIR_TXERIF) {
DMATxDscrTab[0].Status &= ~ETH_DMATxDesc_OWN;
ETH10M->EIR = RB_ETH_EIR_TXERIF;
LINK_STATS_INC(link.err);
}
if (flags & RB_ETH_EIR_RXERIF) {
ETH10M->EIR = RB_ETH_EIR_RXERIF;
ETH10M->ECON1 |= RB_ETH_ECON1_RXEN;
LINK_STATS_INC(link.err);
}
if (flags & RB_ETH_EIR_LINKIF) {
g_link_interrupt_flag = true;
ETH10M->EIR = RB_ETH_EIR_LINKIF;
}
}
void WritePHYReg(uint8_t reg_add, uint16_t reg_val) {
R32_ETH_MIWR = (reg_add & RB_ETH_MIREGADR_MASK) | RB_ETH_MIWR_MIIWR |
(reg_val << RB_ETH_MIWR_DATA_SHIFT);
}
uint16_t ReadPHYReg(uint8_t reg_add) {
ETH10M->MIERGADR = reg_add;
return ETH10M->MIRD;
}

View File

@@ -4,57 +4,45 @@
#include "lwip/err.h"
#include "lwip/netif.h"
void run_tx_test(void);
void WritePHYReg(uint8_t reg_add, uint16_t reg_val);
uint16_t ReadPHYReg(uint8_t reg_add);
#define ROM_CFG_USERADR_ID 0x1FFFF7E8
#define ETH_DMARxDesc_FrameLengthShift 16
#define ETH_MAX_PACKET_SIZE \
1536 /* ETH_HEADER + VLAN_TAG + MAX_ETH_PAYLOAD + ETH_CRC */
#define ETH_HEADER \
14 /* 6 byte Dest addr, 6 byte Src addr, 2 byte length/type \
*/
#define ETH_CRC 4 /* Ethernet CRC */
#define ETH_EXTRA 2 /* Extra bytes in some cases */
#define VLAN_TAG 4 /* optional 802.1q VLAN Tag */
#define MIN_ETH_PAYLOAD 46 /* Minimum Ethernet payload size */
#define MAX_ETH_PAYLOAD 1500 /* Maximum Ethernet payload size */
#define MIN_ETH_FRAME_SIZE (ETH_HEADER + MIN_ETH_PAYLOAD) /* 60 bytes */
typedef struct {
uint32_t volatile Status; /* Status */
uint32_t ControlBufferSize; /* Control and Buffer1, Buffer2 lengths */
uint32_t Buffer1Addr; /* Buffer1 address pointer */
uint32_t Buffer2NextDescAddr; /* Buffer2 or next descriptor address pointer */
} ETH_DMADESCTypeDef;
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Initialize the ethernet interface and lwIP network stack.
* This function should be passed as the init function to netif_add().
* Should be called at the beginning of the program to set up the
* network interface. It calls the function low_level_init() to do the
* actual setup of the hardware.
*
* @param netif The lwIP network interface structure to be initialized.
* @return ERR_OK if the loopif is initialized, ERR_MEM if private data couldn't
* be allocated.
* This function should be passed as a parameter to netif_add().
*
* @param netif the lwip network interface structure for this ethernetif
* @return ERR_OK if the loopif is initialized
* ERR_MEM if private data couldn't be allocated
* any other err_t on error
*/
err_t ethernetif_init(struct netif* netif);
/**
* @brief This function should be called periodically from your main loop
* to check for incoming packets and pass them to lwIP.
* This function should be called when a packet is ready to be read
* from the interface. It uses the function low_level_input() that
* should handle the actual reception of bytes from the network
* interface. Then the type of the received packet is determined and
* the appropriate input function is called.
*
* @param netif The lwIP network interface structure.
* @param netif the lwip network interface structure for this ethernetif
*/
void ethernetif_input(struct netif* netif);
/**
* @brief This function should be called periodically from your main loop
* to check the link status and update lwIP accordingly.
* This function should be called periodically from the main loop
* to check the link status and update lwIP accordingly.
*
* @param netif The lwIP network interface structure..
* @param netif the lwip network interface structure for this ethernetif
*/
void ethernetif_link_poll(struct netif* netif);
#ifdef __cplusplus
}
#endif
#endif /* __ETHERNETIF_H */

View File

@@ -11,59 +11,53 @@
// #define ETHARP_DEBUG LWIP_DBG_ON
#define NO_SYS 1
// Core locking
#define SYS_LIGHTWEIGHT_PROT 0
// Memory options
#define MEM_ALIGNMENT 4
#define MEM_SIZE (10 * 1024) // 4KB of RAM for lwIP heap
// Pbuf options
#define PBUF_POOL_SIZE 24 // Enough for bursts
#define PBUF_POOL_BUFSIZE 600 // ~500 byte typical packet + header
// TCP options
#define LWIP_TCP 1
#define TCP_MSS 1460
#define TCP_SND_BUF (2 * TCP_MSS)
// UDP options
#define LWIP_UDP 1
// ICMP options
#define LWIP_ICMP 1
// DHCP options
#define LWIP_DHCP 1
// Checksum options
// #define CHECKSUM_GEN_IP 0
// #define CHECKSUM_GEN_UDP 0
// #define CHECKSUM_GEN_TCP 0
// #define CHECKSUM_CHECK_IP 0
#define CHECKSUM_CHECK_UDP 0
// #define CHECKSUM_CHECK_TCP 0
// #define LWIP_CHECKSUM_ON_COPY 1
#define LWIP_NETCONN 0
#define LWIP_SOCKET 0
// Statistics
#define LWIP_STATS 0
#define LINK_STATS 0
#define MIB2_STATS 0
// Memory options
#define MEM_ALIGNMENT 4
#define MEM_SIZE (4 * 1024)
// Pbuf options
#define PBUF_POOL_SIZE 16
#define PBUF_POOL_BUFSIZE 590
// TCP options
#define LWIP_TCP 1
#define TCP_MSS 536
#define TCP_SND_BUF (2 * TCP_MSS)
#define TCP_WND (4 * TCP_MSS)
#define TCP_QUEUE_OOSEQ 0
#define LWIP_UDP 1
#define MEMP_NUM_UDP_PCB 3 // # of concurrent UDP "connections"
#define MEMP_NUM_SYS_TIMEOUT 8
#define LWIP_ICMP 1
#define LWIP_DHCP 1
#define LWIP_HTTPD 1
// Use a read-only filesystem populated by makefsdata
// #define HTTPD_FSDATA_FILE "fsdata_custom.c"
#define LWIP_HTTPD_FS_SUPPORT 1
#define HTTPD_USE_CUSTOM_FSDATA 1
// MQTT
#define LWIP_MQTT_CLIENT 1
#define MQTT_OUTPUT_RINGBUF_SIZE 512
#define LWIP_NETIF_HOSTNAME 1
#define LWIP_NETIF_LINK_CALLBACK 1
#define LWIP_NETIF_STATUS_CALLBACK 1
#define LWIP_SUPPORT_CUSTOM_PBUF 1 // for zero-copy
#define LWIP_SUPPORT_CUSTOM_PBUF 1
#define CHECKSUM_GEN_IP 1
#define CHECKSUM_GEN_UDP 1
#define CHECKSUM_GEN_TCP 1
#define CHECKSUM_CHECK_IP 1
#define CHECKSUM_CHECK_UDP 1
#define CHECKSUM_CHECK_TCP 1
#define LWIP_CHECKSUM_ON_COPY 0
#define LWIP_STATS 0
#define LINK_STATS 0
#endif /* __LWIPOPTS_H__ */