mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
167 lines
5.6 KiB
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;
|
||
|
}
|