基于AVR与LLCC68的LoRA串口透传实战

张开发
2026/4/19 12:28:26 15 分钟阅读

分享文章

基于AVR与LLCC68的LoRA串口透传实战
1. 项目背景与硬件选型最近在做一个工业数据采集项目需要把分布在厂区各处的传感器数据汇总到控制室。考虑到布线成本问题决定采用LoRa无线方案。翻遍手头的元器件库存发现还有几片闲置的AVR单片机ATMEGA16和Ra-01SC模块LLCC68芯片正好可以派上用场。为什么选择这套组合首先LLCC68是Semtech新一代LoRa芯片相比SX1278功耗更低且支持FSK/GFSK/MSK等多种调制方式。实测在470MHz频段下空旷地带传输距离能达到3公里以上。而AVR单片机虽然性能不如STM32但胜在开发简单寄存器操作直观特别适合这种对实时性要求不高的透传应用。硬件连接方面LLCC68通过SPI接口与AVR通信需要连接以下引脚SCKSPI时钟MOSI主机输出从机输入MISO主机输入从机输出NSS片选信号低电平有效RESET硬件复位BUSY模块状态指示2. 开发环境搭建开发工具我选用Microchip Studio 7.0相比Arduino IDE更适合底层寄存器操作。新建工程时要注意选择正确的器件型号ATMEGA16时钟频率设为8MHz外部晶振。需要准备的软件库包括LLCC68官方驱动GitHub - Lora-net/llcc68_driverAVR标准库包含avr/io.h等头文件串口驱动用于调试信息输出安装完驱动后项目目录结构应该是这样的project/ ├── llcc68_driver/ │ ├── llcc68.c │ ├── llcc68.h │ ├── llcc68_regs.h │ └── llcc68_hal.h ├── main.c ├── radio.c └── radio.h3. 硬件抽象层实现LLCC68驱动要求我们实现四个关键函数3.1 复位函数实现llcc68_hal_status_t llcc68_hal_reset(const void* context) { uint8_t which_one_llcc68 *(uint8_t*)context; if(which_one_llcc68 0) { CLR_LLCC68_0_nRESET(); // 拉低复位引脚 } else { CLR_LLCC68_1_nRESET(); } _delay_us(150); // 保持低电平至少100us SET_LLCC68_0_nRESET(); // 释放复位引脚 SET_LLCC68_1_nRESET(); _delay_us(3500); // 等待芯片初始化完成 return LLCC68_HAL_STATUS_OK; }3.2 SPI读写函数读写函数要注意处理BUSY信号这是LLCC68特有的设计llcc68_hal_status_t llcc68_hal_write(const void* context, const uint8_t* command, const uint16_t command_length, const uint8_t* data, const uint16_t data_length) { uint8_t which_one_llcc68 *(uint8_t*)context; // 等待BUSY信号变低 uint16_t timeout 2000; if(which_one_llcc68 0) { while(LLCC68_0_command_busy timeout--); } else { while(LLCC68_1_command_busy timeout--); } if(timeout 0) return LLCC68_HAL_STATUS_ERROR; // 开始SPI传输 if(which_one_llcc68 0) CLR_LLCC68_0_NSS(); else CLR_LLCC68_1_NSS(); _delay_us(10); for(uint16_t i0; icommand_length; i) { SPI_Write_Read_Byte(command[i]); } for(uint16_t i0; idata_length; i) { SPI_Write_Read_Byte(data[i]); } if(which_one_llcc68 0) SET_LLCC68_0_NSS(); else SET_LLCC68_1_NSS(); return LLCC68_HAL_STATUS_OK; }4. LoRa参数配置4.1 调制参数设置LLCC68支持多种LoRa参数组合需要根据实际需求选择typedef struct { llcc68_lora_sf_t sf; // 扩频因子 llcc68_lora_bw_t bw; // 带宽 llcc68_lora_cr_t cr; // 编码率 uint8_t ldro; // 低数据率优化 } llcc68_mod_params_lora_t; // 典型配置示例 llcc68_mod_params_lora_t lora_para { LLCC68_LORA_SF9, // 扩频因子9 LLCC68_LORA_BW_125, // 125kHz带宽 LLCC68_LORA_CR_4_5, // 4/5编码率 0 // 关闭低数据率优化 };4.2 数据包参数配置数据包格式也需要明确定义typedef struct { uint16_t preamble_len; // 前导码长度 llcc68_lora_pkt_len_mode_t hdr_type; // 包头类型 uint8_t pld_len_in_bytes; // 有效载荷长度 bool crc_is_on; // CRC校验 bool invert_iq_is_on; // IQ反转 } llcc68_pkt_params_lora_t; // 示例配置 llcc68_pkt_params_lora_t pkt_para { 8, // 8个符号的前导码 LLCC68_LORA_PKT_EXPLICIT, // 显式包头 255, // 最大载荷长度 true, // 启用CRC false // 不反转IQ };5. 通信流程实现5.1 发送端实现发送流程分为初始化和发送两个阶段void radiotx_init(const void* context) { // 1. 复位模块 llcc68_hal_reset(context); // 2. 设置工作模式 llcc68_set_standby(context, LLCC68_STANDBY_CFG_RC); llcc68_set_pkt_type(context, LLCC68_PKT_TYPE_LORA); // 3. 配置射频参数 llcc68_set_rf_freq(context, 470300000); // 470.3MHz llcc68_set_dio2_as_rf_sw_ctrl(context, true); // 关键自动天线切换 // 4. 设置功率放大器 llcc68_pa_cfg_params_t pa_cfg {0x04, 0x07, 0x0, 0x01}; llcc68_set_pa_cfg(context, pa_cfg); llcc68_set_tx_params(context, 0x16, LLCC68_RAMP_40_US); // 5. 设置调制参数 llcc68_set_lora_mod_params(context, lora_para); } void radiosend(const void* context, uint8_t* payload, uint8_t length) { // 1. 更新数据包长度 pkt_para.pld_len_in_bytes length; llcc68_set_lora_pkt_params(context, pkt_para); // 2. 写入发送缓冲区 llcc68_write_buffer(context, 0x00, payload, length); // 3. 启动发送 llcc68_set_tx(context, 0); // 0表示单次发送 }5.2 接收端实现接收端需要持续监听收到数据后通过中断处理void radiorx_init(const void* context) { // 初始化步骤与发送端类似... llcc68_set_rx_with_timeout_in_rtc_step(context, LLCC68_RX_CONTINUOUS); } // 中断处理函数 void radio_irq_processing(const void* context) { uint16_t irq_status; llcc68_get_irq_status(context, irq_status); if(irq_status LLCC68_IRQ_TX_DONE) { printf(发送完成\n); } if(irq_status LLCC68_IRQ_RX_DONE) { uint8_t rx_buff[256]; llcc68_rx_buffer_status_t rx_status; llcc68_get_rx_buffer_status(context, rx_status); llcc68_read_buffer(context, rx_status.buffer_start_pointer, rx_buff, rx_status.pld_len_in_bytes); printf(收到数据: %s\n, rx_buff); } }6. 串口透传实现6.1 硬件连接AVR的USART接口通过MAX3485芯片转换为RS485信号接线方式TXD → DIRXD → RO控制引脚 → RE/DE收发使能6.2 软件实现主循环中实现串口到LoRa的透传while(1) { // 检查串口接收 if(usart_rx_counter 0) { radiosend(llcc68_which_0, usart_rx_buff, usart_rx_counter); usart_rx_counter 0; } // 处理LoRa中断 if(radio_irq_flag 0x02) { radio_irq_processing(llcc68_which_1); radio_irq_flag ~0x02; } _delay_ms(10); }7. 调试与优化7.1 常见问题排查无法通信检查SPI时序是否正确BUSY信号是否处理传输距离短调整扩频因子和带宽SF值越高距离越远但速率越低数据错误检查CRC配置确保收发双方参数一致7.2 性能优化建议增加前向纠错(FEC)处理实现自适应速率调整(ADR)添加数据包重传机制优化天线匹配电路实际测试中在市区环境下470MHz频段SF9125kHz带宽实现了1.2km的稳定传输丢包率小于0.1%。这套方案虽然基于老旧的AVR单片机但胜在稳定可靠特别适合对成本敏感但不要求高带宽的工业场景。

更多文章