#include #include #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" #include "soc/rtc_cntl_reg.h" #include "soc/sens_reg.h" #include "soc/rtc_periph.h" #include "driver/rtc_io.h" #include "ulp_riscv.h" #include "ulp_main.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_WIDTH (64) #define HUB75_HEIGHT (32) #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_OE_CAPTRUE__RTC_PIN_NUM GPIO_NUM_18 #define HUB75_LINE_ADDR_A_RTC_PIN_NUM GPIO_NUM_16 #define HUB75_LINE_ADDR_B_RTC_PIN_NUM GPIO_NUM_15 #define HUB75_LINE_ADDR_C_RTC_PIN_NUM GPIO_NUM_14 #define HUB75_LINE_ADDR_D_RTC_PIN_NUM GPIO_NUM_13 #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; static 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, .height = HUB75_HEIGHT, .width = HUB75_WIDTH, .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)); } #define LEDC_MODE LEDC_LOW_SPEED_MODE #define LEDC_TIMER LEDC_TIMER_0 #define LEDC_DUTY_RES LEDC_TIMER_11_BIT #define LEDC_FREQUENCY (60 * HUB75_MAX_SCAN_TIMES * HUB75_MAX_SCAN_LINE) #define LEDC_DUTY_OE (1000) /* 先低后高,前面有效,控制亮度 */ #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; static 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 times = 0; 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); line ++; if (line == HUB75_MAX_SCAN_LINE) { line = 0; times ++; if (times == HUB75_MAX_SCAN_TIMES) times = 0; } /* ULP处理行地址 */ ulp_line = line; // if (ulp_flag != 0) ESP_LOGE(TAG, "ULP not deal line addr"); ulp_flag = 1; hub75_send_line(io_handle, times, line); } extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start"); extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end"); static void led_matrix_ulp_risc_init(void) { ESP_LOGI(TAG, "led_matrix_ulp_risc_init"); rtc_gpio_init(HUB75_OE_CAPTRUE__RTC_PIN_NUM); rtc_gpio_set_direction(HUB75_OE_CAPTRUE__RTC_PIN_NUM, RTC_GPIO_MODE_INPUT_ONLY); rtc_gpio_pulldown_dis(HUB75_OE_CAPTRUE__RTC_PIN_NUM); rtc_gpio_pullup_dis(HUB75_OE_CAPTRUE__RTC_PIN_NUM); rtc_gpio_hold_en(HUB75_OE_CAPTRUE__RTC_PIN_NUM); rtc_gpio_init(HUB75_LINE_ADDR_A_RTC_PIN_NUM); rtc_gpio_set_direction(HUB75_LINE_ADDR_A_RTC_PIN_NUM, RTC_GPIO_MODE_OUTPUT_ONLY); rtc_gpio_init(HUB75_LINE_ADDR_B_RTC_PIN_NUM); rtc_gpio_set_direction(HUB75_LINE_ADDR_B_RTC_PIN_NUM, RTC_GPIO_MODE_OUTPUT_ONLY); rtc_gpio_init(HUB75_LINE_ADDR_C_RTC_PIN_NUM); rtc_gpio_set_direction(HUB75_LINE_ADDR_C_RTC_PIN_NUM, RTC_GPIO_MODE_OUTPUT_ONLY); rtc_gpio_init(HUB75_LINE_ADDR_D_RTC_PIN_NUM); rtc_gpio_set_direction(HUB75_LINE_ADDR_D_RTC_PIN_NUM, RTC_GPIO_MODE_OUTPUT_ONLY); esp_err_t err = ulp_riscv_load_binary(ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start)); ESP_ERROR_CHECK(err); ulp_set_wakeup_period(0, 20000); /* Start the program */ err = ulp_riscv_run(); ESP_ERROR_CHECK(err); } void led_matrix_init(void) { ESP_LOGI(TAG, "led_matrix_init"); led_matrix_i80_hub75_bus_init(); led_matrix_oe_lat_ledc_init(); led_matrix_ulp_risc_init(); } /* 设置亮度,规定可调范围0-1800 */ int led_matrix_set_brightness(int brightness) { if (brightness < 0 || brightness > 1800) { ESP_LOGE(TAG, "Invalid brightness value(0-1800): %d", brightness); return -1; } ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL_0, brightness)); ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL_0)); return 0; } /* 画点 */ int led_matrix_draw_point(int x, int y, uint8_t color) { return hub75_draw_point(io_handle, x, y, color); } /* 填充区域 */ void led_matrix_fill_rectangle(int x0, int y0, int x1, int y1, void *color) { hub75_fill_rectangle(io_handle, x0, y0, x1, y1, color); }