#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_timer.h"
#include "esp_lcd_panel_io_i80_hub75.h"
#include "esp_lcd_panel_ops.h"
#include "driver/gpio.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_rom_sys.h"
#include "hal/ledc_hal.h"
#include "driver/ledc.h"


static const char *TAG = "led matrix";

#define HUB75_CLOCK_HZ     (10 * 1000 * 1000)
#define HUB75_MAX_SCAN_TIMES   (8)     /* 扫描次数是8次,也就是每一行会被扫8次,用于全彩显示 */
#define HUB75_MAX_SCAN_LINE     (8)     /* 最多扫描行 */
#define HUB75_MAX_TRANSFER_DATA_NUM (128)   /* 最多发送128个uint16数据 */
#define HUB75_MAX_TRANSFER_BUTES    (HUB75_MAX_TRANSFER_DATA_NUM * 2)

#define HUB75_LINE_ADDR_A_PIN_NUM         33 
#define HUB75_LINE_ADDR_B_PIN_NUM         34
#define HUB75_LINE_ADDR_C_PIN_NUM         35
#define HUB75_LINE_ADDR_D_PIN_NUM         36
#define HUB75_LAT_PIN_NUM                 8
#define HUB75_OE_PIN_NUM                  21
#define HUB75_CLK_PIN_NUM                 7

#define HUB75_OE_CAPTRUE_PIN_NUM          18

#define HUB75_G0    1
#define HUB75_R0    2
#define HUB75_B0    3
#define HUB75_G1    4
#define HUB75_R1    5
#define HUB75_B1    6
#define HUB75_G2    37
#define HUB75_R2    38
#define HUB75_B2    39
#define HUB75_G3    40
#define HUB75_R3    41
#define HUB75_B3    42

#define PSRAM_DATA_ALIGNMENT        64

typedef unsigned char uint8_t;
typedef unsigned short int  uint16_t;

esp_lcd_i80_hub75_bus_handle_t i80_bus = NULL;
esp_lcd_panel_io_handle_t io_handle = NULL;
void led_matrix_i80_hub75_bus_init(void)
{
    /* 8080总线配置 */
    ESP_LOGI(TAG, "Initialize Intel 8080 bus");
    esp_lcd_i80_hub75_bus_config_t bus_config = {
        .clk_src = HUB75_CLK_PIN_NUM,
        .wr_gpio_num = HUB75_CLK_PIN_NUM,
        .data_gpio_nums = {
            HUB75_R0,
            HUB75_G0,
            HUB75_B0,
            HUB75_R1,
            HUB75_G1,
            HUB75_B1,
            HUB75_R2,
            HUB75_G2,
            HUB75_B2,
            HUB75_R3,
            HUB75_G3,
            HUB75_B3,
            -1,
            -1,
            -1,
            -1,
        },
        .bus_width = 16,
        .max_transfer_bytes = HUB75_MAX_TRANSFER_BUTES,   /* 每次发一行 */
        .max_scan_line = HUB75_MAX_SCAN_LINE,
        .max_scan_times = HUB75_MAX_SCAN_TIMES,
        .psram_trans_align = PSRAM_DATA_ALIGNMENT,
        .sram_trans_align = 4,
    };
    ESP_ERROR_CHECK(esp_lcd_new_i80_hub75_bus(&bus_config, &i80_bus));

    /* 8080IO设备申请 */
    esp_lcd_panel_io_i80_hub75_config_t io_config = {
        .cs_gpio_num = -1,
        .pclk_hz = HUB75_CLOCK_HZ,
        .trans_queue_depth = 10,
        .flags = {
            .pclk_idle_low = 1,               
        },
        .on_color_trans_done = NULL,
        .user_ctx = NULL,
        .lcd_cmd_bits = 0,
        .lcd_param_bits = 0,
    };
    ESP_ERROR_CHECK(esp_lcd_new_panel_io_i80_hub75(i80_bus, &io_config, &io_handle));
}

void led_matrix_addr_gpio_init(void)
{
    gpio_config_t io_conf = {
        .mode = GPIO_MODE_OUTPUT,
        .pin_bit_mask = (1ULL << HUB75_LAT_PIN_NUM)  |
                        (1ULL << HUB75_OE_PIN_NUM)  |
                        (1ULL << HUB75_LINE_ADDR_A_PIN_NUM)  |
                        (1ULL << HUB75_LINE_ADDR_B_PIN_NUM)  |
                        (1ULL << HUB75_LINE_ADDR_C_PIN_NUM)  |
                        (1ULL << HUB75_LINE_ADDR_D_PIN_NUM),
    };
    ESP_ERROR_CHECK(gpio_config(&io_conf));

     gpio_config_t io_conf1 = {
        .mode = GPIO_MODE_INPUT,
        .pin_bit_mask = (1ULL << HUB75_OE_CAPTRUE_PIN_NUM),
    };
    ESP_ERROR_CHECK(gpio_config(&io_conf1));
}


