This commit is contained in:
2025-09-11 09:51:32 +03:00
parent 458224bd63
commit 0aab7d9101
3 changed files with 64 additions and 16 deletions

View File

@@ -4,11 +4,6 @@
1. Support some encoders on one device.
## Note
1. Encoder pins must be pull up to the VCC via 0.1 µf capacitors.
2. Only PORTD0 - PORTD7 are acceptable!
## Using
In an existing project, run the following command to install the components:
@@ -58,6 +53,7 @@ int main(void)
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
stdout = &uart;
zh_avr_encoder_init_config_t encoder_init_config = ZH_AVR_ENCODER_INIT_CONFIG_DEFAULT();
encoder_init_config.gpio_port = AVR_PORTD;
encoder_init_config.a_gpio_number = PORTD5;
encoder_init_config.b_gpio_number = PORTD6;
encoder_init_config.pullup=true;
@@ -81,8 +77,13 @@ void zh_avr_encoder_event_handler(zh_avr_encoder_event_on_isr_t *event) // Do no
printf("Interrupt Task Remaining Stack Size %d.\n", uxTaskGetStackHighWaterMark(NULL));
}
ISR(PCINT2_vect) // Do not delete!
// ISR(PCINT0_vect) // For AVR_PORTB.
// ISR(PCINT1_vect) // For AVR_PORTC.
ISR(PCINT2_vect) // For AVR_PORTD.
{
zh_avr_encoder_isr_handler(&encoder_handle);
if (zh_avr_encoder_isr_handler(&encoder_handle) == pdTRUE)
{
portYIELD();
}
}
```

View File

@@ -3,6 +3,7 @@
#include "FreeRTOS.h"
#include "semphr.h"
#include "avr_err.h"
#include "avr_port.h"
#include "stdbool.h"
#include "avr/interrupt.h"
#include "avr/pgmspace.h"
@@ -12,6 +13,7 @@
.task_priority = configMAX_PRIORITIES, \
.stack_size = 124, \
.queue_size = 1, \
.gpio_port = 0, \
.a_gpio_number = 0, \
.b_gpio_number = 0, \
.pullup = false, \
@@ -30,6 +32,7 @@ extern "C"
uint8_t task_priority; // Task priority for the encoder isr processing. @note It is not recommended to set a value less than configMAX_PRIORITIES.
uint16_t stack_size; // Stack size for task for the encoder isr processing processing. @note The minimum size is 124 bytes.
uint8_t queue_size; // Queue size for task for the encoder processing. Depends on the number of encoders.
uint8_t gpio_port; // Encoder GPIO port. @note Must be same for A and B GPIO.
uint8_t a_gpio_number; // Encoder A GPIO number.
uint8_t b_gpio_number; // Encoder B GPIO number.
bool pullup; // Using internal pullup resistors.
@@ -41,6 +44,7 @@ extern "C"
typedef struct // Encoder handle.
{
uint8_t gpio_port; // Encoder GPIO port.
uint8_t a_gpio_number; // Encoder A GPIO number.
uint8_t b_gpio_number; // Encoder B GPIO number.
int32_t encoder_min_value; // Encoder min value.

View File

@@ -12,6 +12,7 @@ static const uint8_t _encoder_matrix[7][4] PROGMEM = {
{0x03, 0x05, 0x03, 0x00},
};
TaskHandle_t zh_avr_encoder = NULL;
static QueueHandle_t _queue_handle = NULL;
static bool _is_initialized = false;
@@ -28,6 +29,7 @@ avr_err_t zh_avr_encoder_init(const zh_avr_encoder_init_config_t *config, zh_avr
handle->encoder_max_value = config->encoder_max_value;
handle->encoder_step = config->encoder_step;
handle->encoder_position = (handle->encoder_min_value + handle->encoder_max_value) / 2;
handle->gpio_port = config->gpio_port;
handle->a_gpio_number = config->a_gpio_number;
handle->b_gpio_number = config->b_gpio_number;
err = _zh_avr_encoder_configure_interrupts(config, handle);
@@ -66,26 +68,53 @@ static avr_err_t _zh_avr_encoder_validate_config(const zh_avr_encoder_init_confi
ZH_ERROR_CHECK(config->queue_size > 0, AVR_ERR_INVALID_ARG);
ZH_ERROR_CHECK(config->encoder_max_value > config->encoder_min_value, AVR_ERR_INVALID_ARG);
ZH_ERROR_CHECK(config->encoder_step > 0, AVR_ERR_INVALID_ARG);
ZH_ERROR_CHECK(config->a_gpio_number >= PORTD0 && config->a_gpio_number <= PORTD7, AVR_ERR_INVALID_ARG);
ZH_ERROR_CHECK(config->b_gpio_number >= PORTD0 && config->b_gpio_number <= PORTD7, AVR_ERR_INVALID_ARG);
ZH_ERROR_CHECK(config->gpio_port >= AVR_PORTB && config->gpio_port <= AVR_PORTD, AVR_ERR_INVALID_ARG);
ZH_ERROR_CHECK(config->a_gpio_number >= 0 && config->a_gpio_number <= 7, AVR_ERR_INVALID_ARG);
ZH_ERROR_CHECK(config->b_gpio_number >= 0 && config->b_gpio_number <= 7, AVR_ERR_INVALID_ARG);
ZH_ERROR_CHECK(config->a_gpio_number != config->b_gpio_number, AVR_ERR_INVALID_ARG);
return AVR_OK;
}
static avr_err_t _zh_avr_encoder_configure_interrupts(const zh_avr_encoder_init_config_t *config, zh_avr_encoder_handle_t *handle)
{
DDRD &= ~((1 << config->a_gpio_number) | (1 << config->b_gpio_number));
if (config->pullup == true)
switch (config->gpio_port)
{
PORTD |= ((1 << config->a_gpio_number) | (1 << config->b_gpio_number));
case AVR_PORTB:
DDRB &= ~((1 << config->a_gpio_number) | (1 << config->b_gpio_number));
if (config->pullup == true)
{
PORTB |= ((1 << config->a_gpio_number) | (1 << config->b_gpio_number));
}
PCICR |= (1 << PCIE0);
PCMSK0 |= ((1 << config->a_gpio_number) | (1 << config->b_gpio_number));
break;
case AVR_PORTC:
DDRC &= ~((1 << config->a_gpio_number) | (1 << config->b_gpio_number));
if (config->pullup == true)
{
PORTC |= ((1 << config->a_gpio_number) | (1 << config->b_gpio_number));
}
PCICR |= (1 << PCIE1);
PCMSK1 |= ((1 << config->a_gpio_number) | (1 << config->b_gpio_number));
break;
case AVR_PORTD:
DDRD &= ~((1 << config->a_gpio_number) | (1 << config->b_gpio_number));
if (config->pullup == true)
{
PORTD |= ((1 << config->a_gpio_number) | (1 << config->b_gpio_number));
}
PCICR |= (1 << PCIE2);
PCMSK2 |= ((1 << config->a_gpio_number) | (1 << config->b_gpio_number));
break;
default:
return AVR_ERR_INVALID_ARG;
break;
}
PCICR |= (1 << PCIE2);
PCMSK2 |= ((1 << config->a_gpio_number) | (1 << config->b_gpio_number));
if (_is_initialized == false)
{
_queue_handle = xQueueCreate(config->queue_size, sizeof(zh_avr_encoder_handle_t));
ZH_ERROR_CHECK(_queue_handle != NULL, AVR_ERR_NO_MEM);
BaseType_t x_err = xTaskCreate(_zh_avr_encoder_isr_processing_task, NULL, config->stack_size, NULL, config->task_priority, NULL);
BaseType_t x_err = xTaskCreate(_zh_avr_encoder_isr_processing_task, "zh_avr_encoder", config->stack_size, NULL, config->task_priority, &zh_avr_encoder);
if (x_err != pdPASS)
{
vQueueDelete(_queue_handle);
@@ -98,7 +127,21 @@ static avr_err_t _zh_avr_encoder_configure_interrupts(const zh_avr_encoder_init_
BaseType_t zh_avr_encoder_isr_handler(zh_avr_encoder_handle_t *handle)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
uint8_t temp = pgm_read_byte(&_encoder_matrix[handle->encoder_state & 0x0F][(((PIND & (1 << handle->b_gpio_number)) == 0 ? 0 : 1) << 1) | ((PIND & (1 << handle->a_gpio_number)) == 0 ? 0 : 1)]);
uint8_t temp = 0;
switch (handle->gpio_port)
{
case AVR_PORTB:
temp = pgm_read_byte(&_encoder_matrix[handle->encoder_state & 0x0F][(((PINB & (1 << handle->b_gpio_number)) == 0 ? 0 : 1) << 1) | ((PINB & (1 << handle->a_gpio_number)) == 0 ? 0 : 1)]);
break;
case AVR_PORTC:
temp = pgm_read_byte(&_encoder_matrix[handle->encoder_state & 0x0F][(((PINC & (1 << handle->b_gpio_number)) == 0 ? 0 : 1) << 1) | ((PINC & (1 << handle->a_gpio_number)) == 0 ? 0 : 1)]);
break;
case AVR_PORTD:
temp = pgm_read_byte(&_encoder_matrix[handle->encoder_state & 0x0F][(((PIND & (1 << handle->b_gpio_number)) == 0 ? 0 : 1) << 1) | ((PIND & (1 << handle->a_gpio_number)) == 0 ? 0 : 1)]);
break;
default:
break;
}
if (temp != handle->encoder_state)
{
handle->encoder_state = temp;