esp-idf/examples/peripherals/timer_group/wiegand_interface/main/wiegand_io.c

167 lines
5.6 KiB
C

/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "esp_check.h"
#include "esp_attr.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "wiegand_io.h"
static const char *TAG = "wiegand_io";
typedef struct wiegand_interface_t {
SemaphoreHandle_t sem;
gptimer_handle_t interval_timer;
gptimer_handle_t pulse_timer;
gpio_num_t data_pin0;
gpio_num_t data_pin1;
uint8_t pulse_width_us;
uint16_t interval_width_us;
uint64_t bit_mask;
uint64_t *current_data;
} wiegand_interface_t;
static bool IRAM_ATTR interval_timer_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
{
wiegand_io_handle_t wiegand_io = (wiegand_io_handle_t)user_data;
if (wiegand_io->bit_mask == 1) {
gptimer_stop(timer);
}
gptimer_set_raw_count(wiegand_io->pulse_timer, 0);
gptimer_start(wiegand_io->pulse_timer);
if (*wiegand_io->current_data & wiegand_io->bit_mask) {
gpio_set_level(wiegand_io->data_pin1, 0);
} else {
gpio_set_level(wiegand_io->data_pin0, 0);
}
return false;
}
static bool IRAM_ATTR pulse_timer_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
{
BaseType_t task_woken = pdFALSE;
wiegand_io_handle_t wiegand_io = (wiegand_io_handle_t)user_data;
gpio_set_level(wiegand_io->data_pin0, 1);
gpio_set_level(wiegand_io->data_pin1, 1);
gptimer_stop(timer);
if (!(wiegand_io->bit_mask >>= 1)) {
xSemaphoreGiveFromISR(wiegand_io->sem, &task_woken);
}
return task_woken == pdTRUE;
}
static esp_err_t set_alarm_action(gptimer_handle_t timer, uint64_t count, bool reload)
{
gptimer_alarm_config_t alarm_config = {
.reload_count = 0,
.alarm_count = count,
.flags.auto_reload_on_alarm = reload,
};
ESP_RETURN_ON_ERROR(gptimer_set_alarm_action(timer, &alarm_config), TAG, "set alarm action failed!");
return ESP_OK;
}
static esp_err_t setup_timer(gptimer_handle_t *timer, gptimer_alarm_cb_t alarm_cb, wiegand_io_handle_t wiegand_io, uint64_t count, bool reload)
{
esp_err_t ret = ESP_OK;
gptimer_config_t timer_config = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 1000000, // 1 tick = 1us
};
ESP_RETURN_ON_ERROR(gptimer_new_timer(&timer_config, timer), TAG, "add new timer failed!");
gptimer_event_callbacks_t cbs = {
.on_alarm = alarm_cb,
};
ESP_GOTO_ON_ERROR(gptimer_register_event_callbacks(*timer, &cbs, wiegand_io), err, TAG, "register callbacks failed!");
ESP_GOTO_ON_ERROR(set_alarm_action(*timer, count, reload), err, TAG, "set alarm action failed!");
ESP_GOTO_ON_ERROR(gptimer_enable(*timer), err, TAG, "enable timer failed!");
return ESP_OK;
err:
if (*timer) {
gptimer_del_timer(*timer);
}
return ret;
}
esp_err_t wiegand_new_io(const wiegand_io_config_t *config, wiegand_io_handle_t *ret_handle)
{
esp_err_t ret = ESP_OK;
wiegand_interface_t *wiegand_io = NULL;
ESP_RETURN_ON_FALSE(config && ret_handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
wiegand_io = heap_caps_calloc(1, sizeof(wiegand_interface_t), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
ESP_RETURN_ON_FALSE(wiegand_io, ESP_ERR_NO_MEM, TAG, "no mem for wiegand_io");
wiegand_io->sem = xSemaphoreCreateBinaryWithCaps(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
wiegand_io->data_pin0 = config->data_pin0;
wiegand_io->data_pin1 = config->data_pin1;
wiegand_io->pulse_width_us = config->pulse_width_us;
wiegand_io->interval_width_us = config->interval_width_us;
gpio_config_t io_conf = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = (1ULL << wiegand_io->data_pin0) | (1ULL << wiegand_io->data_pin1),
};
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "gpio config failed!");
// data pins are high by default
gpio_set_level(wiegand_io->data_pin0, 1);
gpio_set_level(wiegand_io->data_pin1, 1);
// Setup the pulse timer
ESP_GOTO_ON_ERROR(setup_timer(&wiegand_io->pulse_timer, pulse_timer_cb, wiegand_io, wiegand_io->pulse_width_us, false), err, TAG, "setup pulse timer failed!");
// Setup the interval timer
ESP_GOTO_ON_ERROR(setup_timer(&wiegand_io->interval_timer, interval_timer_cb, wiegand_io, wiegand_io->interval_width_us, true), err, TAG, "setup interval timer failed!");
// return handle
*ret_handle = wiegand_io;
xSemaphoreGive(wiegand_io->sem);
return ESP_OK;
err:
if (wiegand_io->pulse_timer) {
gptimer_disable(wiegand_io->pulse_timer);
gptimer_del_timer(wiegand_io->pulse_timer);
}
if (wiegand_io->interval_timer) {
gptimer_disable(wiegand_io->interval_timer);
gptimer_del_timer(wiegand_io->interval_timer);
}
if (wiegand_io) {
free(wiegand_io);
}
return ret;
}
esp_err_t wiegand_io_send(wiegand_io_handle_t wiegand_io, void *data, uint8_t num_of_bits)
{
ESP_RETURN_ON_FALSE(wiegand_io && num_of_bits && data, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(num_of_bits > 0 && num_of_bits <= 64, ESP_ERR_INVALID_ARG, TAG, "bits is out of range [1,64]");
xSemaphoreTake(wiegand_io->sem, portMAX_DELAY);
wiegand_io->current_data = (uint64_t *)data;
wiegand_io->bit_mask = 1ULL << (num_of_bits - 1);
ESP_RETURN_ON_ERROR(gptimer_set_raw_count(wiegand_io->interval_timer, 0), TAG, "set timer raw count failed!");
ESP_RETURN_ON_ERROR(gptimer_start(wiegand_io->interval_timer), TAG, "start timer failed!");
return ESP_OK;
}