先简单介绍一下sx1280这款芯片,这款芯片的主要功能用于2.4g频段的数据传输,支持lora,fsk等多种编码格式。本篇文章将教会你如何阅读数据手册和驱动的编写。

数据手册下载

SX1280IMLTRT中文资料_最新报价_数据手册下载_SEMTECH-无线收发芯片-立创商城


如何阅读数据手册

SX1280的数据手册长达143页,在做他的驱动编写的时候想要通篇阅读后完成后再编写会是一个很费力费时的事情,所以得学会更高效的阅读和索引手册中重要的地方。首先最重要肯定就是阅读数据手册目录,我们编写驱动只需要关注他的命令模块就行,其他的物理性质和外部电路完全可以忽略。此外我推荐中文翻译的和英文原版数据手册同时使用最佳。因为中文翻译的手册很多机翻,而且很多重要的命令他某些地方会翻译成中文,某些地方仍然保留英文,这样就不能快速ctrl+f索引,另外有第一手的资料也可以验证中文翻译者有没有翻译过程中出错。而且很多英文数据手册中厂商都已经做好书签,非常方便快速阅读。


以SX1280的lora模式发送数据端为例,我们读数据手册要读什么

我们的目标是使用lora模式发送数据,所以手册目录浏览一遍后,发现我们需要阅读的是目录12命令清单,读了命令清单后,我们可以大致了解都有哪些命令可以使用,接下来驱动里想使用时可以直接搜索每个命令的名字,比如搜0xC0或getstatus,这样直接可以跳转到对应的具体的指令讲解。因为我们使用lora模块,所以还需要读一下13目录的lora操作,这一章目录读完就可以知道我们初始化,收和发需要做什么了。所以我们这样就可以把15个目录简化成阅读两个目录和通过索引阅读我们想要的内容,效率可以大幅提升。

如上两幅图就是演示的通过索引避免了长篇阅读,可以大大提高开发效率。


所以这样思路就很清晰了,我们编写这个模块的驱动,完全不需要知道SX1280内部的编码过程和实现过程,我们只需要关注通信方式把数据和命令扔给SX1280就可以了。


驱动编写

好,可以看到我们初始化需要先进入standby模式,接着需要设置lora数据包类型,然后rf频率,接着是收发缓冲区大小,最后是调制参数。我们可以直接用上面讲到的方法,直接进行索引,搜索standby,直接能跳转到我们所需要的结果(索引的时候用英文的数据手册更好)。

编写代码如下

void My_E28_send(uint8_t *cmd,uint8_t cmd_size)//封装一个e28发送吧
{
    My_spi_nss_en();
	HAL_Delay(5);
    HAL_SPI_Transmit(&hspi1,cmd,cmd_size,100);
    My_spi_nss_close();
    HAL_Delay(5);
    
}

void My_E28_standby()//进入待命模式
{
    uint8_t buf[2];
    buf[0]=0x80;
    buf[1]=0x00;
    My_E28_send(buf,2);
}

所以第一个进入待命模式代码就编写好了,以此类推,编写完剩下的初始化命令得到初始化函数

void My_E28_2G4M20S_init() 
{
    //上电校准
    uint8_t standby[] = {0x80, 0x00}; // STDBY_RC
    My_E28_send( standby, 2);
    HAL_Delay(20);

    // 准备指令数组
    uint8_t cmd_type[] = {0x8a, 0x01};                               
    uint8_t cmd_freq[] = {0x86, 0xb8,0x9d,0x89};               
    uint8_t cmd_txp[]  = {0x8e, 0x1f, 0x15};       //-18+             
    uint8_t cmd_base[] = {0x8f, 0x80, 0x00};                         
    uint8_t cmd_mod[]  = {0x8b, 0x70, 0x0a, 0x01};    //示例SPI命令二进制模式以设置SF7,BW 1600,CR 4/5的Lora6调制(如果无线电模块先前配置为Lora6数据包类型)               
    uint8_t cmd_pkt[]  = {0x8c, 0x0c, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00}; 

    // --- 逐条发送,每条必须独立拉高 NSS 并延时 ---

    // 1. SetPacketType
    My_spi_nss_en();
	HAL_Delay(5);
    HAL_SPI_Transmit(&hspi1, cmd_type, 2, 100);
    My_spi_nss_close();
    HAL_Delay(20); // 盲等 5ms

    // 2. SetRfFrequency
    My_spi_nss_en();
	HAL_Delay(5);
    HAL_SPI_Transmit(&hspi1, cmd_freq, 4, 100);
    My_spi_nss_close();
    HAL_Delay(20);

    // 3. SetTxParams
    My_spi_nss_en();
	HAL_Delay(5);
    HAL_SPI_Transmit(&hspi1, cmd_txp, 3, 100);
    My_spi_nss_close();
    HAL_Delay(20);

    // 4. SetBufferBaseAddress
    My_spi_nss_en();
	HAL_Delay(5);
    HAL_SPI_Transmit(&hspi1, cmd_base, 3, 100);
    My_spi_nss_close();
    HAL_Delay(20);

    // 5. SetModulationParams
    My_spi_nss_en();
	HAL_Delay(5);
    HAL_SPI_Transmit(&hspi1, cmd_mod, 4, 100);
    My_spi_nss_close();
    HAL_Delay(20);

    // 6. SetPacketParams
    My_spi_nss_en();
	HAL_Delay(5);
    HAL_SPI_Transmit(&hspi1, cmd_pkt, 8, 100);
    My_spi_nss_close();
    HAL_Delay(20);

    //使能irq寄存器
    My_E28_Enable_Internal_IRQ();

   
}

