基于GC9A01A的1.28寸圆屏驱动与移植LVGL和emWin

原创 嵌入式Lee 2024-07-09 15:41

一. 前言

前面我们分享了驱动ST7789屏并且移植LVGLemWin的过程,我们现在来分享驱动另外一块屏基于GC9A01A1.28寸圆形屏,并移植LVGLemWin

二. 屏介绍

2.1 接口

我这里选用的是TFT128QY15-V2这个模组,主控是GC9A01A,是一个240x240,最大支持18位,即262k色的TFT屏,一般使用16位色即可。

网上找到了主控的规格书,如下,手动加了书签方便阅读。

从模组规格书看引脚,从引脚的名字就可以知道是用的什么接口方式,这里可以看出用的是

4-line serial interface Ⅰ

串口接口有所谓的三线和四线,I型和II型,组合4种,如下IM引脚配置。

三线和4线的区别是4线专门有D/CX引脚表示发送的是命令还是数据,

而三线则将8位数据扩充为9位,第9位来表示是数据还是命令即无D/CX引脚。

I型和II型的区别是, II型有DODI两个引脚分别收发,而I型只有一个引脚DI即可收又可发。

我这里通过模组引脚可以看出使用的是4线I型。

2.2读写

上述串行接口实际就是SPI接口,对应的是模式0

高位在前,多字节数据使用大端模式。

一般只需要使用写即可,读不是必须的。

2.2.1

按照先发命令字节,D/C=0

然后发送数据D/C=1的方式进行,命令后可以无数据。

CS拉低表示一次传输的开始,命令和数据两个过程可以是两次独立的SPI传输,即CS中间可以拉高。当然中间CS也可以保持拉低,但是要注意D/C的切换。

编程角度来看,分为两次传输更合适,因为一次传输完CS自动拉高,并且也总是在传输前配置好D/C,如果是一次传输,命令切换到数据时,应用层无法知道,无法去切换D/C

注意发送多个数据之时中间CS不能再拉高,即一个字节拉高一次的CS是不符合要求。

2.2.1

I型接口,没有MISO线,使用MOSI同一根线读,且只能在RD开头的命令使用。

2.3 写显存

TTF内部有一个240x240x18-bit 的显存,TFT再根据设定的帧率刷新显存显示。

所以操作TFT显示有两种方式,一种是直接写TFT内部的显存,这种效率低会有拉窗帘的感觉。

还有一种主控芯片里开辟同样的显存,需要刷新显示时再批量写入到TFT的显存中,这种显示效果更好。我们采用后者。

最常用的是配置为16位,RGB565,直接将565格式的数据通过SPI写入即可

三. 驱动

相关代码如下

 - io_spi.c/h 完全可移植,无需任何修改,在GC9A01A_ITF_SPI_IO配置为1时使用,即IO方式模拟SPI    - gc9a01a.c/h 完全可移植,无需任何修改,st7789的驱动代码。- gc9a01a_itf.c 需要移植代码,适配SPI接口。对外提供接口。- gc9a01a_test.c/h 测试代码

gc9a01a.cgc9a01a.h完全可移植

初始化列表进行初始化序列(可按需修改)

gc9a01a.c

