circuitpython/ports/espressif/common-hal/paralleldisplay/ParallelBus.c

192 lines
7.0 KiB
C

/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Lucian Copeland for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdint.h>
#include <string.h>
#include "shared-bindings/paralleldisplay/ParallelBus.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "shared-bindings/digitalio/DigitalInOut.h"
#include "shared-bindings/microcontroller/__init__.h"
#include "common-hal/audiobusio/__init__.h"
#include "common-hal/microcontroller/Pin.h"
#include "py/runtime.h"
#include "i2s_lcd_driver.h"
#include "driver/gpio.h"
/*
* Current pin limitations for ESP32-S2 ParallelBus:
* - data0 pin must be byte aligned
*/
void common_hal_paralleldisplay_parallelbus_construct_nonsequential(paralleldisplay_parallelbus_obj_t *self,
uint8_t n_pins, mcu_pin_obj_t **data_pins,
const mcu_pin_obj_t *command, const mcu_pin_obj_t *chip_select,
const mcu_pin_obj_t *write, const mcu_pin_obj_t *read, const mcu_pin_obj_t *reset, uint32_t frequency) {
if (n_pins != 8 && n_pins != 16) {
mp_raise_ValueError_varg(translate("Number of data_pins must be 8 or 16, not %d"), n_pins);
}
for (uint8_t i = 0; i < n_pins; i++) {
if (!common_hal_mcu_pin_is_free(data_pins[i])) {
mp_raise_ValueError_varg(translate("Bus pin %d is already in use"), i);
}
}
// This will throw if unsuccessful. Everything following is guaranteed to succeed.
port_i2s_allocate_i2s0();
i2s_lcd_config_t config = {
.data_width = n_pins,
.pin_num_cs = common_hal_mcu_pin_number(chip_select),
.pin_num_wr = common_hal_mcu_pin_number(write), // write strobe
.pin_num_rs = common_hal_mcu_pin_number(command), // The "register select" pin is called "command" by CircuitPython
.clk_freq = frequency,
.i2s_port = 0,
.swap_data = false,
.buffer_size = 512,
};
if (reset != NULL) {
common_hal_never_reset_pin(reset);
self->reset_pin_number = reset->number;
} else {
self->reset_pin_number = NO_PIN;
}
for (uint8_t i = 0; i < n_pins; i++) {
common_hal_never_reset_pin(data_pins[i]);
config.pin_data_num[i] = common_hal_mcu_pin_number(data_pins[i]);
}
if (read != NULL) {
common_hal_never_reset_pin(read);
gpio_config_t read_config = {
.pin_bit_mask = 1ull << read->number,
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
gpio_config(&read_config);
self->read_pin_number = read->number;
gpio_set_level(read->number, true);
}
common_hal_never_reset_pin(chip_select);
common_hal_never_reset_pin(command);
common_hal_never_reset_pin(read);
common_hal_never_reset_pin(reset);
common_hal_never_reset_pin(write);
self->config = config;
self->handle = i2s_lcd_driver_init(&config);
if (!self->handle) {
port_i2s_reset_instance(0);
mp_raise_RuntimeError(translate("Internal error"));
}
}
void common_hal_paralleldisplay_parallelbus_construct(paralleldisplay_parallelbus_obj_t *self,
const mcu_pin_obj_t *data0, const mcu_pin_obj_t *command, const mcu_pin_obj_t *chip_select,
const mcu_pin_obj_t *write, const mcu_pin_obj_t *read, const mcu_pin_obj_t *reset, uint32_t frequency) {
char buf[7];
mcu_pin_obj_t *data_pins[8];
for (int i = 0; i < 8; i++) {
snprintf(buf, sizeof(buf), "GPIO%d", data0->number + i);
data_pins[i] = validate_obj_is_free_pin(mp_obj_dict_get(MP_OBJ_FROM_PTR(&mcu_pin_globals), mp_obj_new_str(buf, strlen(buf))));
}
common_hal_paralleldisplay_parallelbus_construct_nonsequential(self, 8, data_pins, command, chip_select, write, read, reset, frequency);
}
void common_hal_paralleldisplay_parallelbus_deinit(paralleldisplay_parallelbus_obj_t *self) {
if (!self->handle) {
return;
}
i2s_lcd_driver_deinit(self->handle);
self->handle = NULL;
/* SNIP - same as from SAMD and NRF ports */
for (uint8_t i = 0; i < self->config.data_width; i++) {
reset_pin_number(self->config.pin_data_num[i]);
}
reset_pin_number(self->config.pin_num_cs);
reset_pin_number(self->config.pin_num_wr);
reset_pin_number(self->read_pin_number);
reset_pin_number(self->config.pin_num_rs);
reset_pin_number(self->reset_pin_number);
port_i2s_reset_instance(0);
}
bool common_hal_paralleldisplay_parallelbus_reset(mp_obj_t obj) {
/* SNIP - same as from SAMD and NRF ports */
paralleldisplay_parallelbus_obj_t *self = MP_OBJ_TO_PTR(obj);
if (self->reset_pin_number == NO_PIN) {
return false;
}
gpio_set_level(self->reset_pin_number, false);
common_hal_mcu_delay_us(4);
gpio_set_level(self->reset_pin_number, true);
return true;
}
bool common_hal_paralleldisplay_parallelbus_bus_free(mp_obj_t obj) {
/* SNIP - same as from SAMD and NRF ports */
return true;
}
bool common_hal_paralleldisplay_parallelbus_begin_transaction(mp_obj_t obj) {
paralleldisplay_parallelbus_obj_t *self = MP_OBJ_TO_PTR(obj);
bool r = i2s_lcd_acquire_nonblocking(self->handle, 1);
if (r) {
gpio_set_level(self->config.pin_num_cs, false);
}
return r;
}
void common_hal_paralleldisplay_parallelbus_send(mp_obj_t obj, display_byte_type_t byte_type,
display_chip_select_behavior_t chip_select, const uint8_t *data, uint32_t data_length) {
paralleldisplay_parallelbus_obj_t *self = MP_OBJ_TO_PTR(obj);
if (data_length) {
gpio_set_level(self->config.pin_num_rs, byte_type == DISPLAY_DATA);
i2s_lcd_write(self->handle, data, data_length);
}
}
void common_hal_paralleldisplay_parallelbus_end_transaction(mp_obj_t obj) {
paralleldisplay_parallelbus_obj_t *self = MP_OBJ_TO_PTR(obj);
i2s_lcd_release(self->handle);
gpio_set_level(self->config.pin_num_cs, true);
}