这里我频繁使用了hal_delay是因为我没有接busy标志位,不方便直接读取芯片是否忙,其实没有接硬件busy标志位也可通过发送getstatus命令读取芯片busy位状态的。另外可以看到我上述代码初始化,除了目录13.4所说的还使能了irq中断寄存器,这个是接下来要看的目录13.4.2发送端设置和操作中所提到的读取中断源,我做初始化的时候一起初始化了。


接下来是发送的驱动,可以将一些不变的配置直接放到初始化驱动中,这样避免太多初始化函数忘记调用。总的来说下面这张图讲的就是写命令来写数据缓冲区,然后发命令让SX1280来读数据缓冲区,最后读一下irq寄存器中的TX中断位有没有为1,为1表示发送完成。


发送驱动代码如下

void My_E28_send_data(uint8_t *buf,uint8_t bufsize)
{     //打开发送模式
    HAL_GPIO_WritePin(RX_EN_GPIO_Port,RX_EN_Pin,0);
    HAL_GPIO_WritePin(TX_EN_GPIO_Port,TX_EN_Pin,1);
    uint8_t cmd_tx[] = {0x83, 0x00, 0x00, 0x00};
    My_E28_standby(); // 1. 先待命,确保状态机干净
   
    My_E28_writebuffer(buf,bufsize);
    My_E28_set_effectbuffer(bufsize);
     // 2. 在发射前,先清空一次 IRQ,确保账本是干净的
    uint8_t clear_irq[] = {0x97, 0xFF, 0xFF};
    My_E28_send(clear_irq, 3);

    My_E28_send(cmd_tx,4);
    HAL_Delay(20); 
     // 4.等待发射完成
    uint16_t irq_stat = 0;
    uint16_t timeout = 2000; // 简单超时防止死循环
    while(timeout--) {
        irq_stat = My_E28_GetIrqStatus_Logic(); // 封装一个读 IRQ 的函数
        if (irq_stat & 0x0001) { // Bit 0 是 TxDone
            break; 
        }
        HAL_Delay(1);
    }

    // 5. 发射完后再次清空 TxDone 标志,并回到待机或接收模式
    My_E28_send(clear_irq, 3);
	irq_stat = My_E28_GetIrqStatus_Logic();
    My_E28_standby();   
}

void My_E28_writebuffer(uint8_t *buf,uint8_t bufsize)//写入缓冲区
{
    uint8_t op_code = 0x1A;//检查写入缓冲区命令对不对
    uint8_t offset = 0x80;

    My_spi_nss_en();
	HAL_Delay(5);
    HAL_SPI_Transmit(&hspi1, &op_code, 1, 100); // 发指令
    HAL_SPI_Transmit(&hspi1, &offset, 1, 100);  // 发偏移量
    HAL_SPI_Transmit(&hspi1, buf, bufsize, 100); // 连续发数据,中途不准拉高NSS
    My_spi_nss_close();
    HAL_Delay(5);
}

void My_E28_set_effectbuffer(uint8_t buf_size)//告诉lora模块有效的缓冲区
{
    uint8_t update_pkt[] = {0x8C, 0x0C, 0x00, buf_size, 0x00, 0x40, 0x00, 0x00};
    My_E28_send(update_pkt,8);
}

uint16_t My_E28_GetIrqStatus_Logic() {
    // 0x15 是 GetIrqStatus 的指令码
    // 我们需要 4 个字节:1个指令 + 1个虚拟字节 + 2个字节接数据
    uint8_t tx_buf[4] = {0x15, 0x00, 0x00, 0x00};//91p
    uint8_t rx_buf[4] = {0};

    My_spi_nss_en();
	HAL_Delay(5);
    // 使用全双工交换数据
    HAL_SPI_TransmitReceive(&hspi1, tx_buf, rx_buf, 4, 100);
    My_spi_nss_close();

    // 根据我们之前的讨论:
    // rx_buf[0] 和 [1] 是状态字节 (Status)
    // rx_buf[2] 是 IRQ 高 8 位
    // rx_buf[3] 是 IRQ 低 8 位
    uint16_t irq_val = (uint16_t)(rx_buf[2] << 8) | rx_buf[3];
    
    return irq_val;
}

结尾

文章到此结束,总的来说就是找到数据手册重要的命令和流程部分就能适用于编写所有类似的不算太难的芯片驱动了,另外关于SX1280数据手册提醒大家官方手册中所写的命令和地址对应错误,此问题在我实现收发功能时发现,tx命令为0x80时设置的tx缓冲区基地址就是为0x80,并非注释所说0x00,rx设置0x00也就是0x00基地址。如果不放心的话可以使用第86页的getrxbufferstatus命令验证rx的基地址。


GITHUB

整个收发功能设计都已放入git,欢迎访问

https://github.com/10ATRI01/lcd_newmx

https://github.com/10ATRI01/sx1281_sx1280_lora_mode_receiver

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