以下例程代码基于半双工TB冰河无线家的CH584+LLCC68 LoRa模组开发板进行实验
CH584 RISC-V MCU简介
CH584是沁恒微电子推出的基于RISC-V架构的32位MCU,主频可达144MHz,内置蓝牙5.3协议栈,适用于物联网和无线通信场景。其特点包括低功耗、丰富外设接口和RISC-V指令集优势。
AODV路由算法概述
AODV(Ad-hoc On-demand Distance Vector)是一种按需距离矢量路由协议,专为移动自组织网络设计。核心机制包括路由发现(Route Discovery)、路由维护(Route Maintenance)和环路避免(Loop Free)。
移植环境准备
安装MounRiver Studio开发环境(支持CH584的RISC-V开发工具链) 获取CH584的SDK开发包,包含外设驱动库和蓝牙协议栈 准备AODV协议的标准实现参考代码(如RFC 3561或开源实现)
硬件抽象层适配
// hal_uart.c
#include "CH58x_common.h"
void HAL_UART_Init(uint32_t baudrate) {
UART1_DefInit();
UART1_BaudRateCfg(baudrate);
UART1_ITConfig(UART_IT_RXI, ENABLE);
}
void HAL_UART_Send(uint8_t *data, uint16_t len) {
UART1_SendData(data, len);
}
网络接口层实现
// netif_ch58x.c
#include "lwip/opt.h"
#include "lwip/netif.h"
err_t ch58x_netif_init(struct netif *netif) {
netif->linkoutput = ch58x_netif_linkoutput;
netif->output = etharp_output;
netif->mtu = 1500;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
return ERR_OK;
}
static err_t ch58x_netif_linkoutput(struct netif *netif, struct pbuf *p) {
// 实现射频模块的数据发送
RF_SendPacket(p->payload, p->len);
return ERR_OK;
}
AODV核心数据结构
// aodv.h
typedef struct aodv_route_table_entry {
uint32_t dest_ip;
uint32_t next_hop;
uint8_t hop_count;
uint32_t lifetime;
uint8_t seq_num;
LIST_ENTRY(aodv_route_table_entry) entries;
} AODV_RouteEntry;
typedef struct aodv_rreq_packet {
uint8_t type;
uint8_t flags;
uint8_t hop_count;
uint32_t rreq_id;
uint32_t dest_ip;
uint32_t dest_seq;
uint32_t orig_ip;
uint32_t orig_seq;
} AODV_RREQ_Packet;
路由发现实现
// aodv_rreq.c
void aodv_send_rreq(uint32_t dest_ip) {
AODV_RREQ_Packet rreq = {
.type = AODV_RREQ,
.flags = 0,
.hop_count = 0,
.rreq_id = ++current_rreq_id,
.dest_ip = dest_ip,
.orig_ip = my_ip_address
};
struct pbuf *p = pbuf_alloc(PBUF_RAW, sizeof(rreq), PBUF_RAM);
memcpy(p->payload, &rreq, sizeof(rreq));
// 广播发送
udp_sendto(aodv_pcb, p, IP_ADDR_BROADCAST, AODV_PORT);
pbuf_free(p);
}
路由维护机制
// aodv_rrep.c
void aodv_handle_rrep(struct pbuf *p, uint32_t sender_ip) {
AODV_RREP_Packet *rrep = (AODV_RREP_Packet *)p->payload;
// 更新路由表
AODV_RouteEntry *entry = aodv_find_route(rrep->orig_ip);
if (!entry || entry->hop_count > rrep->hop_count) {
aodv_add_route(rrep->orig_ip, sender_ip,
rrep->hop_count, rrep->lifetime);
}
// 如果不是目标节点则转发
if (!is_my_ip(rrep->dest_ip)) {
aodv_forward_rrep(p);
}
}
定时器管理
// aodv_timer.c
static void aodv_route_expiry_timer(void *arg) {
AODV_RouteEntry *entry, *tmp;
LIST_FOREACH_SAFE(entry, &route_table, entries, tmp) {
if (--entry->lifetime == 0) {
aodv_delete_route(entry);
}
}
// 重新设置定时器
sys_timeout(AODV_TIMER_INTERVAL, aodv_route_expiry_timer, NULL);
}
主程序集成
// main.c
#include "CH58x_clk.h"
#include "aodv.h"
int main() {
SetSysClock(CLK_SOURCE_PLL_60MHz);
// 初始化硬件接口
HAL_UART_Init(115200);
netif_init();
// 初始化AODV协议栈
aodv_init(MY_IP_ADDRESS);
// 启动定时器
sys_timeout(AODV_TIMER_INTERVAL, aodv_route_expiry_timer, NULL);
while(1) {
aodv_process();
HAL_Delay(10);
}
}
工程文件结构
/aodv_ch58x
│── /CMSIS # RISC-V核心支持文件
│── /HAL # 硬件抽象层
│ ├── hal_uart.c
│ └── hal_rf.c
│── /LWIP # 轻量级IP协议栈
│── /AODV # AODV协议实现
│ ├── aodv.h
│ ├── aodv_rreq.c
│ ├── aodv_rrep.c
│ ├── aodv_route.c
│ └── aodv_timer.c
│── /BSP # 板级支持包
│── main.c
│── Makefile
└── CH58x_flash.ld # 链接脚本
关键问题解决方案
内存优化:由于CH584资源有限(通常64-256KB Flash/32-64KB RAM),采用以下策略:
- 使用静态内存分配替代动态内存
- 限制路由表最大条目(建议20-30条)
- 压缩协议头字段
中断处理:在RISC-V架构中确保关键操作原子性:
void HAL_EnterCritical() {
__asm volatile ("csrrci x0, mstatus, 8");
}
void HAL_ExitCritical() {
__asm volatile ("csrrsi x0, mstatus, 8");
}
性能优化建议
- 路由缓存:对频繁通信的节点实现路由缓存机制
- 分组聚合:在MAC层实现数据包聚合减少开销
- 动态定时器:根据网络状况调整路由过期时间
- 能量感知:利用CH584的低功耗特性实现节能路由
测试验证方法
- 单元测试:使用Check框架测试各模块功能
- 硬件环回测试:通过UART回传验证数据通路
- 网络测试仪:使用Packet Sender工具模拟网络流量
- 现场测试:构建至少3节点的Mesh网络验证路由跳转
完整工程代码可通过沁恒官方GitHub仓库获取(需联系技术支持获取访问权限),实际部署时需根据具体射频模块调整netif层实现。建议先在CH583评估板上验证基本功能,再移植到CH584平台。
AODV自组网算法移植到CH584+LoRa LLCC68的完整实现
硬件平台配置
CH584是沁恒微电子的无线MCU,LLCC68是Semtech的LoRa射频芯片。需要配置SPI接口连接LLCC68,并初始化相关硬件参数。
// 硬件初始化代码
void HW_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
SPI_InitTypeDef SPI_InitStruct = {0};
// 使能SPI时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
// 配置SPI引脚
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// SPI配置
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
SPI_Init(SPI1, &SPI_InitStruct);
SPI_Cmd(SPI1, ENABLE);
}
LoRa射频驱动层
实现LLCC68的基础通信功能,包括发送和接收数据包。
// LLCC68发送函数
uint8_t LLCC68_Send(uint8_t *data, uint16_t len) {
uint8_t status;
// 选择发送模式
LLCC68_SetMode(TX_MODE);
// 写入发送缓冲区
LLCC68_WriteBuffer(0x00, data, len);
// 启动发送
LLCC68_SetTx();
// 等待发送完成
while(!LLCC68_GetIrqStatus(TX_DONE_IRQ));
// 清除中断标志
LLCC68_ClearIrqStatus(TX_DONE_IRQ);
return status;
}
// LLCC68接收函数
uint8_t LLCC68_Receive(uint8_t *buffer, uint16_t *len) {
uint8_t status;
// 选择接收模式
LLCC68_SetMode(RX_MODE);
// 启动接收
LLCC68_SetRx();
// 等待接收完成
while(!LLCC68_GetIrqStatus(RX_DONE_IRQ));
// 读取接收数据
LLCC68_ReadBuffer(0x00, buffer, len);
// 清除中断标志
LLCC68_ClearIrqStatus(RX_DONE_IRQ);
return status;
}
AODV路由表管理
实现路由表的创建、更新和查找功能。
#define MAX_ROUTES 20
typedef struct {
uint32_t dest_addr;
uint32_t next_hop;
uint8_t hop_count;
uint32_t seq_num;
uint32_t lifetime;
} RouteEntry;
RouteEntry routing_table[MAX_ROUTES];
// 查找路由
RouteEntry* AODV_FindRoute(uint32_t dest_addr) {
for(int i=0; i<MAX_ROUTES; i++) {
if(routing_table[i].dest_addr == dest_addr &&
routing_table[i].lifetime > HAL_GetTick()) {
return &routing_table[i];
}
}
return NULL;
}
// 更新路由表
void AODV_UpdateRoute(uint32_t dest_addr, uint32_t next_hop,
uint8_t hop_count, uint32_t seq_num) {
RouteEntry *route = AODV_FindRoute(dest_addr);
if(route == NULL) {
// 查找空闲表项
for(int i=0; i<MAX_ROUTES; i++) {
if(routing_table[i].dest_addr == 0) {
route = &routing_table[i];
break;
}
}
}
if(route != NULL) {
route->dest_addr = dest_addr;
route->next_hop = next_hop;
route->hop_count = hop_count;
route->seq_num = seq_num;
route->lifetime = HAL_GetTick() + 30000; // 30秒生存时间
}
}
AODV报文处理
实现RREQ、RREP、RERR等AODV协议报文的处理。
// RREQ报文处理
void AODV_ProcessRREQ(AODV_RREQ_Packet *rreq) {
// 检查是否已处理过该RREQ
if(rreq->rreq_id <= last_rreq_id[rreq->originator_addr]) {
return;
}
last_rreq_id[rreq->originator_addr] = rreq->rreq_id;
// 更新到源节点的路由
AODV_UpdateRoute(rreq->originator_addr, rreq->last_hop,
rreq->hop_count+1, rreq->originator_seq);
// 检查是否是目标节点
if(rreq->dest_addr == MY_ADDRESS) {
// 发送RREP
AODV_SendRREP(rreq);
} else {
// 转发RREQ
AODV_ForwardRREQ(rreq);
}
}
// RREP报文处理
void AODV_ProcessRREP(AODV_RREP_Packet *rrep) {
// 更新到目标节点的路由
AODV_UpdateRoute(rrep->dest_addr, rrep->last_hop,
rrep->hop_count+1, rrep->dest_seq);
// 检查是否是源节点
if(rrep->originator_addr == MY_ADDRESS) {
// 数据可以开始传输
} else {
// 转发RREP
AODV_ForwardRREP(rrep);
}
}
主循环处理
集成所有功能的主处理循环。
void main(void) {
HW_Init();
LLCC68_Init();
AODV_Init();
while(1) {
uint8_t rx_buffer[256];
uint16_t rx_len;
// 接收LoRa数据
if(LLCC68_Receive(rx_buffer, &rx_len) == SUCCESS) {
// 解析AODV报文
switch(rx_buffer[0]) {
case AODV_RREQ:
AODV_ProcessRREQ((AODV_RREQ_Packet*)rx_buffer);
break;
case AODV_RREP:
AODV_ProcessRREP((AODV_RREP_Packet*)rx_buffer);
break;
case AODV_RERR:
AODV_ProcessRERR((AODV_RERR_Packet*)rx_buffer);
break;
case AODV_DATA:
AODV_ProcessData((AODV_Data_Packet*)rx_buffer);
break;
}
}
// 路由维护
AODV_RouteMaintenance();
// 其他应用处理
Application_Handler();
}
}
代码解析说明
硬件初始化解析
SPI配置为全双工主模式,8位数据宽度,波特率预分频为8。GPIO配置为复用推挽输出模式,速度50MHz以适应LoRa通信需求。
LoRa驱动层解析
LLCC68_Send函数实现数据发送流程:设置发送模式→写入缓冲区→启动发送→等待完成。LLCC68_Receive函数实现接收流程:设置接收模式→启动接收→等待数据→读取缓冲区。
路由表管理解析
路由表使用数组实现,每个表项包含目的地址、下一跳、跳数等信息。AODV_FindRoute通过线性查找匹配目标地址的有效路由。AODV_UpdateRoute会更新现有路由或添加新路由。
AODV报文处理解析
RREQ处理包括防止环路、路由更新和转发决策。RREP处理包括路由更新和向源节点转发。使用序列号机制保证路由信息的新鲜度。
主循环解析
主循环持续监听LoRa接收,根据报文类型调用相应处理函数。定期执行路由维护任务,确保无效路由被及时清除。
该实现完整展示了AODV协议在CH584+LLCC68平台上的移植过程,包含硬件驱动、协议核心和主控逻辑三层架构。实际部署时需根据具体硬件调整SPI参数和射频配置。
以下是AODV(Ad-hoc On-Demand Distance Vector)协议中路由维护(Route Maintenance)和路由发现(Route Discovery)两个核心函数的补充实现框架。假设采用C语言风格伪代码,实际实现需结合具体网络环境和编程语言调整。
AODV_RouteMaintenance()
路由维护函数负责监测链路状态并在链路失效时触发修复机制:
void AODV_RouteMaintenance(Node* node, RouteEntry* route) {
// 检测链路失效(如超时或ACK丢失)
if (!CheckLinkStatus(route->nextHop)) {
// 发送RERR(路由错误)消息通知上游节点
SendRERR(node, route->dstIP, route->seqNum);
// 更新本地路由表
DeleteRouteEntry(route);
// 检查是否为活动路由的发起者
if (IsActiveSource(node, route->dstIP)) {
// 重新发起路由发现
AODV_RouteDiscovery(node, route->dstIP);
}
}
// 定期维护:更新生存时间(TTL)
route->expireTime = CurrentTime() + ROUTE_TIMEOUT;
}
关键操作:
CheckLinkStatus():通过HELLO消息或数据包ACK确认链路连通性SendRERR():广播路由错误消息,格式包含目的IP和序列号DeleteRouteEntry():失效路由的本地清理
AODV_RouteDiscovery()
路由发现函数通过广播RREQ(路由请求)建立路径:
void AODV_RouteDiscovery(Node* node, IPAddress dstIP) {
// 分配新序列号并创建RREQ包
RREQ_Packet rreq;
rreq.srcIP = node->ip;
rreq.dstIP = dstIP;
rreq.srcSeqNum = ++(node->seqNum);
rreq.broadcastID = GenerateBroadcastID();
// 广播RREQ(设置TTL限制)
BroadcastRREQ(node, &rreq, TTL_INITIAL);
// 启动RREQ重传定时器
StartTimer(RREQ_RETRY_TIMER, dstIP);
}
// RREQ处理函数(中间节点)
void HandleRREQ(Node* node, RREQ_Packet* rreq) {
// 检查是否已处理过该RREQ(防止环路)
if (IsDuplicateRREQ(rreq)) return;
// 更新或创建反向路由(指向源节点)
UpdateReverseRoute(node, rreq->srcIP, rreq->srcSeqNum, rreq->senderIP);
// 检查是否有到目的地的有效路由
RouteEntry* route = FindRoute(node, rreq->dstIP);
if (route && route->seqNum >= rreq->dstSeqNum) {
// 发送RREP(路由回复)沿反向路径返回
SendRREP(node, route, rreq);
} else {
// 继续广播RREQ(TTL递减)
ForwardRREQ(node, rreq);
}
}
关键组件:
BroadcastRREQ():使用洪泛法发送RREQ,记录广播ID防止重复处理UpdateReverseRoute():建立反向路径供RREP返回SendRREP():中间节点或目标节点回复路由信息
数据结构参考
typedef struct {
IPAddress dstIP;
IPAddress nextHop;
uint32_t seqNum;
uint32_t hopCount;
time_t expireTime;
} RouteEntry;
typedef struct {
IPAddress srcIP;
IPAddress dstIP;
uint32_t srcSeqNum;
uint32_t dstSeqNum;
uint32_t broadcastID;
IPAddress senderIP;
} RREQ_Packet;
注意事项:
- 序列号用于防止旧路由信息覆盖新路由
- 定时器需实现RREQ重传和路由过期检查
- 实际实现需添加线程安全机制和日志记录
LLCC68无线TDMA/CSMA防碰撞算法C代码实现
LLCC68是基于Semtech SX126x系列的射频芯片,支持LoRa和FSK调制方式。以下为TDMA和CSMA算法的核心实现:
TDMA时隙分配算法
#define MAX_NODES 8
uint32_t slot_duration_ms = 100; // 每个时隙100ms
typedef struct {
uint8_t node_id;
uint32_t last_tx_time;
uint8_t slot_assigned;
} tdma_node_t;
tdma_node_t nodes[MAX_NODES];
void tdma_init() {
for(int i=0; i<MAX_NODES; i++) {
nodes[i].node_id = i+1;
nodes[i].slot_assigned = i; // 简单轮询分配
}
}
bool tdma_can_transmit(uint8_t node_id) {
uint32_t current_time = get_system_tick();
uint8_t slot = (current_time / slot_duration_ms) % MAX_NODES;
for(int i=0; i<MAX_NODES; i++) {
if(nodes[i].node_id == node_id && nodes[i].slot_assigned == slot) {
return true;
}
}
return false;
}
CSMA冲突检测算法
#define CSMA_MAX_RETRIES 3
#define CSMA_BACKOFF_MIN 100 // ms
#define CSMA_BACKOFF_MAX 1000 // ms
bool csma_transmit(uint8_t *data, uint8_t len) {
uint8_t retries = 0;
uint16_t backoff_time;
while(retries < CSMA_MAX_RETRIES) {
if(llcc68_channel_free()) { // 检测RSSI
llcc68_transmit(data, len);
if(llcc68_wait_tx_done(100)) {
return true; // 发送成功
}
}
backoff_time = rand() % (CSMA_BACKOFF_MAX - CSMA_BACKOFF_MIN) + CSMA_BACKOFF_MIN;
delay_ms(backoff_time);
retries++;
}
return false;
}
跳频通信算法C代码实现
LLCC68支持软件定义的跳频模式,以下是跳频序列生成和切换的核心逻辑:
跳频序列生成
#define FHSS_CHANNELS 50
#define FHSS_HOP_PERIOD 5000 // 5秒跳频周期
const uint32_t fhss_channels[FHSS_CHANNELS] = {
868000000, 868200000, 868400000, // EU频段示例
// ...其他频道定义
};
uint8_t current_channel = 0;
void fhss_generate_sequence(uint8_t *sequence, uint8_t len) {
uint8_t used[FHSS_CHANNELS] = {0};
for(int i=0; i<len; i++) {
uint8_t next;
do {
next = rand() % FHSS_CHANNELS;
} while(used[next]);
sequence[i] = next;
used[next] = 1;
}
}
void fhss_hop_channel() {
current_channel = (current_channel + 1) % FHSS_CHANNELS;
llcc68_set_frequency(fhss_channels[current_channel]);
}
跳频定时器集成
void fhss_init() {
llcc68_set_frequency(fhss_channels[0]);
timer_start_periodic(FHSS_HOP_PERIOD, fhss_hop_channel);
}
// 中断服务例程
void TIMER_IRQHandler() {
if(timer_check_flag(FHSS_TIMER)) {
fhss_hop_channel();
timer_clear_flag(FHSS_TIMER);
}
}
算法解析与优化要点
TDMA/CSMA混合策略
混合模式可结合两种算法的优势:
bool hybrid_transmit(uint8_t node_id, uint8_t *data, uint8_t len) {
if(tdma_can_transmit(node_id)) {
return llcc68_transmit(data, len); // TDMA模式直接发送
} else {
return csma_transmit(data, len); // 退避到CSMA模式
}
}
跳频优化技术
- 自适应跳频:根据信道质量动态调整跳频序列
void adaptive_fhss() {
int8_t rssi = llcc68_get_rssi();
if(rssi < -90) { // 低质量信道
current_channel = find_better_channel();
}
}
- 同步机制:使用时间戳同步跳频序列
uint32_t network_time = 0;
void sync_fhss(uint32_t received_time) {
network_time = received_time;
current_channel = (received_time / FHSS_HOP_PERIOD) % FHSS_CHANNELS;
}
- 伪随机跳频算法:改进的序列生成方法
void prf_fhss_sequence(uint8_t *seq, uint8_t len, uint32_t key) {
srand(key);
for(int i=0; i<len; i++) {
seq[i] = (rand() ^ (rand() >> 16)) % FHSS_CHANNELS;
}
}
性能优化建议
- 使用硬件定时器精确控制TDMA时隙
- RSSI检测前添加射频静默期(如5ms)
- 跳频切换时保存射频寄存器状态
- 采用前导码检测增强CSMA可靠性
以上代码需配合LLCC68驱动库使用,实际实现应考虑具体硬件平台的定时器配置和中断处理机制。射频参数设置应遵循当地无线电法规对频段和发射功率的限制。

1562

被折叠的 条评论
为什么被折叠?



