├── img ├── gimp_01.png ├── gimp_02.png ├── gimp_03.png └── hub75e_connector.jpg ├── include ├── color_table.h └── hub75e.h ├── play_vedio_test.py ├── convert.py ├── hub75e_test.py ├── readme.md └── src └── hub75e.c /img/gimp_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vamoosebbf/hub75e/HEAD/img/gimp_01.png -------------------------------------------------------------------------------- /img/gimp_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vamoosebbf/hub75e/HEAD/img/gimp_02.png -------------------------------------------------------------------------------- /img/gimp_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vamoosebbf/hub75e/HEAD/img/gimp_03.png -------------------------------------------------------------------------------- /img/hub75e_connector.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vamoosebbf/hub75e/HEAD/img/hub75e_connector.jpg -------------------------------------------------------------------------------- /include/color_table.h: -------------------------------------------------------------------------------- 1 | #ifndef _COLOR_TABLE_H_ 2 | #define _COLOR_TABLE_H_ 3 | 4 | extern unsigned short rgb565_to_rgb444[65536]; 5 | extern unsigned char pwm_table[16][4096]; 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /play_vedio_test.py: -------------------------------------------------------------------------------- 1 | import sensor, image, time,lcd, video,os 2 | from modules import hub75e 3 | from fpioa_manager import fm 4 | from machine import Timer 5 | print(os.listdir("/")) 6 | width = 192 7 | height = 128 8 | latch = fm.register(13, fm.fpioa.GPIOHS10, force = True) 9 | a = fm.register(8, fm.fpioa.GPIOHS11, force=True) 10 | b = fm.register(9, fm.fpioa.GPIOHS12, force=True) 11 | c = fm.register(10, fm.fpioa.GPIOHS13, force=True) 12 | d = fm.register(11, fm.fpioa.GPIOHS14, force=True) 13 | e = fm.register(7, fm.fpioa.GPIOHS15, force=True) 14 | oe = fm.register(14, fm.fpioa.GPIOHS17, force=True) 15 | latch_gpio = fm.fpioa.GPIOHS10 16 | a_gpio = fm.fpioa.GPIOHS11 17 | b_gpio = fm.fpioa.GPIOHS12 18 | c_gpio = fm.fpioa.GPIOHS13 19 | d_gpio = fm.fpioa.GPIOHS14 20 | e_gpio = fm.fpioa.GPIOHS15 21 | oe_gpio = fm.fpioa.GPIOHS17 22 | # spi, chip_select, r1_pin, g1_pin, b1_pin, r2_pin, g2_pin, b2_pin, a_gpio, b_gpio, c_gpio, d_gpio, e_gpio, oe_gpio, latch_gpio, clk_pin, dma_channel, height, width 23 | parameter = [0, 17, 0, 2, 1, 4, 6, 5, a_gpio, b_gpio, c_gpio, d_gpio, e_gpio, oe_gpio, latch_gpio, 12, 3, width, height] 24 | show = hub75e(parameter) 25 | #lcd.init() 26 | img = image.Image("imgs/img1.jpg") 27 | show.display(img) 28 | time.sleep(2) 29 | v = video.open("badapple_320_240_15fps.avi") 30 | while True: 31 | s = v.capture(img) 32 | print(s) 33 | show.display(img.resize(width, height)) 34 | if s != 3: 35 | show.stop() 36 | break 37 | -------------------------------------------------------------------------------- /include/hub75e.h: -------------------------------------------------------------------------------- 1 | #ifndef _HUB75E_H_ 2 | #define _HUB75E_H_ 3 | 4 | #define HORIZONTAL_PIXELS 64 5 | #define VERTICAL_PIXELS 64 6 | #define VERTICAL_PIXELS_HALF 32 7 | #define PIXELS_COUNT (HORIZONTAL_PIXELS * VERTICAL_PIXELS) 8 | #define PIXELS_COUNT_BYTES (PIXELS_COUNT / 8) 9 | #define HORIZONTAL_PIXELS_BYTES (HORIZONTAL_PIXELS / 8) 10 | 11 | #include 12 | #include 13 | 14 | #include "color_table.h" 15 | #include "dmac.h" 16 | #include "fpioa.h" 17 | #include "gpio.h" 18 | #include "gpiohs.h" 19 | #include "io.h" 20 | #include "pwm.h" 21 | #include "spi.h" 22 | #include "sysctl.h" 23 | #include "utils.h" 24 | 25 | typedef struct 26 | { 27 | uint8_t spi; 28 | uint8_t cs_pin; 29 | uint8_t r1_pin; 30 | uint8_t g1_pin; 31 | uint8_t b1_pin; 32 | uint8_t r2_pin; 33 | uint8_t g2_pin; 34 | uint8_t b2_pin; 35 | uint8_t a_gpio; 36 | uint8_t b_gpio; 37 | uint8_t c_gpio; 38 | uint8_t d_gpio; 39 | uint8_t e_gpio; 40 | uint8_t oe_gpio; 41 | uint8_t latch_gpio; 42 | uint8_t clk_pin; 43 | uint8_t dma_channel; 44 | uint16_t width; 45 | uint16_t height; 46 | } hub75e_t; 47 | 48 | #define GPIOHS_OUT_HIGH(io) (*(volatile uint32_t *)0x3800100CU) |= (1 << (io)) 49 | #define GPIOHS_OUT_LOW(io) (*(volatile uint32_t *)0x3800100CU) &= ~(1 << (io)) 50 | #define GET_GPIOHS_VALX(io) (((*(volatile uint32_t *)0x38001000U) >> (io)) & 1) 51 | 52 | #define HUB75E_FUN_SPIxDv(x,v) FUNC_SPI##x##_D##v 53 | 54 | void hub75e_init(hub75e_t* hub75e_obj); 55 | void hub75e_display_start(hub75e_t* cur_hub75e_obj, uint16_t *cur_image); 56 | void hub75e_display_stop(void); 57 | #endif 58 | -------------------------------------------------------------------------------- /convert.py: -------------------------------------------------------------------------------- 1 | # generate a c table that can convert rgb565 to rgb444 2 | def rgb565torgb444(): 3 | arr = [] 4 | for i in range(65536): 5 | r = i >> 11 & 0x1f 6 | r = (r * 16 + 16) // 32 7 | if( r == 16): 8 | r = 15 9 | g = (i >> 5) & 0x3f 10 | g = (g * 16 + 32) // 64 11 | if(g == 16): 12 | g = 15 13 | b = i & 0x1f 14 | b = (b * 16 + 16) // 32 15 | if(b == 16): 16 | b = 15 17 | rgb444 = (r << 8) | (g << 4) | b 18 | arr.append(rgb444) 19 | return arr 20 | 21 | def format_arr(arr): 22 | s = str("unsigned short rgb565_to_rgb444[65536] = {") 23 | for i in arr: 24 | s = s + str("0x%x"%i) + "," 25 | s = s + "};" 26 | return s 27 | 28 | def rgb444_pwm(): 29 | arr_pwm = [] 30 | for i in range(16): 31 | arr = [] 32 | for j in range(4096): 33 | # red 34 | r = j >> 8 35 | tr = 1 if(r - i > 0) else 0 36 | # green 37 | g = j >> 4 & 0x0f 38 | tg = 1 if(g - i > 0) else 0 39 | # blue 40 | b = j & 0x0f 41 | tb = 1 if(b - i > 0) else 0 42 | t = (tr << 7) | (tg << 6) | tb << 5 43 | arr.append(t) 44 | arr_pwm.append(arr) 45 | return arr_pwm 46 | 47 | def format_arr_pwm(arr_pwm): 48 | s = str("unsigned char pwm_table[16][4096] = {") 49 | for i in arr_pwm: 50 | s = s + "{" 51 | for j in i: 52 | s = s + "0x{:x},".format(j) 53 | s = s + "}," 54 | s = s + "};" 55 | return s 56 | 57 | if __name__ == "__main__": 58 | fo = open("color_table.c", "w") 59 | rgb444 = rgb565torgb444() 60 | pwm = rgb444_pwm() 61 | print(len(rgb444)) 62 | print(len(pwm)) 63 | fo.write("#include \"color_table.h\"\n\n") 64 | fo.write(str(format_arr(rgb444))) 65 | fo.write(str("\n\n\n\n" + format_arr_pwm(pwm))) 66 | fo.close() 67 | -------------------------------------------------------------------------------- /hub75e_test.py: -------------------------------------------------------------------------------- 1 | import sensor, image, time,lcd 2 | from modules import hub75e 3 | from fpioa_manager import fm 4 | from machine import Timer 5 | 6 | latch = fm.register(33, fm.fpioa.GPIOHS10, force = True) 7 | a = fm.register(28, fm.fpioa.GPIOHS11, force=True) 8 | b = fm.register(29, fm.fpioa.GPIOHS12, force=True) 9 | c = fm.register(30, fm.fpioa.GPIOHS13, force=True) 10 | d = fm.register(31, fm.fpioa.GPIOHS14, force=True) 11 | e = fm.register(27, fm.fpioa.GPIOHS15, force=True) 12 | oe = fm.register(34, fm.fpioa.GPIOHS17, force=True) 13 | 14 | 15 | latch_gpio = fm.fpioa.GPIOHS10 16 | a_gpio = fm.fpioa.GPIOHS11 17 | b_gpio = fm.fpioa.GPIOHS12 18 | c_gpio = fm.fpioa.GPIOHS13 19 | d_gpio = fm.fpioa.GPIOHS14 20 | e_gpio = fm.fpioa.GPIOHS15 21 | oe_gpio = fm.fpioa.GPIOHS17 22 | 23 | 24 | height = 128 25 | width = 128 26 | 27 | # spi, chip_select, r1_pin, g1_pin, b1_pin, r2_pin, g2_pin, b2_pin, a_gpio, b_gpio, c_gpio, d_gpio, e_gpio, oe_gpio, latch_gpio, clk_pin, dma_channel, height, width 28 | parameter = [1, 37, 20, 22, 21, 24, 26, 25, a_gpio, b_gpio, c_gpio, d_gpio, e_gpio, oe_gpio, latch_gpio, 32, 3, height, width] 29 | 30 | show = hub75e(parameter) 31 | 32 | # img = image.Image() 33 | # img = img.resize(height,width) 34 | # print(img) 35 | # # img.draw_circle(20,20,10,0x00F8) 36 | # # img.draw_rectangle(00,00,30,30,0xE007) 37 | # # img.draw_rectangle(height-25,width- 25,height - 40,height-40,(0,0,255),fill = True) 38 | 39 | # # img.draw_rectangle(height-54,width - 54, height,width,0xe007,fill = True) 40 | # img.draw_rectangle(0, 0 , height, width, (0,0,255), fill = True) 41 | 42 | sensor.reset() # Reset and initialize the sensor. It will 43 | ## run automatically, call sensor.run(0) to stop 44 | sensor.set_pixformat(sensor.RGB565) # Set pixel format to RGB565 (or GRAYSCALE) 45 | sensor.set_framesize(sensor.QVGA) # Set frame size to QVGA (320x240) 46 | sensor.skip_frames(time = 2000) # Wait for settings take effect. 47 | 48 | img = sensor.snapshot() 49 | img = img.resize(height,width) 50 | 51 | 52 | def on_timer(on_timer): 53 | global img 54 | img = sensor.snapshot() 55 | img = img.resize(height,width) 56 | 57 | tim = Timer(Timer.TIMER0, Timer.CHANNEL0, mode=Timer.MODE_PERIODIC, 58 | period=30, unit=Timer.UNIT_MS, callback=on_timer, arg = on_timer, start=True, priority=1, div=0) 59 | 60 | #lcd.init(freq=15000000) 61 | print(img) 62 | while True: 63 | # lcd.display(img) # Display on LCD 64 | try: 65 | show.display(img) 66 | except Exception as e: 67 | print(e) 68 | #show.stop() 69 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # HUB75E 点阵屏的使用 2 | 3 | HUB75E 点阵屏是一块分辨率为 64*64,32 扫的点阵屏, 为了方便解释定义 SINGLE_WIDTH = 64 为单个屏幕的宽, SINGLE_HEIGHT = 64 位单个屏幕的高, SCAN_MODE = 32 为扫描模式 4 | 5 | ## 关于32扫 6 | 7 | 32 扫即选中 i 地址刷新的是第 i 行和第 i+SCAN_MODE 行, 这样刚好可以用 32 个地址刷新 64 行。 8 | 9 | ## 接口定义 10 | 11 | 12 | 13 | * RGB1 和 RGB2 为数据接口, 代表了两行数据线 14 | * A, B, C, D, E 为地址接口, 最多可以表示32个地址 15 | * Latch 脚,不清楚有什么用 16 | * OE 为使能接口 17 | 18 | ## 单个点 19 | 20 | 该点阵屏为 rgb 三色, 因此控制一个点的颜色只需要三个bit即可,共可形成八种颜色。 21 | 22 | ## 单个点阵屏 23 | 24 | 地址选中 0 开始, 每次点亮两行, 到地址为 SCAN_MODE 即可完成整块屏幕的刷新 25 | 26 | ### 分析 27 | 28 | 29 | 30 | * 整个点阵屏分为上下两部分, 每部分 SINGLE_HEIGHT/2 行。 31 | * 上半部分颜色数据线为 R1,G1, B1, 下半部分为 R2,G2,B2 32 | 33 | ### 扫描一次 34 | 35 | 例如扫描第 0x11 行和第 0x11 + SCAN_MODE 行 36 | 37 | * 填充 linebuffer,将 img 第 0x11 行填入 RGB1,第 0x11 + SCAN_MODE 行填入 RGB2. 38 | * 选中地址 0x11, 此时调用 set_addr 函数设置 A, B, C, D, E 选中 0x11。 39 | * 地址选中函数示例如下: 40 | 41 | ```c 42 | static inline void hub75e_set_addr(hub75e_t* hub75e_obj, uint8_t addr) 43 | { 44 | my_set_gpiohs(hub75e_obj->a_gpio, addr & 0x01); 45 | my_set_gpiohs(hub75e_obj->b_gpio, (addr & 0x02) >> 1); 46 | my_set_gpiohs(hub75e_obj->c_gpio, (addr & 0x04) >> 2); 47 | my_set_gpiohs(hub75e_obj->d_gpio, (addr & 0x08) >> 3); 48 | my_set_gpiohs(hub75e_obj->e_gpio, (addr & 0x10) >> 4); 49 | } 50 | ``` 51 | 52 | ### 点亮单个屏 53 | 54 | 1. 需要准备一个长度为 SINGLE_WIDTH bytes 的 linebuf 来存储行数据, 每个 byte 其中 6 位来存储 rgb1(3bit) rgb2(3bit) 的数据, 这样刚好可以表示 i,i+SCAN_MODE 两行。 55 | 2. 将数据通过 SPI 发送出去 56 | 3. latch 脚拉 57 | 4. latch 脚拉低 58 | 5. 选中地址, 地址为 5 位, A B C D E 由低位到高位 59 | 6. 拉高 OE 使能 60 | 7. 返回步骤1, 循环直到所有地址都扫描了一次 61 | 62 | ## 多个点阵屏 63 | 64 | ### 分析 65 | 66 | 1. 多个点阵屏可以看做时一个长度为:SINGLE_WIDTH*屏幕个数,高度为: SINGLE_HEIGHT 的长条形点阵屏 67 | 2. 该点阵屏上半部分颜色由 RGB1 控制,下半部分由 RGB2 控制。 68 | 3. 长条形点阵屏弯折即可拓展成高度大于 64 的矩阵屏,如图所示: 69 | 4. 这样做只是重新排列了一次而已,控制方式仍然是以单个长条型点阵屏控制,只是需要注意发送数据的顺序 70 | 71 | 72 | 73 | ### 点亮流程 74 | 75 | 这个随摆放方式不同而不同, 我使用的是 S 型摆放, 这里有一个问题是第偶数行点阵屏会是倒过来的, 总之先发送的肯定是排在最后的点阵屏, 以下图为例就是右下角那块。 76 | 77 | 78 | 79 | 以上排列方式代码如下: 80 | 81 | ```C 82 | int hub75e_display(int core) 83 | { 84 | if(!(hub75e_obj&&image)) return -1; 85 | 86 | int y, t, x; 87 | uint16_t vertical_boards = hub75e_obj->height / HEIGHT_PER_BOARD; 88 | uint16_t line_buf_size = hub75e_obj->width * vertical_boards; 89 | uint16_t *rgb444 = (uint16_t *)malloc(hub75e_obj->width * hub75e_obj->height * sizeof(uint16_t)); 90 | uint32_t *line_buffer = (uint32_t *)malloc(line_buf_size * sizeof(uint32_t)); 91 | volatile spi_t *spi_handle = spi[hub75e_obj->spi]; 92 | 93 | // rgb565 -> rgb444 损失色彩, 但是提高速率 94 | for (y = 0; y < hub75e_obj->height; y++) 95 | { 96 | for (x = 0; x < hub75e_obj->width; x++) 97 | { 98 | uint16_t rgb565_yx = *(image + y * hub75e_obj->width + x); 99 | *(rgb444 + y * hub75e_obj->width + x) = rgb565_to_rgb444[SWAP_TO_MP16(rgb565_yx)]; 100 | } 101 | } 102 | 103 | // 每张图刷新 16 次, 可以达到用占空比控制的效果, 16 为 4 位所能表示的全部色彩 104 | for (t = 0; t < 16; t++) 105 | { 106 | // 32 扫, 每次将 y 和 y+32 行填入 linebuffer 107 | for (y = 0; y < SCAN_TIMES; y++) 108 | { 109 | // 每次发送一个 linebuffer , linebuffer 大小为 每块的行宽*屏幕块数 110 | for (int bs = vertical_boards; bs > 0; bs--)//刷新第 bs 行的板子, 板子总行数为 vertical_boards 111 | { 112 | // line_buffer 填充起点 113 | int line_buf_base_index = ((vertical_boards - bs)*hub75e_obj->width); 114 | if(bs % 2 ==0){// 垂直第偶数行板子, 刷新顺序从下到上, 从右至左 115 | // 填入 linebuffer 的 img 行号 116 | int img_line_num = bs * HEIGHT_PER_BOARD - y - 1; 117 | // 填入 linebuffer 的 img 出发点 118 | int img_line_begin = img_line_num * hub75e_obj->width; 119 | // 填入 linebuffer 的 img 行加 32 扫, 因为从下往上 120 | int img_line_scan_begin = (img_line_num - SCAN_TIMES) * hub75e_obj->width; 121 | // 需要填充的 linebuffer 的结束点下标 122 | int line_buf_end_index = line_buf_base_index + hub75e_obj->width - 1; 123 | for(int x = hub75e_obj->width - 1; x >= 0; x--) 124 | { 125 | // 编码每行的点, 一次两行, 高三位: 7(r1),6(g1),5(b1)为第 img_line_num 行), 后三位: 4(r2),3(g2),2(b2) 为第 img_line_num - SCAN_TIMES 行) 126 | line_buffer[line_buf_end_index - x] = ((pwm_table[t][*(rgb444 + img_line_begin + x)]) | \ 127 | pwm_table[t][*(rgb444 + img_line_scan_begin + x)] >> 3); 128 | } 129 | }else{// 垂直第奇数块所在行, 刷新顺序从上到下, 从左至右 130 | // 填入 line-buffer 的 img 行号 131 | int img_line_num = (bs - 1) * HEIGHT_PER_BOARD + y; 132 | // 当前显示 img 行的出发点 133 | int img_line_begin = img_line_num * hub75e_obj->width; 134 | // 填入 linebuffer 的 img 行加 32 扫 135 | int img_line_scan_begin = (img_line_num + SCAN_TIMES) * hub75e_obj->width; 136 | // 显示第 bs 行板的第 y 行 137 | for(int x = 0; x < hub75e_obj->width; x++) 138 | { 139 | // 编码每行的点,高三位: 7(r1),6(g1),5(b1)为第 img_line_num 行), 后三位: 4(r2),3(g2),2(b2) 为第 img_line_num + SCAN_TIMES 行) 140 | line_buffer[x+line_buf_base_index] = ((pwm_table[t][*(rgb444 + img_line_begin + x)]) | \ 141 | pwm_table[t][*(rgb444 + img_line_scan_begin + x)] >> 3); 142 | } 143 | } 144 | } 145 | fill_line(hub75e_obj, spi_handle, line_buffer, y, line_buf_size); // 发送行数据 146 | } 147 | } 148 | free(line_buffer); 149 | free(rgb444); 150 | return 0; 151 | } 152 | ``` 153 | 154 | * 其中将 rgb565 转化为 rgb444 使用了查表方式,更快,该表运行 [convert.py](convert.py)生成, 共65536个元素。 155 | * 可以看到每张图屏片发送了 16(rgb444 中 2^4) 次, 这是为了控制 rgb 三个灯亮灭占空比达到形成多种颜色的效果,同样使用查表方式,运行 [convert.py](convert.py)生成。 156 | * 每张图刷新一次发送32次, 因为是32扫, 只需要发送32次即即可扫描所有屏 157 | 158 | **建议使用排线连接,信号更稳定,不过排线也有可能某根是坏的** -------------------------------------------------------------------------------- /src/hub75e.c: -------------------------------------------------------------------------------- 1 | #include "hub75e.h" 2 | #include "color_table.h" 3 | 4 | #define SWAP_TO_MP16(x) (((x)>>8) | ((x)<<8&0xff00)) // grbg -> rgb 5 | #define HEIGHT_PER_BOARD 64 6 | #define WIDTH_PER_BOARD 64 7 | #define SCAN_TIMES 32 8 | 9 | // cur -> current 10 | static hub75e_t* hub75e_obj = 0; 11 | static uint16_t* image = 0; 12 | 13 | static inline void my_set_gpiohs(uint8_t io, uint8_t val) 14 | { 15 | set_bit(gpiohs->output_val.u32, 1 << io, val << io); 16 | } 17 | 18 | static inline void hub75e_set_addr(hub75e_t* hub75e_obj, uint8_t addr) 19 | { 20 | my_set_gpiohs(hub75e_obj->a_gpio, addr & 0x01); 21 | my_set_gpiohs(hub75e_obj->b_gpio, (addr & 0x02) >> 1); 22 | my_set_gpiohs(hub75e_obj->c_gpio, (addr & 0x04) >> 2); 23 | my_set_gpiohs(hub75e_obj->d_gpio, (addr & 0x08) >> 3); 24 | my_set_gpiohs(hub75e_obj->e_gpio, (addr & 0x10) >> 4); 25 | } 26 | 27 | 28 | static inline void enable_hub75e(hub75e_t* hub75e_obj) 29 | { 30 | GPIOHS_OUT_LOW(hub75e_obj->oe_gpio); 31 | } 32 | static inline void disable_hub75e(hub75e_t* hub75e_obj) 33 | { 34 | GPIOHS_OUT_HIGH(hub75e_obj->oe_gpio); 35 | } 36 | 37 | static inline void latch_hub75e(hub75e_t* hub75e_obj) 38 | { 39 | GPIOHS_OUT_HIGH(hub75e_obj->latch_gpio); 40 | } 41 | 42 | static inline void unlatch_hub75e(hub75e_t* hub75e_obj) 43 | { 44 | GPIOHS_OUT_LOW(hub75e_obj->latch_gpio); 45 | } 46 | 47 | void hub75e_init(hub75e_t* hub75e_obj) 48 | { 49 | dmac_init(); 50 | spi_init(hub75e_obj->spi, SPI_WORK_MODE_0, SPI_FF_OCTAL, 8, 0); 51 | spi_init_non_standard(hub75e_obj->spi, 8 /*instrction length*/, 52 | 0 /*address length*/, 0 /*wait cycles*/, 53 | SPI_AITM_AS_FRAME_FORMAT /*spi address trans mode*/); 54 | spi_set_clk_rate(hub75e_obj->spi, 15000000); 55 | 56 | if(hub75e_obj->spi == 1){ 57 | fpioa_set_function(hub75e_obj->r1_pin, HUB75E_FUN_SPIxDv(1, 7)); 58 | fpioa_set_function(hub75e_obj->g1_pin, HUB75E_FUN_SPIxDv(1, 6)); 59 | fpioa_set_function(hub75e_obj->b1_pin, HUB75E_FUN_SPIxDv(1, 5)); 60 | fpioa_set_function(hub75e_obj->r2_pin, HUB75E_FUN_SPIxDv(1, 4)); 61 | fpioa_set_function(hub75e_obj->g2_pin, HUB75E_FUN_SPIxDv(1, 3)); 62 | fpioa_set_function(hub75e_obj->b2_pin, HUB75E_FUN_SPIxDv(1, 2)); 63 | fpioa_set_function(hub75e_obj->clk_pin, FUNC_SPI1_SCLK); 64 | fpioa_set_function(hub75e_obj->cs_pin, FUNC_SPI1_SS3); 65 | }else if(hub75e_obj->spi == 0){ 66 | fpioa_set_function(hub75e_obj->r1_pin, HUB75E_FUN_SPIxDv(0, 7)); 67 | fpioa_set_function(hub75e_obj->g1_pin, HUB75E_FUN_SPIxDv(0, 6)); 68 | fpioa_set_function(hub75e_obj->b1_pin, HUB75E_FUN_SPIxDv(0, 5)); 69 | fpioa_set_function(hub75e_obj->r2_pin, HUB75E_FUN_SPIxDv(0, 4)); 70 | fpioa_set_function(hub75e_obj->g2_pin, HUB75E_FUN_SPIxDv(0, 3)); 71 | fpioa_set_function(hub75e_obj->b2_pin, HUB75E_FUN_SPIxDv(0, 2)); 72 | fpioa_set_function(hub75e_obj->clk_pin, FUNC_SPI0_SCLK); 73 | fpioa_set_function(hub75e_obj->cs_pin, FUNC_SPI0_SS3); 74 | sysctl_set_spi0_dvp_data(1); 75 | }else{ 76 | printf("Error spi num should be 1 or 0\r\n"); 77 | return; 78 | } 79 | 80 | // fpioa_set_function(HUB75E_ADDR_A_PIN, HUB75E_ADDR_A_GPIOHSNUM + 24); 81 | // fpioa_set_io_pull(HUB75E_ADDR_A_PIN, FPIOA_PULL_UP); 82 | gpiohs_set_drive_mode(hub75e_obj->a_gpio, GPIO_DM_OUTPUT); 83 | gpiohs_set_pin(hub75e_obj->a_gpio, GPIO_PV_LOW); 84 | 85 | // fpioa_set_function(HUB75E_ADDR_B_PIN, HUB75E_ADDR_B_GPIOHSNUM + 24); 86 | // fpioa_set_io_pull(HUB75E_ADDR_B_PIN, FPIOA_PULL_UP); 87 | gpiohs_set_drive_mode(hub75e_obj->b_gpio, GPIO_DM_OUTPUT); 88 | gpiohs_set_pin(hub75e_obj->b_gpio, GPIO_PV_LOW); 89 | 90 | // fpioa_set_function(HUB75E_ADDR_C_PIN, HUB75E_ADDR_C_GPIOHSNUM + 24); 91 | // fpioa_set_io_pull(HUB75E_ADDR_C_PIN, FPIOA_PULL_UP); 92 | gpiohs_set_drive_mode(hub75e_obj->c_gpio, GPIO_DM_OUTPUT); 93 | gpiohs_set_pin(hub75e_obj->c_gpio, GPIO_PV_LOW); 94 | 95 | // fpioa_set_function(HUB75E_ADDR_D_PIN, HUB75E_ADDR_D_GPIOHSNUM + 24); 96 | // fpioa_set_io_pull(HUB75E_ADDR_D_PIN, FPIOA_PULL_UP); 97 | gpiohs_set_drive_mode(hub75e_obj->d_gpio, GPIO_DM_OUTPUT); 98 | gpiohs_set_pin(hub75e_obj->d_gpio, GPIO_PV_LOW); 99 | 100 | // fpioa_set_function(HUB75E_ADDR_E_PIN, HUB75E_ADDR_E_GPIOHSNUM + 24); 101 | // fpioa_set_io_pull(HUB75E_ADDR_E_PIN, FPIOA_PULL_UP); 102 | gpiohs_set_drive_mode(hub75e_obj->e_gpio, GPIO_DM_OUTPUT); 103 | gpiohs_set_pin(hub75e_obj->e_gpio, GPIO_PV_LOW); 104 | 105 | // fpioa_set_function(HUB75E_OE_PIN, HUB75E_OE_GPIOHSNUM + 24); 106 | // fpioa_set_io_pull(HUB75E_OE_PIN, FPIOA_PULL_UP); 107 | gpiohs_set_drive_mode(hub75e_obj->oe_gpio, GPIO_DM_OUTPUT); 108 | gpiohs_set_pin(hub75e_obj->oe_gpio, GPIO_PV_LOW); 109 | 110 | // fpioa_set_function(HUB75E_LATCH_PIN, HUB75E_LATCH_GPIOHSNUM + 24); 111 | // fpioa_set_io_pull(HUB75E_LATCH_PIN, FPIOA_PULL_UP); 112 | gpiohs_set_drive_mode(hub75e_obj->latch_gpio, GPIO_DM_OUTPUT); 113 | gpiohs_set_pin(hub75e_obj->latch_gpio, GPIO_PV_LOW); 114 | 115 | disable_hub75e(hub75e_obj); //防止初始化时闪烁 116 | } 117 | 118 | static inline void fill_line(hub75e_t *hub75e_obj, spi_t *spi_handle, uint32_t *line_buf, const int addr, int line_buf_size) 119 | { 120 | // 传输行数据, 以下相当于 spi_send_data_normal_dma 函数 121 | set_bit(&spi_handle->ctrlr0, 3 << 8, SPI_TMOD_TRANS << 8); 122 | 123 | spi_handle->dmacr = 0x2; /*enable dma transmit*/ 124 | spi_handle->ssienr = 0x01; 125 | sysctl_dma_select((sysctl_dma_channel_t)(hub75e_obj->dma_channel), 126 | SYSCTL_DMA_SELECT_SSI0_TX_REQ + hub75e_obj->spi * 2); 127 | dmac_set_single_mode(hub75e_obj->dma_channel, line_buf, 128 | (void *)(&spi_handle->dr[0]), DMAC_ADDR_INCREMENT, 129 | DMAC_ADDR_NOCHANGE, DMAC_MSIZE_4, DMAC_TRANS_WIDTH_32, 130 | line_buf_size); 131 | spi_handle->ser = 1U << SPI_CHIP_SELECT_3; 132 | dmac_wait_done(hub75e_obj->dma_channel); 133 | while ((spi_handle->sr & 0x05) != 0x04) 134 | ; 135 | spi_handle->ser = 0x00; 136 | spi_handle->ssienr = 0x00; 137 | 138 | latch_hub75e(hub75e_obj); 139 | unlatch_hub75e(hub75e_obj); 140 | hub75e_set_addr(hub75e_obj, addr); // 选择行地址 141 | enable_hub75e(hub75e_obj); // 开始显示 142 | } 143 | 144 | int hub75e_display(int core) 145 | { 146 | if(!(hub75e_obj&&image)) return -1; 147 | 148 | int y, t, x; 149 | uint16_t vertical_boards = hub75e_obj->height / HEIGHT_PER_BOARD; 150 | uint16_t line_buf_size = hub75e_obj->width * vertical_boards; 151 | uint16_t *rgb444 = (uint16_t *)malloc(hub75e_obj->width * hub75e_obj->height * sizeof(uint16_t)); 152 | uint32_t *line_buffer = (uint32_t *)malloc(line_buf_size * sizeof(uint32_t)); 153 | volatile spi_t *spi_handle = spi[hub75e_obj->spi]; 154 | 155 | // rgb565 -> rgb444 156 | for (y = 0; y < hub75e_obj->height; y++) 157 | { 158 | for (x = 0; x < hub75e_obj->width; x++) 159 | { 160 | uint16_t rgb565_yx = *(image + y * hub75e_obj->width + x); 161 | *(rgb444 + y * hub75e_obj->width + x) = rgb565_to_rgb444[SWAP_TO_MP16(rgb565_yx)]; 162 | } 163 | } 164 | 165 | // 每张图刷新 16 次, 可以达到用占空比控制的效果, 16 为 4 位所能表示的全部色彩 166 | for (t = 0; t < 16; t++) 167 | { 168 | // 32 扫, 每次将 y 和 y+32 行填入 linebuffer 169 | for (y = 0; y < SCAN_TIMES; y++) 170 | { 171 | // 每次发送一个 linebuffer , linebuffer 大小为 每块的行宽*屏幕块数 172 | for (int bs = vertical_boards; bs > 0; bs--)//刷新第 bs 行的板子, 板子总行数为 vertical_boards 173 | { 174 | // line_buffer 填充起点 175 | int line_buf_base_index = ((vertical_boards - bs)*hub75e_obj->width); 176 | if(bs % 2 ==0){// 垂直第偶数行板子, 刷新顺序从下到上, 从右至左 177 | // 填入 linebuffer 的 img 行号 178 | int img_line_num = bs * HEIGHT_PER_BOARD - y - 1; 179 | // 填入 linebuffer 的 img 出发点 180 | int img_line_begin = img_line_num * hub75e_obj->width; 181 | // 填入 linebuffer 的 img 行加 32 扫, 因为从下往上 182 | int img_line_scan_begin = (img_line_num - SCAN_TIMES) * hub75e_obj->width; 183 | // 需要填充的 linebuffer 的结束点下标 184 | int line_buf_end_index = line_buf_base_index + hub75e_obj->width - 1; 185 | for(int x = hub75e_obj->width - 1; x >= 0; x--) 186 | { 187 | // 编码每行的点, 一次两行, 高三位: 7(r1),6(g1),5(b1)为第 img_line_num 行), 后三位: 4(r2),3(g2),2(b2) 为第 img_line_num - SCAN_TIMES 行) 188 | line_buffer[line_buf_end_index - x] = ((pwm_table[t][*(rgb444 + img_line_begin + x)]) | \ 189 | pwm_table[t][*(rgb444 + img_line_scan_begin + x)] >> 3); 190 | } 191 | }else{// 垂直第奇数块所在行, 刷新顺序从上到下, 从左至右 192 | // 填入 line-buffer 的 img 行号 193 | int img_line_num = (bs - 1) * HEIGHT_PER_BOARD + y; 194 | // 当前显示 img 行的出发点 195 | int img_line_begin = img_line_num * hub75e_obj->width; 196 | // 填入 linebuffer 的 img 行加 32 扫 197 | int img_line_scan_begin = (img_line_num + SCAN_TIMES) * hub75e_obj->width; 198 | // 显示第 bs 块板的第 y 行 199 | for(int x = 0; x < hub75e_obj->width; x++) 200 | { 201 | // 编码每行的点,高三位: 7(r1),6(g1),5(b1)为第 img_line_num 行), 后三位: 4(r2),3(g2),2(b2) 为第 img_line_num + SCAN_TIMES 行) 202 | line_buffer[x+line_buf_base_index] = ((pwm_table[t][*(rgb444 + img_line_begin + x)]) | \ 203 | pwm_table[t][*(rgb444 + img_line_scan_begin + x)] >> 3); 204 | } 205 | } 206 | } 207 | fill_line(hub75e_obj, spi_handle, line_buffer, y, line_buf_size); // 发送行数据 208 | } 209 | } 210 | free(line_buffer); 211 | free(rgb444); 212 | return 0; 213 | } 214 | 215 | typedef int (*dual_func_t)(int); 216 | extern volatile dual_func_t dual_func; 217 | volatile int hub75e_display_lock = 0; 218 | 219 | void hub75e_display_start(hub75e_t* cur_hub75e_obj, uint16_t *cur_image) 220 | { 221 | hub75e_obj = cur_hub75e_obj; 222 | image = cur_image; 223 | dual_func = hub75e_display; 224 | hub75e_display_lock = 1; 225 | // printf("hub75e_display_start: %p\r\n", hub75e_display); 226 | } 227 | 228 | void hub75e_display_stop(void) 229 | { 230 | hub75e_obj = 0; 231 | image = 0; 232 | dual_func = 0; 233 | hub75e_display_lock = 0; 234 | } --------------------------------------------------------------------------------