#define LEDC_MODE       LEDC_LOW_SPEED_MODE
#define LEDC_TIMER      LEDC_TIMER_0
#define LEDC_DUTY_RES   LEDC_TIMER_11_BIT 
#define LEDC_FREQUENCY  (480)      /* 3840 = 60 * 8 * 8 */

#define LEDC_DUTY_OE    (100)      /* 先低后高,前面有效,控制亮度 */       
#define LEDC_DUTY_LAT   (2048 - 5)  /* 高电平锁存 */   

static void IRAM_ATTR ledc_timer_ovf_isr(void *arg);
extern esp_err_t ledc_set_timer_ovf_intr(ledc_mode_t speed_mode, ledc_channel_t channel, int enbale);
extern void ledc_clear_timer_ovf_intr_status(ledc_timer_t timer_num);
static ledc_isr_handle_t s_ledc_time_isr_handle = NULL;

esp_err_t led_matrix_oe_lat_ledc_init(void)
{
    ESP_LOGI(TAG, "led_matrix_oe_lat_ledc_init");

    ledc_timer_config_t ledc_timer = {
        .speed_mode = LEDC_MODE,
        .timer_num = LEDC_TIMER,
        .duty_resolution = LEDC_DUTY_RES,
        .freq_hz = LEDC_FREQUENCY, 
        .clk_cfg = LEDC_AUTO_CLK};
    ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));

    ledc_channel_config_t ledc_channel_oe = {
        .speed_mode = LEDC_MODE,
        .channel = LEDC_CHANNEL_0,
        .timer_sel = LEDC_TIMER,
        .intr_type = LEDC_INTR_DISABLE,
        .gpio_num = HUB75_OE_PIN_NUM,
        .duty = LEDC_DUTY_OE, 
        .flags.output_invert = 1,
        .hpoint = 0};
    ledc_channel_config_t ledc_channel_lat = {
        .speed_mode = LEDC_MODE,
        .channel = LEDC_CHANNEL_1,
        .timer_sel = LEDC_TIMER,
        .intr_type = LEDC_INTR_DISABLE,
        .gpio_num = HUB75_LAT_PIN_NUM,
        .duty = LEDC_DUTY_LAT, 
        .flags.output_invert = 1,
        .hpoint = 0};
    ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel_oe));
    ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel_lat));

    /* 中断配置 */
    ledc_set_timer_ovf_intr(LEDC_MODE, LEDC_TIMER, false);
    int isr_flags = ESP_INTR_FLAG_LEVEL2;
    int ret = ledc_isr_register(ledc_timer_ovf_isr, i80_bus, isr_flags, &s_ledc_time_isr_handle);
    ESP_GOTO_ON_ERROR(ret, err, TAG, "install interrupt failed");
    ledc_set_timer_ovf_intr(LEDC_MODE, LEDC_TIMER, true);

    return ESP_OK;
err:
    return ESP_FAIL;
}

static int line = 0;            /* 本次扫描行,32行16扫,一次显示两行 */
static void IRAM_ATTR ledc_timer_ovf_isr(void *arg)
{
    ledc_clear_timer_ovf_intr_status(LEDC_TIMER_0);
    gpio_set_level(HUB75_LINE_ADDR_A_PIN_NUM, line & 1);        /* 行地址,A是低位(待求证) */
    gpio_set_level(HUB75_LINE_ADDR_B_PIN_NUM, (line >> 1) & 1);
    gpio_set_level(HUB75_LINE_ADDR_C_PIN_NUM, (line >> 2) & 1);
    gpio_set_level(HUB75_LINE_ADDR_D_PIN_NUM, (line >> 3) & 1);
    line ++;
    if (line == 8) line = 0;
    hub75_send_line(io_handle, 0, line);
}

static void hub75_addr_task(void *arg)
{
    ESP_LOGI(TAG, "hub75_addr_task");
    int oe = 0;
    while (1)
    {
        oe = gpio_get_level(HUB75_OE_CAPTRUE_PIN_NUM);
        if (oe == 1)
        {
         //   ESP_LOGI(TAG, "oe = 1");
            gpio_set_level(HUB75_LINE_ADDR_A_PIN_NUM, line & 1);        /* 行地址,A是低位(待求证) */
            gpio_set_level(HUB75_LINE_ADDR_B_PIN_NUM, (line >> 1) & 1);
            gpio_set_level(HUB75_LINE_ADDR_C_PIN_NUM, (line >> 2) & 1);
            gpio_set_level(HUB75_LINE_ADDR_D_PIN_NUM, (line >> 3) & 1);
        }
    }
}


void app_main(void)
{
    led_matrix_i80_hub75_bus_init();
    led_matrix_addr_gpio_init();

    test_init_buf(i80_bus);
    led_matrix_oe_lat_ledc_init();

 //  xTaskCreatePinnedToCore(hub75_addr_task, "hub75_addr", 4096, NULL, 9, NULL, tskNO_AFFINITY);

    while (1) {
         vTaskDelay(pdMS_TO_TICKS(1000));
    }

}