#include "gc9a01a.h"
/** * \struct gc9a01a_cmd_st * 命令结构体*/typedef struct{ uint8_t cmd; /**< 命令 */ uint8_t data[12]; /**< 参数,最多5个参数 */ uint8_t datalen; /**< 参数长度 */ uint16_t delay; /**< 延时时间 */} gc9a01a_cmd_st;
static gc9a01a_cmd_st s_gc9a01a_cmd_init_list[]={ ///{0xEF,{0},0,0}, ///{0xEB,{0x14},1,0},
/* 很多寄存器访问都需要Inter_command为高(默认为低)所以先发FE和EF配置Inter_command为高 */ {GC9A01A_CMD_IRE1,{0},0,0}, {GC9A01A_CMD_IRE2,{0},0,0}, {0xEB,{0x14},1,0},
{0x84,{0x40},1,0}, {0x85,{0xFF},1,0}, {0x86,{0xFF},1,0}, {0x87,{0xFF},1,0}, {0x88,{0x0A},1,0}, {0x89,{0x21},1,0}, {0x8A,{0x00},1,0}, {0x8B,{0x80},1,0}, {0x8C,{0x01},1,0}, {0x8D,{0x01},1,0}, {0x8E,{0xFF},1,0}, {0x8F,{0xFF},1,0},
/* 设置GS SS * 第一个参数为0,第二个参数有效 * GS bit6 0:G1->G32 1:G32->G1 * SS bit5 0:S1->S360 1:S360->S1 */ {GC9A01A_CMD_DFC,{0x00,0x20},2,0}, /** * Memory Access Control * 7 6 5 4 3 2 1 0 * MY MX MV ML BGR MH 0 0 * Y反转 X反转 XY交换 垂直刷新方向 0-RGB 水平刷新方向 * 1-BGR */ {GC9A01A_CMD_MADCTL,{0x08},1,0}, /* Pixel Format Set *7 【6 5 4】 3 【2 1 0】 * DPI DBI * RGB接口 MCU接口 * 101 16位 011 12位 * 110 18位 101 16位 * 110 18位 */ {GC9A01A_CMD_COLMOD,{0x55},1,0},
{0x90,{0x08,0x08,0x08,0x08},4,0}, {0xBD,{0x06},1,0}, {0xBC,{0x00},1,0},
{0xFF,{0x60,0x01,0x04},3,0},
/* 电源控制 */ {GC9A01A_CMD_PC2,{0x13},1,0}, //vbp {GC9A01A_CMD_PC3,{0x13},1,0}, //vbn {GC9A01A_CMD_PC4,{0x22},1,0}, //vrh
{0xBE,{0x11},1,0}, {0xE1,{0x10,0x0E},2,0}, {0xDF,{0x21,0x0C,0x02},3,0}, /* 设置gamma曲线 */ {GC9A01A_CMD_SETGAMMA1,{0x45,0x09,0x08,0x08,0x26,0x2A},6,0}, //默认值 80 03 08 06 05 2B {GC9A01A_CMD_SETGAMMA2,{0x43,0x70,0x72,0x36,0x37,0x6F},6,0}, //默认值 41 97 98 13 17 CD {GC9A01A_CMD_SETGAMMA3,{0x45,0x09,0x08,0x08,0x26,0x2A},6,0}, //默认值 40 03 08 0B 08 2E {GC9A01A_CMD_SETGAMMA3,{0x43,0x70,0x72,0x36,0x37,0x6F},6,0}, //默认值 3F 98 B4 14 18 CD
{0xED,{0x1B,0x0B},2,0}, {0xAE,{0x77},1,0}, {0xED,{0x1B,0x0B},2,0}, {0xCD,{0x63},1,0},
{0x70,{0x07,0x07,0x04,0x0E,0x0F,0x09,0x07,0x08,0x03},9,0}, {0xEB,{0x34},1,0}, {0x62,{0x18,0x0D,0x71,0xED,0x70,0x70,0x18,0x0F,0x71,0xEF,0x70,0x70},12,0}, {0x63,{0x18,0x11,0x71,0xF1,0x70,0x70,0x18,0x13,0x71,0xF3,0x70,0x70},12,0}, {0x64,{0x28,0x29,0xF1,0x01,0xF1,0x00,0x07},7,0}, {0x66,{0x3C,0x00,0xCD,0x67,0x45,0x45,0x10,0x00,0x00,0x00},10,0}, {0x67,{0x00,0x3C,0x00,0x00,0x00,0x01,0x54,0x10,0x32,0x98},10,0}, {0x74,{0x10,0x85,0x80,0x00,0x00,0x4E,0x00},7,0},
{0x98,{0x3E,0x07},2,0},
{GC9A01A_CMD_TELON,{0},0,0}, /* Tearing Effect Line ON */ {GC9A01A_CMD_INVON, {0x00},0,0}, {GC9A01A_CMD_SLPOUT,{0 },0,120}, /**< SLPOUT (11h): Sleep Out */ {GC9A01A_CMD_DISPON,{0}, 0,20}, /**< DISPON (29h): Display On */};
/** * \fn gc9a01a_write_cmd * 写命令 * \param[in] dev \ref gc9a01a_dev_st * \param[in] cmd 命令字节 * \retval 0 成功 * \retval 其他值 失败*/static int gc9a01a_write_cmd(gc9a01a_dev_st* dev,uint8_t cmd){ uint8_t tmp;#if GC9A01A_CHECK_PARAM if(dev == (gc9a01a_dev_st*)0) { return -1; } if(dev->set_dcx == (gc9a01a_set_dcx_pf)0) { return -1; } if(dev->write == (gc9a01a_spi_write_pf)0) { return -1; }#endif tmp = cmd; dev->enable(1); dev->set_dcx(0); dev->write(&tmp,1); dev->enable(0); return 0;}
/** * \fn gc9a01a_write_data * 写数据 * \param[in] dev \ref gc9a01a_dev_st * \param[in] data 待写入数据 * \param[in] len 待写入数据长度 * \retval 0 成功 * \retval 其他值 失败*/static int gc9a01a_write_data(gc9a01a_dev_st* dev,uint8_t* data, uint32_t len){#if GC9A01A_CHECK_PARAM if(dev == (gc9a01a_dev_st*)0) { return -1; } if(dev->set_dcx == (gc9a01a_set_dcx_pf)0) { return -1; } if(dev->write == (gc9a01a_spi_write_pf)0) { return -1; }#endif dev->enable(1); dev->set_dcx(1); dev->write(data,len); dev->enable(0); return 0;}
/** * \fn gc9a01a_set_windows * 设置窗口范围(行列地址) * \param[in] dev \ref gc9a01a_dev_st * \param[in] data 待写入数据 * \param[in] len 待写入数据长度 * \retval 0 成功 * \retval 其他值 失败*/static int gc9a01a_set_windows(gc9a01a_dev_st* dev, uint16_t x0, uint16_t x1, uint16_t y0, uint16_t y1){ uint8_t data[4]; gc9a01a_write_cmd(dev, GC9A01A_CMD_CASET); data[0] = (x0>>8) & 0xFF; /* 列开始地址 大端 */ data[1] = x0 & 0xFF; data[2] = (x1>>8) & 0xFF; /* 列结束地址 大端 */ data[3] = x1 & 0xFF; gc9a01a_write_data(dev, data, 4);
gc9a01a_write_cmd(dev, GC9A01A_CMD_RASET); data[0] = (y0>>8) & 0xFF; /* 行开始地址 大端 */ data[1] = y0 & 0xFF; data[2] = (y1>>8) & 0xFF; /* 行结束地址 大端 */ data[3] = y1 & 0xFF; gc9a01a_write_data(dev, data, 4);
return 0;}
/** * \fn gc9a01a_sync * 现存写入gc9a01a * \param[in] dev \ref gc9a01a_dev_st * \paran[in] x0 列开始地址 * \paran[in] x1 列结束地址 * \paran[in] y0 行开始地址 * \paran[in] y1 行结束地址 * \paran[in] buffer 待写入数据 * \paran[in] len 待写入数据长度 * \retval 0 成功 * \retval 其他值 失败*/int gc9a01a_sync(gc9a01a_dev_st* dev, uint16_t x0, uint16_t x1, uint16_t y0, uint16_t y1, uint16_t* buffer, uint32_t len){ (void)dev; gc9a01a_set_windows(dev, x0, x1, y0, y1); gc9a01a_write_cmd(dev,GC9A01A_CMD_RAMWR); gc9a01a_write_data(dev, (uint8_t*)buffer, len); return 0;}
/** * \fn gc9a01a_init * 初始化 * \param[in] dev \ref gc9a01a_dev_st * \retval 0 成功 * \retval 其他值 失败*/int gc9a01a_init(gc9a01a_dev_st* dev){#if GC9A01A_CHECK_PARAM if(dev == (gc9a01a_dev_st*)0) { return -1; }#endif if(dev->init_flag != 0) { return 0; } dev->init_flag = 1; if(dev->init != 0) { dev->init(); } dev->set_reset(1); dev->delay(120); dev->set_reset(0); dev->delay(120); dev->set_reset(1); dev->delay(120);
/* 初始化序列 */ for(uint32_t i=0; i<sizeof(s_gc9a01a_cmd_init_list)/sizeof(s_gc9a01a_cmd_init_list[0]); i++) { gc9a01a_write_cmd(dev, s_gc9a01a_cmd_init_list[i].cmd); if(s_gc9a01a_cmd_init_list[i].datalen > 0) { gc9a01a_write_data(dev, s_gc9a01a_cmd_init_list[i].data,s_gc9a01a_cmd_init_list[i].datalen); if(s_gc9a01a_cmd_init_list[i].delay > 0) { dev->delay(s_gc9a01a_cmd_init_list[i].delay); } } }
return 0;}
/** * \fn gc9a01a_deinit * 解除初始化 * \param[in] dev \ref gc9a01a_dev_st * \return 总是返回0*/int gc9a01a_deinit(gc9a01a_dev_st* dev){#if GC9A01A_CHECK_PARAM if(dev == (gc9a01a_dev_st*)0) { return -1; }#endif
/* @todo 添加IO等解除初始化配置 */
if(dev->deinit != 0) { dev->deinit(); } return 0;}

gc9a01a.h

#ifndef GC9A01A_H#define GC9A01A_H
#ifdef __cplusplus extern "C"{#endif
#include
#define GC9A01A_CHECK_PARAM 1
typedef void (*gc9a01a_set_dcx_pf)(uint8_t val); /**< DCX引脚操作接口,val=1为数据和参数, val=0为命令 */typedef void (*gc9a01a_set_reset_pf)(uint8_t val); /**< 复位引脚操作,val=1输出高,val=0输出低 */typedef void (*gc9a01a_spi_write_pf)(uint8_t* buffer, uint32_t len); /**< MOSI写接口接口,buffer为待写数据,len为待写长度 */typedef void (*gc9a01a_spi_enable_pf)(uint8_t val); /**< 使能接口 */typedef void (*gc9a01a_spi_delay_ms_pf)(uint32_t t); /**< 延时接口 */typedef void (*gc9a01a_init_pf)(void); /**< 初始化接口 */typedef void (*gc9a01a_deinit_pf)(void); /**< 解除初始化接口 */
#define GC9A01A_CMD_SLPOUT 0x11 /* 退出SLEEP模式 */#define GC9A01A_CMD_INVON 0x21 /* 显示反转 */#define GC9A01A_CMD_DISPON 0x29 /* 打开显示 */#define GC9A01A_CMD_CASET 0x2A /* 设置列地址 */#define GC9A01A_CMD_RASET 0x2B /* 设置行地址 */#define GC9A01A_CMD_RAMWR 0x2C /* 写数据 */#define GC9A01A_CMD_MADCTL 0x36 /* 显存访问控制 */#define GC9A01A_CMD_COLMOD 0x3A /* 点格式 */#define GC9A01A_CMD_IRE1 0xFE /* Inter Register Enable1 */#define GC9A01A_CMD_IRE2 0xEF /* Inter Register Enable2 */#define GC9A01A_CMD_DFC 0xB6 /* Display Function Control */#define GC9A01A_CMD_PC2 0xC3 /* Power Control 2 */#define GC9A01A_CMD_PC3 0xC4 /* Power Control 3 */#define GC9A01A_CMD_PC4 0xC9 /* Power Control 4 */#define GC9A01A_CMD_TELON 0x35 /* Tearing Effect Line ON */#define GC9A01A_CMD_SETGAMMA1 0xF0 /*SET_GAMMA1 */#define GC9A01A_CMD_SETGAMMA2 0xF1 /*SET_GAMMA2 */#define GC9A01A_CMD_SETGAMMA3 0xF2 /*SET_GAMMA3 */#define GC9A01A_CMD_SETGAMMA4 0xF3 /*SET_GAMMA4 */
/** * \struct gc9a01a_dev_st * 设备接口结构体*/typedef struct{ gc9a01a_set_dcx_pf set_dcx; /**< DCX写接口 */ gc9a01a_set_reset_pf set_reset; /**< RESET写接口 */ gc9a01a_spi_write_pf write; /**< 数据写接口 */ gc9a01a_spi_enable_pf enable; /**< 使能接口 */ gc9a01a_spi_delay_ms_pf delay; /**< 延时接口 */ gc9a01a_init_pf init; /**< 初始化接口 */ gc9a01a_deinit_pf deinit; /**< 解除初始化接口 */
uint16_t* buffer; /**< 显存,用户分配 */ int init_flag; /**< 初始化标志 */ } gc9a01a_dev_st;
/** * \fn gc9a01a_sync * 现存写入gc9a01a * \param[in] dev \ref gc9a01a_dev_st * \paran[in] x0 列开始地址 * \paran[in] x1 列结束地址 * \paran[in] y0 行开始地址 * \paran[in] y1 行结束地址 * \paran[in] buffer 待写入数据 * \paran[in] len 待写入数据长度 * \retval 0 成功 * \retval 其他值 失败*/int gc9a01a_sync(gc9a01a_dev_st* dev, uint16_t x0, uint16_t x1, uint16_t y0, uint16_t y1, uint16_t* buffer, uint32_t len);
/** * \fn gc9a01a_init * 初始化 * \param[in] dev \ref gc9a01a_dev_st * \retval 0 成功 * \retval 其他值 失败*/int gc9a01a_init(gc9a01a_dev_st* dev);
/** * \fn gc9a01a_deinit * 解除初始化 * \param[in] dev \ref gc9a01a_dev_st * \return 总是返回0*/int gc9a01a_deinit(gc9a01a_dev_st* dev);
#ifdef __cplusplus }#endif
#endif

gc9a01a_test.c


#include #include #include #include "timer.h"#include "os_utils.h"#include "gc9a01a_itf.h"#include "gc9a01a_test.h"
static void rgb_test(void){ for(int x=0;x<240;x++) { for(int y=0;y<320;y++) { gc9a01a_itf_set_pixel(x, y, 0xF800); } } gc9a01a_itf_sync(); os_delay(1000);
for(int x=0;x<240;x++) { for(int y=0;y<320;y++) { gc9a01a_itf_set_pixel(x, y, 0x07E0); } } gc9a01a_itf_sync(); os_delay(1000);
for(int x=0;x<240;x++) { for(int y=0;y<320;y++) { gc9a01a_itf_set_pixel(x, y, 0x001F); } } gc9a01a_itf_sync(); os_delay(1000);}
int gc9a01a_test(void){ printf("gc9a01a test\r\n"); gc9a01a_itf_init(); rgb_test(); uint32_t start; uint32_t end; uint32_t ftime = 0; while(0) { start = timer_get_time(); for(int i=0;i<10;i++) { gc9a01a_itf_sync(); } end = timer_get_time(); ftime = (end - start);
uint32_t fps = (ftime*2+100)/(100*2); /* 刷新一次的时间uS */ if(fps > 0) { printf("FPS:%d\r\n",1000000/fps); } else { printf("FPS:%d\r\n",0); } } return 0;}

gc9a01a_test.h

#ifndef GC9A01A_TEST_H#define GC9A01A_TEST_H
#ifdef __cplusplus extern "C"{#endif
#include
int gc9a01a_test(void);
#ifdef __cplusplus }#endif
#endif

gc9a01a_itf.c

#include #include "resource.h"#include "gpio.h"#include "timer.h"#include "os_utils.h"#include "gc9a01a_itf.h"#include "gc9a01a.h"
#define USE_SPI_PORT SPI_PORT1#if 1#define CS_PIN GPIO_30 #define DC_PIN GPIO_20 #define SCL_PIN GPIO_29 #define SDA_PIN GPIO_39 #define RST_PIN GPIO_38 #else#define CS_PIN GPIO_05 #define DC_PIN GPIO_06 #define SCL_PIN GPIO_04 #define SDA_PIN GPIO_03 #define RST_PIN GPIO_00 #endif/****************************************************************************** * 以下是底层适配 * ******************************************************************************/
#if GC9A01A_ITF_SPI_IO/* 使用IO模拟SPI方式 */ #include "io_spi.h"
static void port_io_spi_cs_write(uint8_t val){ gpio_write(CS_PIN,val);}
static void port_io_spi_sck_write(uint8_t val){ gpio_write(SCL_PIN,val);}
static void port_io_spi_mosi_write(uint8_t val){ gpio_write(SDA_PIN,val);}
static uint8_t port_io_spi_miso_read(void){ return 0;}
static void port_io_spi_init(void){ gpio_open(CS_PIN, GPIO_DIRECTION_OUTPUT); gpio_set_pull_mode(CS_PIN, GPIO_PULL_UP); gpio_write(CS_PIN,1); gpio_open(SCL_PIN, GPIO_DIRECTION_OUTPUT); gpio_set_pull_mode(SCL_PIN, GPIO_PULL_UP); gpio_write(SCL_PIN,1); gpio_open(SDA_PIN, GPIO_DIRECTION_OUTPUT); gpio_set_pull_mode(SDA_PIN, GPIO_PULL_UP); gpio_write(SDA_PIN,1); if(RST_PIN < GPIO_INVALID) { gpio_open(RST_PIN, GPIO_DIRECTION_OUTPUT); gpio_set_pull_mode(RST_PIN, GPIO_PULL_UP); gpio_write(RST_PIN,1); }}
static void port_io_spi_deinit(void){ gpio_close(CS_PIN); gpio_close(SCL_PIN); gpio_close(SDA_PIN); }
/* IO模拟SPI设备实例 */static io_spi_dev_st s_io_spi_dev ={ .cs_write = port_io_spi_cs_write, .sck_write = port_io_spi_sck_write, .mosi_write = port_io_spi_mosi_write, .miso_read = port_io_spi_miso_read, .delay_pf = 0, .init = port_io_spi_init, .deinit = port_io_spi_deinit, .delayns = 1, .mode = 0, .msb = 1,};
static void port_gc9a01a_set_dcx(uint8_t val){ gpio_write(DC_PIN, val);}
static void port_gc9a01a_set_reset(uint8_t val){ (void)val; if(RST_PIN < GPIO_INVALID) { gpio_write(RST_PIN, val); }}
static void port_gc9a01a_spi_write(uint8_t* buffer, uint32_t len){ io_spi_trans(&s_io_spi_dev, buffer, 0, len);}
static void port_gc9a01a_spi_enable(uint8_t val){ if(val) { io_spi_enable(&s_io_spi_dev); } else { io_spi_disable(&s_io_spi_dev); }}
static void port_gc9a01a_delay_ms(uint32_t t){ if(t > 0) { os_delay(t); }}
static void port_gc9a01a_init(void){ gpio_open(DC_PIN, GPIO_DIRECTION_OUTPUT); gpio_set_pull_mode(DC_PIN, GPIO_PULL_UP); gpio_write(DC_PIN,1);
io_spi_init(&s_io_spi_dev);}
static void port_gc9a01a_deinit(void){ gpio_close(DC_PIN);
io_spi_deinit(&s_io_spi_dev);}
#else /* 使用硬件SPI方式 */ #include "spi.h"
static void port_gc9a01a_set_dcx(uint8_t val){ gpio_write(DC_PIN, val);}
static void port_gc9a01a_set_reset(uint8_t val){ (void)val; if(RST_PIN < GPIO_INVALID) { gpio_write(RST_PIN, val); }}
volatile uint32_t s_gc9a01a_spi_busy_flag = 0;
static void spi_dma_cb(void){ //printf("spi done\r\n"); s_gc9a01a_spi_busy_flag = 0; timer_delay_us(2);}
static void port_gc9a01a_spi_write(uint8_t* buffer, uint32_t len){ RET ret; int timeout = 1000; while(s_gc9a01a_spi_busy_flag != 0) { if(len > 1024ul) { /* 传输数据较多时os delay让出CPU给其他任务 */ os_delay(1); timeout--; if(timeout <= 0) { printf("spi busy\r\n"); return; } } else { /* 传输数据较少时,直接等待传输完,没必要再主动delay让出CPU */ } } s_gc9a01a_spi_busy_flag = 1; if(RET_OK != (ret = spi_dma_trans_direct(USE_SPI_PORT, buffer, 0, len, spi_dma_cb))) { printf("spi err %d\r\n", ret); s_gc9a01a_spi_busy_flag = 0; return; }}
static void port_gc9a01a_spi_enable(uint8_t val){ if(val) { //gpio_write(CS_PIN,0); } else { //gpio_write(CS_PIN,1); }}
static void port_gc9a01a_delay_ms(uint32_t t){ os_delay(t);}
static void port_gc9a01a_init(void){ gpio_open(DC_PIN, GPIO_DIRECTION_OUTPUT); gpio_set_pull_mode(DC_PIN, GPIO_PULL_UP); gpio_write(DC_PIN,1);
gpio_open(GPIO_PMM00, GPIO_DIRECTION_OUTPUT); gpio_set_pull_mode(GPIO_PMM00, GPIO_PULL_UP); gpio_write(GPIO_PMM00,1); gpio_open(GPIO_PMM01, GPIO_DIRECTION_OUTPUT); gpio_set_pull_mode(GPIO_PMM01, GPIO_PULL_UP); gpio_write(GPIO_PMM01,1);
//gpio_open(CS_PIN, GPIO_DIRECTION_OUTPUT); //gpio_set_pull_mode(CS_PIN, GPIO_PULL_UP); //gpio_write(CS_PIN,1); if(RST_PIN < GPIO_INVALID) { gpio_open(RST_PIN, GPIO_DIRECTION_OUTPUT); gpio_set_pull_mode(RST_PIN, GPIO_PULL_UP); gpio_write(RST_PIN,1); } printf("io init\r\n"); /* SPI配置 */ spi_cfg_t spi_cfg; spi_gpio_cfg_t gpio_cfg;
spi_cfg.frequency = 80ul * 1000ul * 1000ul; // SPI时钟源是350M,在此基础上再分频。本TFT支持最大100M. 设置60M实际是350/6=58.3MHz gpio_cfg.auto_cs = false; gpio_cfg.cs = CS_PIN; gpio_cfg.clk = SCL_PIN; gpio_cfg.miso = GPIO_INVALID; gpio_cfg.mosi = SDA_PIN;
if(gpio_cfg.auto_cs == false) { gpio_open(gpio_cfg.cs, GPIO_DIRECTION_OUTPUT); gpio_set_pull_mode(gpio_cfg.cs, GPIO_PULL_UP); } RET ret; if(RET_OK != (ret = spi_init(USE_SPI_PORT))) { printf("spi init err %d\r\n", ret); } if(RET_OK != (ret = spi_open(USE_SPI_PORT, &spi_cfg, &gpio_cfg))) { printf("spi open err %d\r\n", ret); } spi_modify_CPOL_CPHA(USE_SPI_PORT, SPI_CLK_MODE_0);
printf("spi init\r\n");}
static void port_gc9a01a_deinit(void){ gpio_close(DC_PIN); if(RST_PIN < GPIO_INVALID) { gpio_close(RST_PIN); } spi_close(USE_SPI_PORT);} #endif
/****************************************************************************** * 以下是GC9A01A设备实例 * ******************************************************************************/
static uint16_t s_gc9a01a_itf_buffer[GC9A01A_HSIZE][GC9A01A_VSIZE]; /**< 显存 */
/* 设备实例 */static gc9a01a_dev_st s_gc9a01a_itf_dev ={ .set_dcx = port_gc9a01a_set_dcx, .set_reset = port_gc9a01a_set_reset, .write = port_gc9a01a_spi_write, .enable = port_gc9a01a_spi_enable, .delay = port_gc9a01a_delay_ms, .init = port_gc9a01a_init, .deinit = port_gc9a01a_deinit,
.buffer = (uint16_t*)s_gc9a01a_itf_buffer,};
/****************************************************************************** * 以下是对外操作接口 * ******************************************************************************/

/** * \fn gc9a01a_itf_init * 初始化 * \retval 0 成功 * \retval 其他值 失败*/int gc9a01a_itf_init(void){#if 0 gpio_open(DC_PIN, GPIO_DIRECTION_OUTPUT); gpio_set_pull_mode(DC_PIN, GPIO_PULL_UP); gpio_write(DC_PIN,1); gpio_open(CS_PIN, GPIO_DIRECTION_OUTPUT); gpio_set_pull_mode(CS_PIN, GPIO_PULL_UP); gpio_write(CS_PIN,1); gpio_open(SCL_PIN, GPIO_DIRECTION_OUTPUT); gpio_set_pull_mode(SCL_PIN, GPIO_PULL_UP); gpio_write(SCL_PIN,1); gpio_open(SDA_PIN, GPIO_DIRECTION_OUTPUT); gpio_set_pull_mode(SDA_PIN, GPIO_PULL_UP); gpio_write(SDA_PIN,1); while(1) { static int s_cnt = 0; if(s_cnt%1 == 0) { gpio_toggle(DC_PIN); } if(s_cnt%2 == 1) { gpio_toggle(CS_PIN); } if(s_cnt%4 == 2) { gpio_toggle(SCL_PIN); } if(s_cnt%8 == 3) { gpio_toggle(SDA_PIN); } os_delay(1); s_cnt++; }#endif return gc9a01a_init(&s_gc9a01a_itf_dev);}
/** * \fn gc9a01a_itf_deinit * 解除初始化 * \retval 0 成功 * \retval 其他值 失败*/int gc9a01a_itf_deinit(void){ return gc9a01a_deinit(&s_gc9a01a_itf_dev);}
/** * \fn gc9a01a_itf_sync * 刷新显示 * \retval 0 成功 * \retval 其他值 失败*/int gc9a01a_itf_sync(void){ return gc9a01a_sync(&s_gc9a01a_itf_dev, 0, GC9A01A_HSIZE-1, 0, GC9A01A_VSIZE-1, s_gc9a01a_itf_dev.buffer, GC9A01A_HSIZE*GC9A01A_VSIZE*2);}
/** * \fn gc9a01a_itf_set_pixel * 写点 * \param[in] x x坐标位置 * \param[in] y y坐标位置 * \param[in] rgb565 颜色*/void gc9a01a_itf_set_pixel(uint16_t x, uint16_t y, uint16_t rgb565){ //if(x >= GC9A01A_HSIZE) //{ // return -1; //} //if(y >= GC9A01A_VSIZE) //{ // return -1; //} s_gc9a01a_itf_dev.buffer[y*GC9A01A_HSIZE + x] = (uint16_t)((rgb565>>8)&0xFF) | (uint16_t)((rgb565<<8) & 0xFF00);}
/** * \fn gc9a01a_itf_get_pixel * 读点 * \param[in] x x坐标位置 * \param[in] y y坐标位置 * \return rgb565颜色*/uint16_t gc9a01a_itf_get_pixel(uint16_t x, uint16_t y){ uint16_t color = s_gc9a01a_itf_dev.buffer[y*GC9A01A_HSIZE + x]; return ((uint16_t)(color>>8) | (uint16_t)(color<<8));}

gc9a01_itf.h

#ifndef GC9A01A_ITF_H#define GC9A01A_ITF_H
#ifdef __cplusplus extern "C"{#endif
#include
#define GC9A01A_ITF_SPI_IO 0 /**< 配置为1使用IO模拟, 配置为0使用硬件SPI */
#define GC9A01A_HSIZE 240#define GC9A01A_VSIZE 240
/** * \fn gc9a01a_itf_init * 初始化 * \retval 0 成功 * \retval 其他值 失败*/int gc9a01a_itf_init(void);
/** * \fn gc9a01a_itf_deinit * 解除初始化 * \retval 0 成功 * \retval 其他值 失败*/int gc9a01a_itf_deinit(void);
/** * \fn gc9a01a_itf_sync * 刷新显示 * \retval 0 成功 * \retval 其他值 失败*/int gc9a01a_itf_sync(void);
/** * \fn gc9a01a_itf_set_pixel * 写点 * \param[in] x x坐标位置 * \param[in] y y坐标位置 * \param[in] rgb565 颜色*/void gc9a01a_itf_set_pixel(uint16_t x, uint16_t y, uint16_t rgb565);
/** * \fn gc9a01a_itf_get_pixel * 读点 * \param[in] x x坐标位置 * \param[in] y y坐标位置 * \return rgb565颜色*/uint16_t gc9a01a_itf_get_pixel(uint16_t x, uint16_t y);
#ifdef __cplusplus }#endif
#endif

io_spi.c

#include "io_spi.h"
void io_spi_enable(io_spi_dev_st* dev){ if((dev != 0) && (dev->cs_write != 0) && (dev->sck_write != 0)) { /* 准备空闲时的SCK状态,在CS拉低之前准备好 */ dev->sck_write((dev->mode & 0x02) >> 1); if(dev->delay_pf != 0) { dev->delay_pf(dev->delayns); } /* 拉低CS */ dev->cs_write(0); /* (5) SCK电平保持 */ //if(dev->delay_pf != 0) //{ // dev->delay_pf(dev->delayns); //} }}
void io_spi_disable(io_spi_dev_st* dev){ if((dev != 0) && (dev->cs_write != 0)) { dev->cs_write(1); }}
/** * _____ _____ * CS |_____________________________________________________________| * _____________ _________ * SCK(CPOL=0) xx__________| |___ xxx __________| |__________ * __________ ____xxx __________ __________ * SCK(CPOL=1) xx |_____________| |_________| * (0) * (1) * (2) * (3)(4) * (5) * (6)(7) * MISO ^ ^ * MOSI ^ * (1) (2) (4) (6) * (3) (5) * 其中()表示行为,^表示MOSI/MISO的输出或者采样位置. * (0) io_spi_enable 准备SCK空闲状态,拉低CS. * (1) 准备SCK初始状态,和(0)时SCK初始状态一样,代码中执行这个操作的目的仅仅是初始化局部变量cpol而已. * (2) 输出MOSI数据. * (3) 反转SCK产生第1个边沿. * (4) 如果CPHA=0 则第1个边沿采样,MISO在此采样. * (5) SCK高/低电平保持时间. * (6) 反转SCK产生第2个边沿. * (7) 如果CPHA=1 则第2个边沿采样,MISO在此采样. */int io_spi_trans(io_spi_dev_st* dev, uint8_t* tx, uint8_t* rx, uint32_t size){ uint32_t i = 0; /* 字节数循环 */ uint8_t j = 0; /* 位数循环 */ uint8_t msb = 0; /* MSB标志 */ uint8_t cpha = 0; /* 相位标志bit0 */ uint8_t cpol = 0; /* 极性标志bit1 */ uint8_t rx_val = 0; /* 发送字节缓存 */ uint8_t tx_val = 0; /* 接收字节缓存 */ if(dev == 0) { return -1; } if((dev->miso_read == 0) || (dev->mosi_write == 0) || (dev->sck_write == 0)) { /* dev->delay_pf 可以不实现 */ return -1; } cpha = dev->mode & 0x01; msb = dev->msb; /* (1) 准备空闲时的SCK状态 */ cpol = (dev->mode & 0x02) >> 1; /* 这一句其实可以不用,和io_spi_enable效果一样,这里仅需要初始化cpol局部变量即可 * 加上这一句可以在此确保SCK引脚状态初始化,可靠性角度来说加上提高冗余. */ dev->sck_write(cpol);
for(i=0; i { /* 取待发送的值, 用户没有提供则发送0xFF */ if(tx != 0) { tx_val = *tx++; } else { tx_val = 0xFF; } /* 接收到的值初始化 */ rx_val = 0;
for(j=0 ;j<8; j++) { /* (2)对于发送,不管对方哪个边沿采样,都是都在第一个边沿之前准备好MOSI就行 * 如果对于对方第一个边沿采样,这里修改MOSI之后最好有个数据建立时间 */ if(msb) { dev->mosi_write(tx_val & 0x80); /* 高位在前,先发送高位,未发送数据再往高位移动 */ tx_val <<= 0x1; /* 注意写的时候是先写后移位 */ } else { dev->mosi_write(tx_val & 0x01); /* 低位在前,先发送低位,未发送数据再往高位移动 */ tx_val >>= 0x1; }
/* (3)反转产生第1个CLK边沿 */ cpol ^= 0x01; dev->sck_write(cpol); if(rx != 0) { if(cpha == 0) { /* (4)第一个边沿采样 */ if(msb) { rx_val <<= 0x1; /* 注意读的时候是先移位后读 */ rx_val |= dev->miso_read(); /* 高位在前,先读到低位,已接收数据再往高位移动 */ } else { rx_val >>= 0x1; rx_val |= dev->miso_read() <<7; /* 低位在前,先读到高位,已接收数据再往低位移动 */ } } } /* (5) SCK电平保持 */ if(dev->delay_pf != 0) { dev->delay_pf(dev->delayns); }
/* (6)反转产生第2个CLK边沿 */ cpol ^= 0x01; dev->sck_write(cpol); if(rx != 0) { if(cpha == 1) { /* (7) 第2个边沿采样 */ if(msb) { rx_val <<= 0x1; rx_val |= dev->miso_read(); /* 高位在前,先读到低位再往高位移动 */ } else { rx_val >>= 0x1; rx_val |= dev->miso_read()<<7; /* 低位在前,先读到高位再往低位移动 */ } } } /* (5) SCK电平保持 */ if(dev->delay_pf != 0) { dev->delay_pf(dev->delayns); } } /* 存储读到的值 */ if(rx != 0) { *rx++ = rx_val; } } return 0;}
void io_spi_init(io_spi_dev_st* dev){ if((dev != 0) && (dev->init != 0)) { dev->init(); }}
void io_spi_deinit(io_spi_dev_st* dev){ if((dev != 0) && (dev->deinit != 0)) { dev->deinit(); }}

io_spi.h

#ifndef IO_SPI_H#define IO_SPI_H
#ifdef __cplusplus extern "C"{#endif
#include
typedef void (*io_spi_cs_write_pf)(uint8_t val); /**< CS写接口 */typedef void (*io_spi_sck_write_pf)(uint8_t val); /**< SCK写接口 */typedef void (*io_spi_mosi_write_pf)(uint8_t val); /**< MOSI写接口 */typedef uint8_t (*io_spi_miso_read_pf)(void); /**< MISO读接口 */typedef void (*io_spi_delay_ns_pf)(uint32_t delay); /**< 延时接口 */typedef void (*io_spi_init_pf)(void); /**< 初始化接口 */typedef void (*io_spi_deinit_pf)(void); /**< 解除初始化接口 */
/** * \struct io_spi_dev_st * 接口结构体*/typedef struct{ io_spi_cs_write_pf cs_write; /**< cs写接口 */ io_spi_sck_write_pf sck_write; /**< sck写接口 */ io_spi_mosi_write_pf mosi_write; /**< mosi写接口 */ io_spi_miso_read_pf miso_read; /**< miso读接口 */ io_spi_delay_ns_pf delay_pf; /**< 延时接口 */ io_spi_init_pf init; /**< 初始化接口 */ io_spi_deinit_pf deinit; /**< 解除初始化接口 */ uint32_t delayns; /**< 延迟时间 */ uint8_t mode; /**< 模式0~3 bit0 CPHA bit1 CPOL */ uint8_t msb; /**< 1高位在前 否则低位在前 */} io_spi_dev_st;
/** * \fn io_spi_enable * 发送CS使能信号,拉低CS * \param[in] dev \ref io_spi_dev_st*/void io_spi_enable(io_spi_dev_st* dev);
/** * \fn io_spi_disable * 拉高CS,取消片选 * \param[in] dev \ref io_spi_dev_st*/void io_spi_disable(io_spi_dev_st* dev);
/** * \fn io_spi_trans * 传输,发送的同时读 * \param[in] dev \ref io_spi_dev_st * \param[in] tx 待发送的数据 如果tx为空则默认发送FF * \param[out] rx 存储接收的数据 如果rx为空则不读 * \param[in] size 传输的字节数 * \retval 0 读成功 * \retval -1 参数错误*/int io_spi_trans(io_spi_dev_st* dev, uint8_t* tx, uint8_t* rx, uint32_t size);
/** * \fn io_spi_init * 初始化 * \param[in] dev \ref io_spi_dev_st*/void io_spi_init(io_spi_dev_st* dev);
/** * \fn io_spi_deinit * 解除初始化 * \param[in] dev \ref io_spi_dev_st*/void io_spi_deinit(io_spi_dev_st* dev);
#ifdef __cplusplus }#endif
#endif

四. 测试

先检查供电,比如IOVCC1.8VVCC2.8V,背光是否正确,其他GND等引脚是否正确。

注意RESET一样要由MCU控制,初始化时复位才可靠。

然后再看信号引脚。

初始化序列,和s_gc9a01a_cmd_init_list对应

先逻辑分析仪确认发出的命令正确,比如

如下初始化序列对应逻辑分析仪抓取的信号如下,可以看到是正确的

{GC9A01A_CMD_IRE1,{0},0,0},    {GC9A01A_CMD_IRE2,{0},0,0},    {0xEB,{0x14},1,0},

整个波形见如下地址,使用DsView打开。

链接:https://pan.baidu.com/s/19yAHeiG-aPeWOh_ojErIqw?pwd=dj05

提取码:dj05

显示效果见视频LVGLemWinDemo

https://mp.weixin.qq.com/s/SjvmB4dsMvdXkj8WGgJRqA 基于GC9A01A1.28存圆屏移植emWin

https://mp.weixin.qq.com/s/gtRr41XfvttNhsRhep2IGg 基于GC9A01A1.28存圆屏移植LVGL

五. 总结

调试屏幕时可以先用IO模拟SPI方式调通,然后再改为SPI模式,调整性能。

RESET一定要接,最好是软件控制进行复位。浮空肯定是不行的,固定上拉也不可靠,最好每次由软件初始化时进行复位操作。




评论
  • 随着市场需求不断的变化,各行各业对CPU的要求越来越高,特别是近几年流行的 AIOT,为了有更好的用户体验,CPU的算力就要求更高了。今天为大家推荐由米尔基于瑞芯微RK3576处理器推出的MYC-LR3576核心板及开发板。关于RK3576处理器国产CPU,是这些年的骄傲,华为手机全国产化,国人一片呼声,再也不用卡脖子了。RK3576处理器,就是一款由国产是厂商瑞芯微,今年第二季推出的全新通用型的高性能SOC芯片,这款CPU到底有多么的高性能,下面看看它的几个特性:8核心6 TOPS超强算力双千
    米尔电子嵌入式 2025-01-03 17:04 48浏览
  • 物联网(IoT)的快速发展彻底改变了从智能家居到工业自动化等各个行业。由于物联网系统需要高效、可靠且紧凑的组件来处理众多传感器、执行器和通信设备,国产固态继电器(SSR)已成为满足中国这些需求的关键解决方案。本文探讨了国产SSR如何满足物联网应用的需求,重点介绍了它们的优势、技术能力以及在现实场景中的应用。了解物联网中的固态继电器固态继电器是一种电子开关设备,它使用半导体而不是机械触点来控制负载。与传统的机械继电器不同,固态继电器具有以下优势:快速切换:确保精确快速的响应,这对于实时物联网系统至
    克里雅半导体科技 2025-01-03 16:11 176浏览
  • 车身域是指负责管理和控制汽车车身相关功能的一个功能域,在汽车域控系统中起着至关重要的作用。它涵盖了车门、车窗、车灯、雨刮器等各种与车身相关的功能模块。与汽车电子电气架构升级相一致,车身域发展亦可以划分为三个阶段,功能集成愈加丰富:第一阶段为分布式架构:对应BCM车身控制模块,包含灯光、雨刮、门窗等传统车身控制功能。第二阶段为域集中架构:对应BDC/CEM域控制器,在BCM基础上集成网关、PEPS等。第三阶段为SOA理念下的中央集中架构:VIU/ZCU区域控制器,在BDC/CEM基础上集成VCU、
    北汇信息 2025-01-03 16:01 193浏览
  • 自动化已成为现代制造业的基石,而驱动隔离器作为关键组件,在提升效率、精度和可靠性方面起到了不可或缺的作用。随着工业技术不断革新,驱动隔离器正助力自动化生产设备适应新兴趋势,并推动行业未来的发展。本文将探讨自动化的核心趋势及驱动隔离器在其中的重要角色。自动化领域的新兴趋势智能工厂的崛起智能工厂已成为自动化生产的新标杆。通过结合物联网(IoT)、人工智能(AI)和机器学习(ML),智能工厂实现了实时监控和动态决策。驱动隔离器在其中至关重要,它确保了传感器、执行器和控制单元之间的信号完整性,同时提供高
    腾恩科技-彭工 2025-01-03 16:28 166浏览
  • 本文介绍Linux系统更换开机logo方法教程,通用RK3566、RK3568、RK3588、RK3576等开发板,触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。制作图片开机logo图片制作注意事项(1)图片必须为bmp格式;(2)图片大小不能大于4MB;(3)BMP位深最大是32,建议设置为8;(4)图片名称为logo.bmp和logo_kernel.bmp;开机
    Industio_触觉智能 2025-01-06 10:43 72浏览
  • 这篇内容主要讨论三个基本问题,硅电容是什么,为什么要使用硅电容,如何正确使用硅电容?1.  硅电容是什么首先我们需要了解电容是什么?物理学上电容的概念指的是给定电位差下自由电荷的储藏量,记为C,单位是F,指的是容纳电荷的能力,C=εS/d=ε0εrS/4πkd(真空)=Q/U。百度百科上电容器的概念指的是两个相互靠近的导体,中间夹一层不导电的绝缘介质。通过观察电容本身的定义公式中可以看到,在各个变量中比较能够改变的就是εr,S和d,也就是介质的介电常数,金属板有效相对面积以及距离。当前
    知白 2025-01-06 12:04 110浏览
  • PLC组态方式主要有三种,每种都有其独特的特点和适用场景。下面来简单说说: 1. 硬件组态   定义:硬件组态指的是选择适合的PLC型号、I/O模块、通信模块等硬件组件,并按照实际需求进行连接和配置。    灵活性:这种方式允许用户根据项目需求自由搭配硬件组件,具有较高的灵活性。    成本:可能需要额外的硬件购买成本,适用于对系统性能和扩展性有较高要求的场合。 2. 软件组态   定义:软件组态主要是通过PLC
    丙丁先生 2025-01-06 09:23 71浏览
  • 在智能家居领域中,Wi-Fi、蓝牙、Zigbee、Thread与Z-Wave等无线通信协议是构建短距物联局域网的关键手段,它们常在实际应用中交叉运用,以满足智能家居生态系统多样化的功能需求。然而,这些协议之间并未遵循统一的互通标准,缺乏直接的互操作性,在进行组网时需要引入额外的网关作为“翻译桥梁”,极大地增加了系统的复杂性。 同时,Apple HomeKit、SamSung SmartThings、Amazon Alexa、Google Home等主流智能家居平台为了提升市占率与消费者
    华普微HOPERF 2025-01-06 17:23 97浏览
  • 光耦合器,也称为光隔离器,是一种利用光在两个隔离电路之间传输电信号的组件。在医疗领域,确保患者安全和设备可靠性至关重要。在众多有助于医疗设备安全性和效率的组件中,光耦合器起着至关重要的作用。这些紧凑型设备经常被忽视,但对于隔离高压和防止敏感医疗设备中的电气危害却是必不可少的。本文深入探讨了光耦合器的功能、其在医疗应用中的重要性以及其实际使用示例。什么是光耦合器?它通常由以下部分组成:LED(发光二极管):将电信号转换为光。光电探测器(例如光电晶体管):检测光并将其转换回电信号。这种布置确保输入和
    腾恩科技-彭工 2025-01-03 16:27 171浏览
  • 根据Global Info Research项目团队最新调研,预计2030年全球封闭式电机产值达到1425百万美元,2024-2030年期间年复合增长率CAGR为3.4%。 封闭式电机是一种电动机,其外壳设计为密闭结构,通常用于要求较高的防护等级的应用场合。封闭式电机可以有效防止外部灰尘、水分和其他污染物进入内部,从而保护电机的内部组件,延长其使用寿命。 环洋市场咨询机构出版的调研分析报告【全球封闭式电机行业总体规模、主要厂商及IPO上市调研报告,2025-2031】研究全球封闭式电机总体规
    GIRtina 2025-01-06 11:10 87浏览
  •     为控制片内设备并且查询其工作状态,MCU内部总是有一组特殊功能寄存器(SFR,Special Function Register)。    使用Eclipse环境调试MCU程序时,可以利用 Peripheral Registers Viewer来查看SFR。这个小工具是怎样知道某个型号的MCU有怎样的寄存器定义呢?它使用一种描述性的文本文件——SVD文件。这个文件存储在下面红色字体的路径下。    例:南京沁恒  &n
    电子知识打边炉 2025-01-04 20:04 79浏览
  • 每日可见的315MHz和433MHz遥控模块,你能分清楚吗?众所周知,一套遥控设备主要由发射部分和接收部分组成,发射器可以将控制者的控制按键经过编码,调制到射频信号上面,然后经天线发射出无线信号。而接收器是将天线接收到的无线信号进行解码,从而得到与控制按键相对应的信号,然后再去控制相应的设备工作。当前,常见的遥控设备主要分为红外遥控与无线电遥控两大类,其主要区别为所采用的载波频率及其应用场景不一致。红外遥控设备所采用的射频信号频率一般为38kHz,通常应用在电视、投影仪等设备中;而无线电遥控设备
    华普微HOPERF 2025-01-06 15:29 92浏览
  • 在快速发展的能源领域,发电厂是发电的支柱,效率和安全性至关重要。在这种背景下,国产数字隔离器已成为现代化和优化发电厂运营的重要组成部分。本文探讨了这些设备在提高性能方面的重要性,同时展示了中国在生产可靠且具有成本效益的数字隔离器方面的进步。什么是数字隔离器?数字隔离器充当屏障,在电气上将系统的不同部分隔离开来,同时允许无缝数据传输。在发电厂中,它们保护敏感的控制电路免受高压尖峰的影响,确保准确的信号处理,并在恶劣条件下保持系统完整性。中国国产数字隔离器经历了重大创新,在许多方面达到甚至超过了全球
    克里雅半导体科技 2025-01-03 16:10 122浏览
  • 彼得·德鲁克被誉为“现代管理学之父”,他的管理思想影响了无数企业和管理者。然而,关于他的书籍分类,一种流行的说法令人感到困惑:德鲁克一生写了39本书,其中15本是关于管理的,而其中“专门写工商企业或为企业管理者写的”只有两本——《为成果而管理》和《创新与企业家精神》。这样的表述广为流传,但深入探讨后却发现并不完全准确。让我们一起重新审视这一说法,解析其中的矛盾与根源,进而重新认识德鲁克的管理思想及其著作的真正价值。从《创新与企业家精神》看德鲁克的视角《创新与企业家精神》通常被认为是一本专为企业管
    优思学院 2025-01-06 12:03 76浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