Initial import
This commit is contained in:
129
drivers/lora/lr11xx/lr11xx_hal.c
Normal file
129
drivers/lora/lr11xx/lr11xx_hal.c
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* LR11xx Zephyr HAL — SPI and GPIO low-level operations.
|
||||
*
|
||||
* The LR11xx SPI protocol:
|
||||
* WRITE: Assert NSS → send 2-byte opcode + param bytes → deassert NSS
|
||||
* READ: Assert NSS → send 2-byte opcode → deassert NSS
|
||||
* Wait for BUSY low
|
||||
* Assert NSS → read STAT1 + STAT2 + N response bytes → deassert NSS
|
||||
*
|
||||
* All transactions must wait for BUSY=low before asserting NSS.
|
||||
*/
|
||||
|
||||
#include "lr11xx.h"
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/spi.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(lr11xx, LOG_LEVEL_DBG);
|
||||
|
||||
#define BUSY_POLL_INTERVAL_US 100
|
||||
#define BUSY_TIMEOUT_MS 1000
|
||||
|
||||
int lr11xx_hal_reset(const struct device *dev)
|
||||
{
|
||||
const struct lr11xx_config *cfg = dev->config;
|
||||
int ret;
|
||||
|
||||
ret = gpio_pin_set_dt(&cfg->reset, 1);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
k_sleep(K_USEC(200));
|
||||
ret = gpio_pin_set_dt(&cfg->reset, 0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
k_sleep(K_MSEC(10));
|
||||
|
||||
return lr11xx_hal_wait_busy(dev, K_MSEC(BUSY_TIMEOUT_MS));
|
||||
}
|
||||
|
||||
int lr11xx_hal_wait_busy(const struct device *dev, k_timeout_t timeout)
|
||||
{
|
||||
const struct lr11xx_config *cfg = dev->config;
|
||||
uint32_t start = k_uptime_get_32();
|
||||
uint32_t timeout_ms = k_ticks_to_ms_floor32(timeout.ticks);
|
||||
|
||||
while (gpio_pin_get_dt(&cfg->busy)) {
|
||||
if (timeout_ms > 0 &&
|
||||
(k_uptime_get_32() - start) > timeout_ms) {
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
k_busy_wait(BUSY_POLL_INTERVAL_US);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lr11xx_hal_write_cmd(const struct device *dev,
|
||||
uint16_t opcode,
|
||||
const uint8_t *params, size_t params_len)
|
||||
{
|
||||
const struct lr11xx_config *cfg = dev->config;
|
||||
int ret;
|
||||
|
||||
ret = lr11xx_hal_wait_busy(dev, K_MSEC(BUSY_TIMEOUT_MS));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t opcode_buf[2] = {
|
||||
(uint8_t)(opcode >> 8),
|
||||
(uint8_t)(opcode & 0xFF),
|
||||
};
|
||||
|
||||
const struct spi_buf tx_bufs[] = {
|
||||
{ .buf = opcode_buf, .len = sizeof(opcode_buf) },
|
||||
{ .buf = (void *)params, .len = params_len },
|
||||
};
|
||||
const struct spi_buf_set tx = {
|
||||
.buffers = tx_bufs,
|
||||
.count = (params_len > 0) ? 2 : 1,
|
||||
};
|
||||
|
||||
return spi_write_dt(&cfg->spi, &tx);
|
||||
}
|
||||
|
||||
int lr11xx_hal_read_resp(const struct device *dev,
|
||||
uint8_t *buf, size_t len)
|
||||
{
|
||||
const struct lr11xx_config *cfg = dev->config;
|
||||
int ret;
|
||||
|
||||
ret = lr11xx_hal_wait_busy(dev, K_MSEC(BUSY_TIMEOUT_MS));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* First two bytes are STAT1 and STAT2 — discard for now */
|
||||
uint8_t status[2];
|
||||
uint8_t dummy_opcode[2] = {0x00, 0x00};
|
||||
|
||||
const struct spi_buf tx_bufs[] = {
|
||||
{ .buf = dummy_opcode, .len = sizeof(dummy_opcode) },
|
||||
};
|
||||
struct spi_buf rx_bufs[] = {
|
||||
{ .buf = status, .len = sizeof(status) },
|
||||
{ .buf = buf, .len = len },
|
||||
};
|
||||
const struct spi_buf_set tx = { .buffers = tx_bufs, .count = 1 };
|
||||
const struct spi_buf_set rx = { .buffers = rx_bufs, .count = (len > 0) ? 2 : 1 };
|
||||
|
||||
return spi_transceive_dt(&cfg->spi, &tx, &rx);
|
||||
}
|
||||
|
||||
void lr11xx_hal_event_handler(const struct device *port,
|
||||
struct gpio_callback *cb, uint32_t pins)
|
||||
{
|
||||
ARG_UNUSED(port);
|
||||
ARG_UNUSED(pins);
|
||||
|
||||
struct lr11xx_data *data =
|
||||
CONTAINER_OF(cb, struct lr11xx_data, event_cb);
|
||||
|
||||
k_sem_give(&data->event_sem);
|
||||
}
|
||||
Reference in New Issue
Block a user