尹小舟

  • 2024-07-01
  • 回复了主题帖: 【兆易GD32H759I-EVAL】 硬件随机数试验

    lugl4313820 发表于 2024-7-1 06:36 他生成随数的性能是如何来评估的呢,有没有跟竞品对比一下。 这个还测试啥性能啊,一般应用不考虑这个的性能吧

  • 回复了主题帖: 【兆易GD32H759I-EVAL】 硬件随机数试验

    秦天qintian0303 发表于 2024-7-1 10:00 TRNG基于物理过程(如模拟噪声)来生成随机数,这个范围有限制吗 有限制,寄存器大小是固定的

  • 2024-06-30
  • 发表了主题帖: 【兆易GD32H759I-EVAL】 硬件随机数试验

    真随机数生成器(TRNG) 简介 真随机数生成器(TRNG):与传统的伪随机数生成器(PRNG)不同,TRNG基于物理过程(如模拟噪声)来生成随机数,因此其输出在理论上不可预测且不可重复。 NIST SP800-90B标准:这是一个由美国国家标准技术研究院(NIST)制定的标准,用于指导随机数和伪随机数的生成、使用和统计测试。 主要特征 LFSR模式和NIST模式: LFSR模式:基于线性反馈移位寄存器(Linear Feedback Shift Register)的伪随机数生成模式。由于它是基于算法的,所以生成的随机数不是真正的随机,但在许多应用中可以作为随机数的近似。 NIST模式:遵循NIST SP800-90B标准的真随机数生成模式。它基于物理过程(如模拟噪声)来生成随机数。 随机数产生速率: LFSR模式下:两个连续随机数的间隔大约为40个TRNG_CLK时钟周期,每次产生32位随机数。 NIST模式下:每次可产生32位 * 4或32位 * 8的随机数。 NIST SP800-90B标准健康测试:TRNG模块支持NIST推荐的统计测试,以确保生成的随机数满足随机性要求。 健康测试功能及错误标志:TRNG模块具有内置的健康测试功能,如果检测到随机数不满足随机性要求,将设置相关的错误标志。 功耗管理:关闭TRNG模块可以一定程度上降低功耗,这在不需要频繁生成随机数的应用中是有用的。 128位随机数种子:通过模拟信号噪声生成128位随机数种子,用于在NIST模式下初始化或重新初始化随机数生成过程。这有助于确保即使在同一设备上,每次启动或重新初始化TRNG时都能获得不同的随机数序列。 实验代码 #include "gd32h7xx.h" #include "gd32h759i_eval.h" #include "systick.h" #include <stdio.h> /*! \brief enable the CPU cache \param[in] none \param[out] none \retval none */ void cache_enable(void) { /* enable I-Cache */ SCB_EnableICache(); /* enable D-Cache */ SCB_EnableDCache(); } /** * [url=home.php?mod=space&uid=159083]@brief[/url] 检查随机数准备状态 * @param 无 * @retval 0, 随机数有效; * 1, 随机数无效; */ uint8_t trng_ready_check(void) { uint32_t timeout = 0; FlagStatus trng_flag = RESET; while((RESET == trng_flag) && (0xFFFF > timeout)) /* 检查随机数是否有效 */ { timeout++; trng_flag = trng_flag_get(TRNG_FLAG_DRDY); } if (SET == trng_flag) /* 随机数有效 */ { if (RESET == trng_flag_get(TRNG_FLAG_CECS)) /* 当前是否检测到时钟错误 */ { if (RESET == trng_flag_get(TRNG_FLAG_SECS)) /* 当前是否检测到种子错误 */ { return 0; } } } printf("error occurred! : %x \r\n", TRNG_STAT); /* 查看TRNG状态寄存器内容 */ return 1; } /** * @brief 初始化TRNG * @param 无 * @retval 0,成功;1,失败 */ uint8_t trng_init(void) { uint8_t reval = 0; /* TRNG时钟配置 */ rcu_osci_on(RCU_IRC48M); /* 打开IRC48M振荡器 */ if (ERROR == rcu_osci_stab_wait(RCU_IRC48M)) /* 等待IRC48M振荡器时钟稳定 */ { return 1; } rcu_ck48m_clock_config(RCU_CK48MSRC_IRC48M); /* 选择IRC48M作为CK48M时钟的时钟源 */ rcu_periph_clock_enable(RCU_TRNG); /* 使能TRNG时钟 */ trng_deinit(); /* 复位TRNG */ trng_conditioning_reset_enable(); /* 复位逻辑训练单元使能 */ trng_mode_config(TRNG_MODSEL_LFSR); /* 配置TRNG工作在LFSR模式 */ trng_conditioning_disable(); /* 失能TRNG训练单元 */ trng_enable(); /* 使能TRNG接口 */ reval = trng_ready_check(); /* 检查随机数是否准备就绪 */ return reval; } /** * @brief 得到真随机数 * @param 无 * @retval 获取到的真随机数(32bit) */ uint32_t trng_get_random_num(void) { uint32_t random_data; while (SUCCESS == trng_ready_check()); /* 等待随机数准备就绪 */ random_data = trng_get_true_random_data(); /* 获取真随机值 */ return random_data; } /** * @brief 得到某个范围内的随机数 * @param min,max: 区间最小,最大值. * @retval 得到的随机数(rval),满足:min<=rval<=max */ int trng_get_random_range(int min, int max) { uint32_t random_data; while (SUCCESS == trng_ready_check()); /* 等待随机数准备就绪 */ random_data = trng_get_true_random_data(); /* 获取真随机值 */ return random_data % (max - min + 1) + min; } /*! \brief main function \param[in] none \param[out] none \retval none */ int main(void) { uint32_t random; /* enable the CPU Cache */ cache_enable(); uint8_t x = 0; gd_eval_com_init(EVAL_COM); printf(" address = %02x\n\r", x); gd_eval_led_init(LED1); trng_init(); while(1) { gd_eval_led_toggle(LED1); random = trng_get_random_num(); /* 获取一次随机数 */ printf(" random_num %d\n\r", random); random = trng_get_random_range(0, 100); /* 取[0,9]区间的随机数 */ printf(" random_range %d\n\r", random); } } /* retarget the C library printf function to the USART */ int fputc(int ch, FILE *f) { usart_data_transmit(EVAL_COM, (uint8_t)ch); while(RESET == usart_flag_get(EVAL_COM, USART_FLAG_TC)); return ch; }   实验现象      

  • 2024-06-29
  • 发表了主题帖: 【兆易GD32H759I-EVAL】 内部Flash 读写实验

    通过本实验主要内容: • FMC控制器原理; • FMC擦写读操作;     GD32H7系列的MCU FLASH存储器区域分类 高达3840KB主FLASH存储器; 高达64KB引导装载程序(boot loader)信息块存储器; 器件配置的选项字节。   FMC(Flash Memory Controller)为GD32H7XX系列MCU提供了全面的片上Flash内存管理功能: Flash控制器(FMC): FMC负责管理和控制GD32H7XX系列MCU上的Flash内存。 它提供了所有必要的接口和功能,用于执行Flash内存的读取、编程(写入)和擦除操作。 Flash内存大小: GD32H7XX系列MCU配备了高达3840KB(或3.75MB)的片上Flash内存。 这片内存可用于存储程序代码、常量数据或可修改的数据。 读取操作: Flash支持多种读取操作,包括64字节双字(即128位)、32位整字、16位半字和字节读操作。 这些不同的读取方式可以满足不同性能和应用需求。 编程(写入)操作: Flash编程支持64位双字(即8字节)和32位整字编程。 这意味着你可以直接写入一个完整的双字或整字到Flash内存中,而不需要单独写入每个字节。 擦除操作: FMC支持扇区擦除和整片擦除两种操作。 扇区擦除允许你选择性地擦除Flash内存中的某个特定区域,而整片擦除则会清除整个Flash内存。 代码保护区域: Flash内存包含专用的代码保护区域,这些区域只能执行,不能被读取或写入。 这种设计有助于保护存储在Flash中的程序代码免受未授权访问或篡改,从而增强了系统的安全性。 它也支持二次合作开发,因为开发者可以在不暴露原始代码的情况下,与其他团队或合作伙伴共享硬件平台。 其他特性: Flash内存可能还具有其他特性,如耐久性(可擦写次数)、读取速度、写入延迟等,这些都会影响Flash内存在具体应用中的性能和用途。       闪存包括3840KB字节主闪存,分为960个扇区,扇区大小为4KB,和64KB用于引导加载程序的信息块。主存储闪存的每个扇区都可以单独擦除 。   有关Flash擦写操作均需要先解锁Flash,然后进行擦写操作,擦写完成后再进行锁Flash,注意Flash特性只能由1写0,也就是Flash需要先擦除才能写入新的数据,如果确保写入地址的数据为全0xFF,也可以直接写入。读取Flash数据可以采取直接寻址的方式进行读取。   函数接口 解锁FMC_CTL寄存器  fmc_unlock 函数 fmc_unlock 函数的目标是解锁 FMC(Flash Memory Controller)的控制寄存器(FMC_CTL)     锁定FMC_CTL寄存器 /*! \brief unlock FMC_CTL register \param[in] none \param[out] none \retval none */ void fmc_unlock(void) { if((RESET != (FMC_CTL & FMC_CTL_LK))) { /* write the FMC key */ FMC_KEY = UNLOCK_KEY0; FMC_KEY = UNLOCK_KEY1; } } 函数首先 ,检查是否已经解锁,如果 FMC 控制器是锁定的,函数会连续写入两个解锁密钥(UNLOCK_KEY0 和 UNLOCK_KEY1)到 FMC_KEY 寄存器中。这通常是微控制器特定的解锁序列,用于安全地解锁 Flash 控制器的配置寄存器。   锁定FMC_CTL寄存器 /*! \brief lock FMC_CTL register \param[in] none \param[out] none \retval none */ void fmc_lock(void) { /* set the LK bit*/ FMC_CTL |= FMC_CTL_LK; }   全字编程 /*! \brief FMC program a word at the corresponding address \param[in] address: address to program \param[in] data: word to program \param[out] none \retval state of FMC \arg FMC_READY: the operation has been completed \arg FMC_BUSY: the operation is in progress \arg FMC_WPERR: erase/program protection error \arg FMC_PGSERR: program sequence error \arg FMC_RPERR: read protection error \arg FMC_RSERR: read secure error \arg FMC_ECCCOR: one bit correct error \arg FMC_ECCDET: two bits detect error \arg FMC_OBMERR: option byte modify error \arg FMC_TOERR: timeout error */ fmc_state_enum fmc_word_program(uint32_t address, uint32_t data) { fmc_state_enum fmc_state = FMC_READY; /* wait for the FMC ready */ fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); if(FMC_READY == fmc_state) { /* set the PG bit to start program */ FMC_CTL |= FMC_CTL_PG; __ISB(); __DSB(); REG32(address) = data; __ISB(); __DSB(); /* wait for the FMC ready */ fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT); /* reset the PG bit */ FMC_CTL &= ~FMC_CTL_PG; } /* return the FMC state */ return fmc_state; }     实验代码 /*! * 说明 读取Flash地址数据 * 输入[1] write_read_addr:需要读取的Flash地址 * 返回值 读取的数据 */ uint8_t fmc_read_8_data(uint32_t write_read_addr) { return *(uint8_t *)write_read_addr; } /*! * 说明 读取Flash地址数据 * 输入[1] write_read_addr:需要读取的Flash地址 * 返回值 读取的数据 */ uint8_t fmc_read_32_data(uint32_t write_read_addr) { return *(uint32_t *)write_read_addr; } /*! \brief main function \param[in] none \param[out] none \retval none */ int main(void) { /* enable the CPU Cache */ cache_enable(); uint8_t x = 0; gd_eval_com_init(EVAL_COM); printf(" address = %02x\n\r", x); // systick_config(); /* initialize led on the board */ gd_eval_led_init(LED1); // gd_eval_led_init(LED2); // __disable_irq(); //解锁 fmc_unlock(); /* 清除所有挂起的标志 */ fmc_all_flags_clear(); /* erase the flash sectors */ fmc_sector_erase(FMC_WRITE_START_ADDR); fmc_all_flags_clear(); //上锁 fmc_lock(); __enable_irq(); address = fmc_read_32_data(FMC_WRITE_START_ADDR); printf(" address = %02x\n\r", address); /* 判断要不要擦写 */ if(fmc_read_32_data(FMC_WRITE_START_ADDR) != data) { __disable_irq(); //解锁 fmc_unlock(); /* 清除所有挂起的标志 */ fmc_all_flags_clear(); /* erase the flash sectors */ fmc_sector_erase(FMC_WRITE_START_ADDR); fmc_all_flags_clear(); fmc_word_program(FMC_WRITE_START_ADDR, data); fmc_all_flags_clear(); //上锁 fmc_lock(); __enable_irq(); } address = fmc_read_32_data(FMC_WRITE_START_ADDR); printf(" address = %02x\n\r", address); while(1) { gd_eval_led_toggle(LED1); } }     实验现象                               

  • 2024-06-28
  • 回复了主题帖: 中微CMS8S6990 ___资源介绍

    nmg 发表于 2024-6-28 11:39 你在玩中微的mcu? 我用这个

  • 发表了主题帖: 中微CMS8S6990 ___资源介绍

    中微CMS8S6990 MCU是一款基于增强型1T 8051内核的闪存微控制器(Flash MCU),具有一系列出色的特性和参数。以下是关于该MCU的详细信息和特点: 内核与架构: 采用了增强型1T 8051内核,这是许多嵌入式系统设计师熟悉的架构,有利于开发和移植现有程序。 工作频率: 系统时钟(Fsys)最高可达48MHz,CPU时钟(Fcpu)为24MHz,支持较高的运行速度,满足快速数据处理需求。 封装与尺寸: 提供SSOP20、TSSOP20、QFN20、SSOP24及QFN24等多种封装形式。 封装尺寸(以SSOP24为例)为:长宽6.60 x 4.50mm,脚间距0.65mm,高度1.20mm,设计紧凑,适合空间有限的应用。 内存容量: 内置16KB的闪存程序空间,适合存储中等大小的程序代码。 同时提供1KB Data Flash、256B通用RAM、1KB通用XRAM,以满足不同的数据存储需求。 I/O端口与GPIO: 拥有最多22个可编程的I/O端口(GPIO),适用于多种输入输出需求。 所有数字功能可分配到任意GPIO,提供了高度的灵活性和可配置性。 模拟功能: 最多可达22路12位ADC,内置1.2V基准电压,参考电压可选1.2V/2.0V/2.4V/3.0V/VDD,提供了高精度的模拟信号转换能力。 内置2路模拟比较器,2路运算放大器,1路可编程增益放大器,支持复杂的模拟信号处理。 通信接口: 提供2个UART串口,波特率最高可达1Mb/s。 1个SPI接口,通信速率最高可达6Mb/s。 1个I2C接口,通信速率最高可达400Kb/s。 定时与控制功能: 拥有5个16位定时器,Timer2具备Capture/Compare功能。 内置WDT定时器,LSE定时器支持休眠唤醒功能。 提供6通道带死区控制的互补型PWM输出,支持独立和互补模式,以及可编程死区延时。 工作电压与温度: 工作电压范围2.1V-5.5V,适用于不同电压等级的系统。 工作温度范围-40℃至105℃,满足各种环境条件下的工作需求。 其他特性: 内置低压复位功能(LVR)和低压检测功能(LVD),确保系统稳定运行。 蜂鸣器控制功能。 96bit唯一ID(UID),用于设备识别和管理。 CMS8S6990 MCU 的 GPIO(通用输入/输出)特性提供了灵活性和多样性,使得开发者可以根据应用需求进行配置。 管脚多种功能共享: CMS8S6990 的 GPIO 引脚支持多种功能共享,这意味着同一个引脚可以被配置为执行不同的功能,例如数字输入/输出、PWM 输出等。这种多功能性极大地增加了设计的灵活性。 可配置 2 档 I/O 输出速率: GPIO 端口可以配置为两种不同的输出速率,以适应不同速度和性能要求的应用场景。 可配置 2 档 I/O 驱动电流:强驱动和弱驱动: 不同的驱动电流设置可以调整 GPIO 引脚的输出能力,以满足不同的负载和功耗需求。 IOL1 和 IOL2:分别代表输出低电压时的强驱动电流和弱驱动电流。 IOH1 和 IOH2:分别代表输出高电压时的强驱动电流和弱驱动电流。 可读取数据锁存器状态或者引脚状态: GPIO 端口的状态可以通过读取数据锁存器或引脚状态来获取。这对于监控外部信号或与其他设备通信非常有用。 可配置上升沿、下降沿、双沿触发中断: GPIO 端口可以配置为在特定信号事件(如电压上升、下降或同时上升和下降)时触发中断。这使得 MCU 可以对外部事件做出快速响应。 可配置上升沿、下降沿、双沿中断唤醒芯片: 除了触发中断外,GPIO 端口还可以配置为在特定信号事件时唤醒处于低功耗模式的 MCU。这对于需要低功耗待机并快速响应外部事件的应用非常有用。 可配置成普通输入、上拉输入、下拉输入、推挽输出、开漏输出模式: GPIO 端口支持多种配置模式,以满足不同的应用需求。 普通输入:引脚作为数字输入使用。 上拉输入:在没有外部信号时,引脚被内部上拉电阻拉至高电平。 下拉输入:在没有外部信号时,引脚被内部下拉电阻拉至低电平。 推挽输出:引脚可以输出高电平或低电平,并具有较快的切换速度。 开漏输出:引脚可以作为开漏驱动器使用,通常与外部上拉电阻一起使用以产生高电平。 最好用的就是IO口数字功能可以任意映射  

  • 发表了主题帖: 【兆易GD32H759I-EVAL】使用Guider 快速实现一个数字时钟

    本帖最后由 尹小舟 于 2024-6-29 13:25 编辑 1.  在画布上放一个图片  2 . 加入图片                3.加入数字时钟       4.改下透明度     5 ,  生成代码     6.  复制代码到自己工程         实验现象 [localvideo]83cb9533b707a92d2f98c61b3c2c6802[/localvideo]    

  • 2024-06-27
  • 回复了主题帖: 芯链微Flash unlock 命令不一样说明

    Jacktang 发表于 2024-6-27 07:33 flash解锁需要正确的序列,锁定时候只要序列不正确就锁了   ,两处的代码起到的效果是一样的 ... 恩恩

  • 2024-06-26
  • 发表了主题帖: 芯链微Flash unlock 命令不一样说明

          这两个的代码不一样,哪个是对的? 解答:flash解锁需要正确的序列,锁定时候只要序列不正确就锁了   ,两处的代码起到的效果是一样的    

  • 回复了主题帖: 如何区分·芯链微003和003S?

    fxyc87 发表于 2024-6-26 09:09 为啥不发出芯片全称?谁知道是啥芯片?003后缀的芯片多了去了,难道世界上只有一家stm32f003? CMS32L003 CLM32L003S

  • 回复了主题帖: 如何区分·芯链微003和003S?

    wangerxian 发表于 2024-6-25 19:15 两个型号的Flash不一样大是吧? 一样大,003S 便宜不少

  • 2024-06-25
  • 发表了主题帖: 如何区分·芯链微003和003S?

    可以读取0X180000C0地址数据           如果读出来全是F就是003

  • 发表了主题帖: 【兆易GD32H759I-EVAL】 LVGL 实验 跑Guider实例

    本帖最后由 尹小舟 于 2024-6-26 08:45 编辑 LVGL(Light and Versatile Graphics Library)是一个开源的嵌入式图形库,专为资源有限的微控制器和显示器设计。要在MCU中使用LVGL中要实现定时器心跳函数、LCD填充函数和触摸屏驱动。需要遵循几个步骤。下面是一个简化的方法: LVGL(Light and Versatile Graphics Library)是一个开源的嵌入式图形库,专为资源有限的微控制器和显示器设计。要在LVGL中实现定时器心跳函数、LCD填充函数和触摸屏驱动,你需要遵循几个步骤。下面是一个简化的指南: 1. 定时器心跳函数 LVGL 需要一个心跳函数(通常是一个定时器中断服务程序)来定期调用 lv_task_handler()。这个函数处理所有与LVGL相关的任务,如动画、事件处理等。 步骤: 设置定时器:使用你的微控制器的定时器功能来设置一个周期性的中断。 中断服务程序:在中断服务程序中,调用 lv_task_handler()。这个函数会处理LVGL的所有任务。 调整周期:根据你的应用需求调整定时器的周期。较短的周期可以提供更平滑的动画效果,但也会增加CPU负载。 2. LCD填充函数 LVGL 需要一个用于在LCD上显示像素的函数。这通常是一个硬件相关的函数,它知道如何将像素数据发送到LCD控制器。 步骤: 定义填充函数:创建一个函数,它接受一个矩形区域(由x、y坐标、宽度和高度定义)和一个颜色值作为参数。 实现填充逻辑:在这个函数中,编写代码来将颜色值写入到LCD控制器的帧缓冲区中,对应于指定的矩形区域。这通常涉及设置LCD控制器的地址指针、写入颜色数据等。 注册填充函数:使用LVGL的API( lv_disp_drv_register())来注册你的填充函数。当LVGL需要更新屏幕时,它就会调用你的函数。 3. 触摸屏驱动 为了使用触摸屏输入,你需要编写一个驱动程序来读取触摸屏控制器的数据,并将其转换为LVGL可以理解的坐标和触摸事件。 步骤: 初始化触摸屏:配置触摸屏控制器的寄存器,使其处于正常工作状态。 读取触摸数据:编写一个函数来从触摸屏控制器的寄存器中读取触摸数据(如X和Y坐标、触摸状态等)。 转换数据:将读取到的原始数据转换为LVGL可以理解的坐标和事件。例如,你可能需要将触摸坐标从触摸屏控制器的分辨率缩放到LVGL的坐标系统。 注册驱动程序:使用LVGL的API(如lv_indev_drv_register())来注册你的触摸屏驱动程序。这样,当LVGL需要处理触摸事件时,它就会调用你的驱动程序来读取触摸数据。 处理触摸事件:在驱动程序中,除了读取触摸数据外,你还需要编写代码来处理触摸事件(如按下、移动、释放等)。这些事件可以通过LVGL的API(如 lv_event_send())发送到LVGL的事件队列中,以便LVGL的控件可以响应它们。   定时器心跳函数移植     #include "./BSP/TIMER/timer.h" #include "lvgl.h" /** * @brief 定时器TIMERX定时中断初始化函数 * @note * 定时器的时钟来自APB1或APB2,如果CK_APBx = CK_AHB或CK_APBx = CK_AHB/2, * 则定时器的时钟等于CK_AHB(CK_TIMERx = CK_AHB),而CK_AHB为300M, 所以定时器时钟 = 300Mhz * 定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us. * Ft=定时器工作频率,单位:Mhz * * @param arr: 自动重装值 * @param psc: 时钟预分频数 * @retval 无 */ void timerx_int_init(uint64_t arr, uint16_t psc) { timer_parameter_struct timer_initpara; /* timer_initpara用于存放定时器的参数 */ /* 使能外设定时器时钟 */ rcu_periph_clock_enable(TIMERX_INT_CLK); /* 使能TIMERX的时钟 */ /* 复位定时器 */ timer_deinit(TIMERX_INT); /* 复位TIMERX */ timer_struct_para_init(&timer_initpara); /* 初始化timer_initpara为默认值 */ /* 配置定时器参数 */ timer_initpara.prescaler = psc; /* 设置预分频值 */ timer_initpara.alignedmode = TIMER_COUNTER_EDGE; /* 设置对齐模式为边沿对齐模式 */ timer_initpara.counterdirection = TIMER_COUNTER_UP; /* 设置向上计数模式 */ timer_initpara.period = arr; /* 设置自动重装载值 */ timer_initpara.clockdivision = TIMER_CKDIV_DIV1; /* 设置时钟分频因子 */ timer_initpara.repetitioncounter = 0; /* 设置重复计数器值 */ timer_init(TIMERX_INT, &timer_initpara); /* 根据参数初始化定时器 */ /* 使能定时器及其中断 */ timer_interrupt_flag_clear(TIMERX_INT, TIMER_INT_FLAG_UP); /* 清除定时器更新中断标志 */ timer_interrupt_enable(TIMERX_INT, TIMER_INT_UP); /* 使能定时器的更新中断 */ nvic_irq_enable(TIMERX_INT_IRQn, 1, 3); /* 配置NVIC设置优先级,抢占优先级1,响应优先级3 */ timer_enable(TIMERX_INT); /* 使能定时器TIMERX */ } /** * @brief 定时器TIMERX中断服务函数 * @param 无 * @retval 无 */ void TIMERX_INT_IRQHandler(void) { if (timer_interrupt_flag_get(TIMERX_INT, TIMER_INT_FLAG_UP) == SET) /* 判断定时器更新中断是否发生 */ { lv_tick_inc(1); /* lvgl的1ms心跳 */ timer_interrupt_flag_clear(TIMERX_INT, TIMER_INT_FLAG_UP); /* 清除定时器更新中断标志 */ } }     LCD填充函数   /** * @File lv_port_disp_templ.c * */ /*Copy this file as "lv_port_disp.c" and set this value to "1" to enable content*/ #if 1 /********************* * INCLUDES *********************/ #include "gd32h759i_lcd_eval.h" #include "lv_port_disp_template.h" #include <stdbool.h> #include "./BSP/LCD/lcd.h" /********************* * DEFINES *********************/ #ifndef MY_DISP_HOR_RES #warning Please define or replace the macro MY_DISP_HOR_RES with the actual screen width, default value 320 is used for now. #define MY_DISP_HOR_RES 480 #endif #ifndef MY_DISP_VER_RES #warning Please define or replace the macro MY_DISP_HOR_RES with the actual screen height, default value 240 is used for now. #define MY_DISP_VER_RES 272 #endif /********************** * TYPEDEFS **********************/ /********************** * STATIC PROTOTYPES **********************/ static void disp_init(void); static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p); //static void gpu_fill(lv_disp_drv_t * disp_drv, lv_color_t * dest_buf, lv_coord_t dest_width, // const lv_area_t * fill_area, lv_color_t color); /********************** * STATIC VARIABLES **********************/ /********************** * MACROS **********************/ /********************** * GLOBAL FUNCTIONS **********************/ void lv_port_disp_init(void) { /*------------------------- * Initialize your display * -----------------------*/ disp_init(); /*----------------------------- * Create a buffer for drawing *----------------------------*/ /** * LVGL requires a buffer where it internally draws the widgets. * Later this buffer will passed to your display driver's `flush_cb` to copy its content to your display. * The buffer has to be greater than 1 display row * * There are 3 buffering configurations: * 1. Create ONE buffer: * LVGL will draw the display's content here and writes it to your display * * 2. Create TWO buffer: * LVGL will draw the display's content to a buffer and writes it your display. * You should use DMA to write the buffer's content to the display. * It will enable LVGL to draw the next part of the screen to the other buffer while * the data is being sent form the first buffer. It makes rendering and flushing parallel. * * 3. Double buffering * Set 2 screens sized buffers and set disp_drv.full_refresh = 1. * This way LVGL will always provide the whole rendered screen in `flush_cb` * and you only need to change the frame buffer's address. */ /* Example for 1) */ // static lv_disp_draw_buf_t draw_buf_dsc_1; // static lv_color_t buf_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/ // lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/ /* Example for 2) */ static lv_disp_draw_buf_t draw_buf_dsc_2; static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/ static lv_color_t buf_2_2[MY_DISP_HOR_RES * 10]; /*An other buffer for 10 rows*/ lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/ // /* Example for 3) also set disp_drv.full_refresh = 1 below*/ // static lv_disp_draw_buf_t draw_buf_dsc_3; // static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*A screen sized buffer*/ // static lv_color_t buf_3_2[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*Another screen sized buffer*/ // lv_disp_draw_buf_init(&draw_buf_dsc_3, buf_3_1, buf_3_2, // MY_DISP_VER_RES * LV_VER_RES_MAX); /*Initialize the display buffer*/ /*----------------------------------- * Register the display in LVGL *----------------------------------*/ static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/ lv_disp_drv_init(&disp_drv); /*Basic initialization*/ /*Set up the functions to access to your display*/ /*Set the resolution of the display*/ disp_drv.hor_res = MY_DISP_HOR_RES; disp_drv.ver_res = MY_DISP_VER_RES; /*Used to copy the buffer's content to the display*/ disp_drv.flush_cb = disp_flush; /*Set a display buffer*/ disp_drv.draw_buf = &draw_buf_dsc_2; /*Required for Example 3)*/ //disp_drv.full_refresh = 1; /* Fill a memory array with a color if you have GPU. * Note that, in lv_conf.h you can enable GPUs that has built-in support in LVGL. * But if you have a different GPU you can use with this callback.*/ //disp_drv.gpu_fill_cb = gpu_fill; /*Finally register the driver*/ lv_disp_drv_register(&disp_drv); } /********************** * STATIC FUNCTIONS **********************/ /*Initialize your display and the required peripherals.*/ static void disp_init(void) { /*You code here*/ } volatile bool disp_flush_enabled = true; /* Enable updating the screen (the flushing process) when disp_flush() is called by LVGL */ void disp_enable_update(void) { disp_flush_enabled = true; } /* Disable updating the screen (the flushing process) when disp_flush() is called by LVGL */ void disp_disable_update(void) { disp_flush_enabled = false; } /*Flush the content of the internal buffer the specific area on the display *You can use DMA or any hardware acceleration to do this operation in the background but *'lv_disp_flush_ready()' has to be called when finished.*/ static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) { if(disp_flush_enabled) { /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/ int32_t x; int32_t y; for(y = area->y1; y <= area->y2; y++) { for(x = area->x1; x <= area->x2; x++) { /*Put a pixel to the display. For example:*/ /*put_px(x, y, *color_p)*/ lcd_draw_point(x, y, color_p ->full); color_p++; } } } /*IMPORTANT!!! *Inform the graphics library that you are ready with the flushing*/ lv_disp_flush_ready(disp_drv); } /*OPTIONAL: GPU INTERFACE*/ /*If your MCU has hardware accelerator (GPU) then you can use it to fill a memory with a color*/ //static void gpu_fill(lv_disp_drv_t * disp_drv, lv_color_t * dest_buf, lv_coord_t dest_width, // const lv_area_t * fill_area, lv_color_t color) //{ // /*It's an example code which should be done by your GPU*/ // int32_t x, y; // dest_buf += dest_width * fill_area->y1; /*Go to the first line*/ // // for(y = fill_area->y1; y <= fill_area->y2; y++) { // for(x = fill_area->x1; x <= fill_area->x2; x++) { // dest_buf[x] = color; // } // dest_buf+=dest_width; /*Go to the next line*/ // } //} #else /*Enable this file at the top*/ /*This dummy typedef exists purely to silence -Wpedantic.*/ typedef int keep_pedantic_happy; #endif     画点函数 /** * @brief 画点 * @param x,y: 坐标 * @param color: 点的颜色(32位颜色,方便兼容TLI) * @retval 无 */ void lcd_draw_point(uint16_t x, uint16_t y, uint16_t color) { *(__IO uint16_t*)(current_framebuffer + 2*((LCD_PIXEL_WIDTH*y) + x)) = color; }  触摸屏驱动 /** * @file lv_port_indev_templ.c * */ /*Copy this file as "lv_port_indev.c" and set this value to "1" to enable content*/ #if 1 /********************* * INCLUDES *********************/ #include "lv_port_indev_template.h" #include "../../lvgl.h" #include "./BSP/TOUCH/touch.h" #include <stdio.h> /********************* * DEFINES *********************/ /********************** * TYPEDEFS **********************/ /********************** * STATIC PROTOTYPES **********************/ static void touchpad_init(void); static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data); static bool touchpad_is_pressed(void); static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y); //static void mouse_init(void); //static void mouse_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data); //static bool mouse_is_pressed(void); //static void mouse_get_xy(lv_coord_t * x, lv_coord_t * y); //static void keypad_init(void); //static void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data); //static uint32_t keypad_get_key(void); //static void encoder_init(void); //static void encoder_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data); //static void encoder_handler(void); //static void button_init(void); //static void button_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data); //static int8_t button_get_pressed_id(void); //static bool button_is_pressed(uint8_t id); /********************** * STATIC VARIABLES **********************/ lv_indev_t * indev_touchpad; //lv_indev_t * indev_mouse; //lv_indev_t * indev_keypad; //lv_indev_t * indev_encoder; //lv_indev_t * indev_button; //static int32_t encoder_diff; //static lv_indev_state_t encoder_state; /********************** * MACROS **********************/ /********************** * GLOBAL FUNCTIONS **********************/ void lv_port_indev_init(void) { /** * Here you will find example implementation of input devices supported by LittelvGL: * - Touchpad * - Mouse (with cursor support) * - Keypad (supports GUI usage only with key) * - Encoder (supports GUI usage only with: left, right, push) * - Button (external buttons to press points on the screen) * * The `..._read()` function are only examples. * You should shape them according to your hardware */ static lv_indev_drv_t indev_drv; /*------------------ * Touchpad * -----------------*/ /*Initialize your touchpad if you have*/ touchpad_init(); /*Register a touchpad input device*/ lv_indev_drv_init(&indev_drv); indev_drv.type = LV_INDEV_TYPE_POINTER; indev_drv.read_cb = touchpad_read; indev_touchpad = lv_indev_drv_register(&indev_drv); /*------------------ * Mouse * -----------------*/ /*Initialize your mouse if you have*/ // mouse_init(); // /*Register a mouse input device*/ // lv_indev_drv_init(&indev_drv); // indev_drv.type = LV_INDEV_TYPE_POINTER; // indev_drv.read_cb = mouse_read; // indev_mouse = lv_indev_drv_register(&indev_drv); // /*Set cursor. For simplicity set a HOME symbol now.*/ // lv_obj_t * mouse_cursor = lv_img_create(lv_scr_act()); // lv_img_set_src(mouse_cursor, LV_SYMBOL_HOME); // lv_indev_set_cursor(indev_mouse, mouse_cursor); // /*------------------ // * Keypad // * -----------------*/ // /*Initialize your keypad or keyboard if you have*/ // keypad_init(); // /*Register a keypad input device*/ // lv_indev_drv_init(&indev_drv); // indev_drv.type = LV_INDEV_TYPE_KEYPAD; // indev_drv.read_cb = keypad_read; // indev_keypad = lv_indev_drv_register(&indev_drv); // /*Later you should create group(s) with `lv_group_t * group = lv_group_create()`, // *add objects to the group with `lv_group_add_obj(group, obj)` // *and assign this input device to group to navigate in it: // *`lv_indev_set_group(indev_keypad, group);`*/ // /*------------------ // * Encoder // * -----------------*/ // /*Initialize your encoder if you have*/ // encoder_init(); // /*Register a encoder input device*/ // lv_indev_drv_init(&indev_drv); // indev_drv.type = LV_INDEV_TYPE_ENCODER; // indev_drv.read_cb = encoder_read; // indev_encoder = lv_indev_drv_register(&indev_drv); // /*Later you should create group(s) with `lv_group_t * group = lv_group_create()`, // *add objects to the group with `lv_group_add_obj(group, obj)` // *and assign this input device to group to navigate in it: // *`lv_indev_set_group(indev_encoder, group);`*/ // /*------------------ // * Button // * -----------------*/ // /*Initialize your button if you have*/ // button_init(); // /*Register a button input device*/ // lv_indev_drv_init(&indev_drv); // indev_drv.type = LV_INDEV_TYPE_BUTTON; // indev_drv.read_cb = button_read; // indev_button = lv_indev_drv_register(&indev_drv); // /*Assign buttons to points on the screen*/ // static const lv_point_t btn_points[2] = { // {10, 10}, /*Button 0 -> x:10; y:10*/ // {40, 100}, /*Button 1 -> x:40; y:100*/ // }; // lv_indev_set_button_points(indev_button, btn_points); } /********************** * STATIC FUNCTIONS **********************/ /*------------------ * Touchpad * -----------------*/ /*Initialize your touchpad*/ static void touchpad_init(void) { /*Your code comes here*/ /*Your code comes here*/ // /* 电阻屏如果发现显示屏XY镜像现象,需要坐标矫正 */ // if (0 == (tp_dev.touchtype & 0x80)) { // tp_adjust(); // tp_save_adjust_data(); // } } /*Will be called by the library to read the touchpad*/ static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data) { static lv_coord_t last_x = 0; static lv_coord_t last_y = 0; /*Save the pressed coordinates and the state*/ if(touchpad_is_pressed()) { touchpad_get_xy(&last_x, &last_y); data->state = LV_INDEV_STATE_PR; } else { data->state = LV_INDEV_STATE_REL; } /*Set the last pressed coordinates*/ data->point.x = last_x; data->point.y = last_y; } /*Return true is the touchpad is pressed*/ static bool touchpad_is_pressed(void) { /*Your code comes here*/ tp_dev.scan(0); if (tp_dev.sta & TP_PRES_DOWN) { printf("\r\n x = %d y = %d",tp_dev.x[0],tp_dev.y[0]); return true; } return false; } /*Get the x and y coordinates if the touchpad is pressed*/ static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y) { /*Your code comes here*/ (*x) = tp_dev.x[0]; (*y) = tp_dev.y[0]; } /*------------------ * Mouse * -----------------*/ /*Initialize your mouse*/ static void mouse_init(void) { /*Your code comes here*/ } ///*Will be called by the library to read the mouse*/ //static void mouse_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data) //{ // /*Get the current x and y coordinates*/ // mouse_get_xy(&data->point.x, &data->point.y); // /*Get whether the mouse button is pressed or released*/ // if(mouse_is_pressed()) { // data->state = LV_INDEV_STATE_PR; // } // else { // data->state = LV_INDEV_STATE_REL; // } //} ///*Return true is the mouse button is pressed*/ //static bool mouse_is_pressed(void) //{ // /*Your code comes here*/ // return false; //} ///*Get the x and y coordinates if the mouse is pressed*/ //static void mouse_get_xy(lv_coord_t * x, lv_coord_t * y) //{ // /*Your code comes here*/ // (*x) = 0; // (*y) = 0; //} ///*------------------ // * Keypad // * -----------------*/ ///*Initialize your keypad*/ //static void keypad_init(void) //{ // /*Your code comes here*/ //} ///*Will be called by the library to read the mouse*/ //static void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data) //{ // static uint32_t last_key = 0; // /*Get the current x and y coordinates*/ // mouse_get_xy(&data->point.x, &data->point.y); // /*Get whether the a key is pressed and save the pressed key*/ // uint32_t act_key = keypad_get_key(); // if(act_key != 0) { // data->state = LV_INDEV_STATE_PR; // /*Translate the keys to LVGL control characters according to your key definitions*/ // switch(act_key) { // case 1: // act_key = LV_KEY_NEXT; // break; // case 2: // act_key = LV_KEY_PREV; // break; // case 3: // act_key = LV_KEY_LEFT; // break; // case 4: // act_key = LV_KEY_RIGHT; // break; // case 5: // act_key = LV_KEY_ENTER; // break; // } // last_key = act_key; // } // else { // data->state = LV_INDEV_STATE_REL; // } // data->key = last_key; //} ///*Get the currently being pressed key. 0 if no key is pressed*/ //static uint32_t keypad_get_key(void) //{ // /*Your code comes here*/ // return 0; //} ///*------------------ // * Encoder // * -----------------*/ ///*Initialize your keypad*/ //static void encoder_init(void) //{ // /*Your code comes here*/ //} ///*Will be called by the library to read the encoder*/ //static void encoder_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data) //{ // data->enc_diff = encoder_diff; // data->state = encoder_state; //} ///*Call this function in an interrupt to process encoder events (turn, press)*/ //static void encoder_handler(void) //{ // /*Your code comes here*/ // encoder_diff += 0; // encoder_state = LV_INDEV_STATE_REL; //} ///*------------------ // * Button // * -----------------*/ ///*Initialize your buttons*/ //static void button_init(void) //{ // /*Your code comes here*/ //} ///*Will be called by the library to read the button*/ //static void button_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data) //{ // static uint8_t last_btn = 0; // /*Get the pressed button's ID*/ // int8_t btn_act = button_get_pressed_id(); // if(btn_act >= 0) { // data->state = LV_INDEV_STATE_PR; // last_btn = btn_act; // } // else { // data->state = LV_INDEV_STATE_REL; // } // /*Save the last pressed button's ID*/ // data->btn_id = last_btn; //} ///*Get ID (0, 1, 2 ..) of the pressed button*/ //static int8_t button_get_pressed_id(void) //{ // uint8_t i; // /*Check to buttons see which is being pressed (assume there are 2 buttons)*/ // for(i = 0; i < 2; i++) { // /*Return the pressed button's ID*/ // if(button_is_pressed(i)) { // return i; // } // } // /*No button pressed*/ // return -1; //} ///*Test if `id` button is pressed or not*/ //static bool button_is_pressed(uint8_t id) //{ // /*Your code comes here*/ // return false; //} #else /*Enable this file at the top*/ /*This dummy typedef exists purely to silence -Wpedantic.*/ typedef int keep_pedantic_happy; #endif   跑一个Guider的实例   GUI-Guider 是恩智浦(NXP)为 LVGL 开发的一个上位机 GUI 设计工具。 功能特点: 拖放控件设计:GUI-Guider 提供了一个直观的拖放编辑器,允许用户通过拖放控件的方式快速设计 LVGL GUI 页面,从而加速 GUI 的设计过程。 PC 端仿真运行:设计完成的 GUI 页面可以在 PC 上进行仿真运行,用户可以在没有实际硬件的情况下预览自己设计的 UI 界面。 C 代码生成:确认设计完毕后,GUI-Guider 可以生成 C 代码,这些代码可以直接整合到 MCU 项目中,实现嵌入式设备的 GUI 功能。 兼容性: GUI-Guider 支持 Windows 10 和 Ubuntu 20.04 等操作系统。 它与恩智浦的通用和跨界 MCU 兼容,并包括用于多个受支持平台的内置项目模板。 优势: 相比 LVGL 官方推出的 SquareLine Studio(付费工具),GUI-Guider 完全免费,同时提供了类似的功能和界面布局。 GUI-Guider 生成的示例工程底层代码全部开源 选择V8.3.5     选择Simulator     选择ButtonCounter模版   输入名称创建       选择模拟器生成编译     添加文件       添加头文件目录       main.c 添加头文件 #include "gui_guider.h" #include "events_init.h"   定义  guider_ui  全局变量 lv_ui guider_ui;   #include "gd32h7xx.h" #include "systick.h" #include <stdio.h> #include "gd32h759i_eval.h" #include "gd32h759i_lcd_eval.h" #include "./BSP/LCD/lcd.h" #include "lv_port_disp_template.h" #include "lv_port_indev_template.h" #include "../../lvgl.h" #include "lv_demo_stress.h" #include "lv_demo_music.h" #include "./BSP/TOUCH/touch.h" #include "./BSP/TIMER/timer.h" #include "gui_guider.h" #include "events_init.h" lv_ui guider_ui; /*! \brief enable the CPU cache \param[in] none \param[out] none \retval none */ void cache_enable(void) { /* enable i-cache */ SCB_EnableICache(); /* enable d-cache */ SCB_EnableDCache(); } /** * @brief 清空屏幕并在右上角显示"RST" * @param 无 * @retval 无 */ void load_draw_dialog(void) { lcd_clear(WHITE); /* 清屏 */ lcd_show_string(lcddev.width - 24, 0, 200, 16, 16, "RST", BLUE); /* 显示清屏区域 */ } /*! \brief main function \param[in] none \param[out] none \retval none */ int main(void) { /* enable the CPU cache */ cache_enable(); /* configure systick */ //systick_config(); delay_init(600); /* initilize the LEDs, USART and key */ gd_eval_led_init(LED1); gd_eval_led_init(LED2); gd_eval_com_init(EVAL_COM); gd_eval_key_init(KEY_WAKEUP, KEY_MODE_GPIO); timerx_int_init(10 - 1, 30000 - 1); printf("\r\nCK_SYS is %d", rcu_clock_freq_get(CK_SYS)); lcd_init(); tp_dev.init(); if(RESET == gd_eval_key_scan(KEY_WAKEUP)) { lcd_clear(WHITE); /* 清屏 */ tp_adjust(); /* 屏幕校准 */ tp_save_adjust_data(); } lv_init(); lv_port_disp_init(); lv_port_indev_init(); setup_ui(&guider_ui); events_init(&guider_ui); while(1) { lv_timer_handler(); } } /* retarget the C library printf function to the USART */ int fputc(int ch, FILE *f) { usart_data_transmit(EVAL_COM, (uint8_t)ch); while(RESET == usart_flag_get(EVAL_COM, USART_FLAG_TBE)); return ch; }   实验现象       [localvideo]bca73787f3ca9675ab821faa2a20ae22[/localvideo]                

  • 2024-06-23
  • 发表了主题帖: 【兆易GD32H759I-EVAL】GD32H7 SysTick 延时

      Cortex-M7中的SysTick是一个重要的系统定时器,它在Cortex-M7内核中扮演着关键角色。         SysTick 校准值固定为 1000 , SysTick 时 钟 源 可 选 择 为 CK_SYS或 CK_SYS/2 。 通 过 对 SYST_RVR寄存器进行配置,从而为系统提供1ms时基。当SysTick时钟源为CK_SYS (CK_SYS=a MHz)时,SYST_RVR寄存器值设置为(a*1000-1)。当SysTick时钟源为 CK_SYS/2时,SYST_RVR寄存器值设置为(a/2*1000-1)。     SysTick定时器配置1ms时基的步骤: 选择SysTick时钟源: 如果选择CK_SYS作为时钟源,那么SysTick的计数频率将等于系统时钟频率。 如果选择CK_SYS/2作为时钟源,那么SysTick的计数频率将是系统时钟频率的一半。 计算SYST_RVR寄存器的值: 当使用CK_SYS作为时钟源时(假设CK_SYS = a MHz),SysTick每计数一次需要的时间为1/a微秒。为了获得1ms(即1000微秒)的时基,SysTick需要计数a * 1000次。但是,由于SysTick是向下递减的计数器,我们需要将计数次数减1作为重载值,所以SYST_RVR的值应设置为a * 1000 - 1。 当使用CK_SYS/2作为时钟源时,SysTick每计数一次需要的时间为2/a微秒。为了获得1ms的时基,SysTick需要计数a/2 * 1000次。同样地,SYST_RVR的值应设置为a/2 * 1000 - 1。 配置SYST_RVR寄存器: 使用合适的值配置SYST_RVR寄存器。这通常涉及将计算出的值写入该寄存器。 启用SysTick中断和定时器: 配置SysTick控制寄存器(SYST_CTRL)以启用SysTick中断(如果需要的话)并启动定时器。 编写SysTick中断服务程序: 编写一个中断服务程序来处理SysTick中断。这个中断服务程序将在每个1ms的时间间隔内被调用。     实例代码 #include "./SYSTEM/delay/delay.h" static uint16_t g_fac_us = 0; /* us延时倍乘数 */ /** * [url=home.php?mod=space&uid=159083]@brief[/url] 初始化延迟函数 * @param sysclk: 系统时钟频率, 即CPU频率(HCLK),单位 Mhz * @retval 无 */ void delay_init(uint16_t sysclk) { SysTick->CTRL |= (1 << 2); /* SYSTICK使用系统时钟源,频率为HCLK */ g_fac_us = sysclk ; /* 不论是否使用OS,g_fac_us都需要使用,作为1us的基础时基 */ SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; /* 开启SYSTICK */ SysTick->LOAD = 0XFFFFFF; /* 注意systick计数器24位,所以这里设置最大重装载值 */ } /** * @brief 延时nus * @note 无论是否使用OS, 都是用时钟摘取法来做us延时 * @param nus: 要延时的us数. * @note nus取值范围: 0 ~ (2^32 / g_fac_us) (g_fac_us一般等于系统主频, 自行套入计算) * @retval 无 */ void delay_us(uint32_t nus) { uint32_t ticks; uint32_t told, tnow, tcnt = 0; uint32_t reload; reload = SysTick->LOAD; /* LOAD的值 */ ticks = nus * g_fac_us; /* 需要的节拍数 */ told = SysTick->VAL; /* 刚进入时的计数器值 */ while (1) { tnow = SysTick->VAL; if (tnow != told) { if (tnow < told) { tcnt += told - tnow; /* 这里注意一下SYSTICK是一个递减的计数器就可以了. */ } else { tcnt += reload - tnow + told; } told = tnow; if (tcnt >= ticks) { break; /* 时间超过/等于要延迟的时间,则退出. */ } } } } /** * @brief 延时nms * @param nms: 要延时的ms数 (0< nms <= (2^32 / g_fac_us / 1000))(g_fac_us一般等于系统主频, 自行套入计算) * @retval 无 */ void delay_ms(uint16_t nms) { delay_us((uint32_t)(nms * 1000)); /* 普通方式延时 */ }    

  • 发表了主题帖: 【兆易GD32H759I-EVAL】 触摸屏驱动实验

    本帖最后由 尹小舟 于 2024-6-23 22:01 编辑 实验目的  能够读取触摸屏x,yz值   电路接口     XPT2046 芯片 1. 电阻触摸屏与XPT2046 电阻触摸屏通常包含两层导电材料,当触摸屏被按下时,这两层材料会在某个点接触,从而改变其电阻值。XPT2046通过测量这些电阻值的变化来确定触摸点的位置。 2. XPT2046芯片特点 分辨率与转换速率:XPT2046是一款12位的ADC芯片,具有125KHz的转换速率,能够提供高精度的坐标检测。 工作电压:工作电压范围为2.2V~5.25V,支持1.5V~5.25V的数字I/O口。 功能丰富:内建2.5V参考电压源,支持电源电压测量(0V~6V)、内建温度测量功能和触摸压力测量。 通信接口:采用SPI 3线控制通信接口,支持标准SPI接口时序。 功耗与封装:在125KHz转换速率和2.7V电压下的功耗仅为750 µW。封装有QFN-16、TSSOP-16和VFBGA-48等选项。 3. 电阻触摸驱动实现 初始化:在驱动程序中,首先需要对XPT2046进行初始化,设置工作参数,如分辨率、采样率等。 坐标检测:通过执行两次A/D转换来检测触摸屏上被按下的位置。首先,在X+电极施加驱动电压,X-接地,通过Y+或Y-测量电压值来获取X坐标;然后,在Y+电极施加驱动电压,Y-接地,通过X+或X-测量电压值来获取Y坐标。 中断处理:XPT2046支持笔中断输出,该引脚可以配置到单片机的中断脚上。当触摸屏被按下时,会产生中断信号,触发中断处理函数。在中断处理函数中,可以读取触摸屏的坐标数据并进行相应处理。 功耗管理:通过配置XPT2046的工作模式和参考电压等参数,可以降低功耗。例如,在不需要频繁检测触摸事件时,可以关闭参考电压以降低功耗。     实验代码   _m_tp_dev tp_dev = { tp_init, tp_scan, tp_adjust, 0, 0, 0, 0, 0, 0, 0, 0, }; /** * @brief SPI写数据 * @note 向触摸屏IC写入1 byte数据 * @param data: 要写入的数据 * @retval 无 */ static void tp_write_byte(uint8_t data) { uint8_t count = 0; for (count = 0; count < 8; count++) { if (data & 0x80) /* 发送1 */ { T_MOSI(1); } else /* 发送0 */ { T_MOSI(0); } data <<= 1; T_CLK(0); delay_us(1); T_CLK(1); /* 上升沿有效 */ } } /** * @brief SPI读数据 * @note 从触摸屏IC读取adc值 * @param cmd: 指令 * @retval 读取到的数据,ADC值(12bit) */ static uint16_t tp_read_ad(uint8_t cmd) { uint8_t count = 0; uint16_t num = 0; T_CLK(0); /* 先拉低时钟 */ T_MOSI(0); /* 拉低数据线 */ T_CS(0); /* 选中触摸屏IC */ tp_write_byte(cmd); /* 发送命令字 */ delay_us(6); /* ADS7846的转换时间最长为6us */ T_CLK(0); delay_us(1); T_CLK(1); /* 给1个时钟,清除BUSY */ delay_us(1); T_CLK(0); for (count = 0; count < 16; count++) /* 读出16位数据,只有高12位有效 */ { num <<= 1; T_CLK(0); /* 下降沿有效 */ delay_us(1); T_CLK(1); if (T_MISO)num++; } num >>= 4; /* 只有高12位有效. */ T_CS(1); /* 释放片选 */ return num; } /* 电阻触摸驱动芯片 数据采集 滤波用参数 */ #define TP_READ_TIMES 5 /* 读取次数 */ #define TP_LOST_VAL 1 /* 丢弃值 */ /** * @brief 读取一个坐标值(x或者y) * @note 连续读取TP_READ_TIMES次数据,对这些数据升序排列, * 然后去掉最低和最高TP_LOST_VAL个数, 取平均值 * 设置时需满足: TP_READ_TIMES > 2*TP_LOST_VAL 的条件 * * @param cmd : 指令 * @arg 0XD0: 读取X轴坐标(@竖屏状态,横屏状态和Y对调.) * @arg 0X90: 读取Y轴坐标(@竖屏状态,横屏状态和X对调.) * * @retval 读取到的数据(滤波后的), ADC值(12bit) */ static uint16_t tp_read_xoy(uint8_t cmd) { uint16_t i, j; uint16_t buf[TP_READ_TIMES]; uint16_t sum = 0; uint16_t temp; for (i = 0; i < TP_READ_TIMES; i++) /* 先读取TP_READ_TIMES次数据 */ { buf[i] = tp_read_ad(cmd); } for (i = 0; i < TP_READ_TIMES - 1; i++) /* 对数据进行排序 */ { for (j = i + 1; j < TP_READ_TIMES; j++) { if (buf[i] > buf[j]) /* 升序排列 */ { temp = buf[i]; buf[i] = buf[j]; buf[j] = temp; } } } sum = 0; for (i = TP_LOST_VAL; i < TP_READ_TIMES - TP_LOST_VAL; i++) /* 去掉两端的丢弃值 */ { sum += buf[i]; /* 累加去掉丢弃值以后的数据. */ } temp = sum / (TP_READ_TIMES - 2 * TP_LOST_VAL); /* 取平均值 */ return temp; } /** * @brief 读取x, y坐标 * @param x,y: 读取到的坐标值 * @retval 无 */ static void tp_read_xy(uint16_t *x, uint16_t *y) { uint16_t xval, yval; if (tp_dev.touchtype & 0X01) /* X,Y方向与屏幕相反 */ { xval = tp_read_xoy(0X90); /* 读取X轴坐标AD值, 并进行方向变换 */ yval = tp_read_xoy(0XD0); /* 读取Y轴坐标AD值 */ } else /* X,Y方向与屏幕相同 */ { xval = tp_read_xoy(0XD0); /* 读取X轴坐标AD值 */ yval = tp_read_xoy(0X90); /* 读取Y轴坐标AD值 */ } *x = xval; *y = yval; } /* 连续两次读取X,Y坐标的数据误差最大允许值 */ #define TP_ERR_RANGE 50 /* 误差范围 */ /** * @brief 连续读取2次触摸IC数据, 并滤波 * @note 连续2次读取触摸屏IC,且这两次的偏差不能超过ERR_RANGE,满足 * 条件,则认为读数正确,否则读数错误.该函数能大大提高准确度. * * @param x,y: 读取到的坐标值 * @retval 0, 失败; 1, 成功; */ static uint8_t tp_read_xy2(uint16_t *x, uint16_t *y) { uint16_t x1, y1; uint16_t x2, y2; tp_read_xy(&x1, &y1); /* 读取第一次数据 */ tp_read_xy(&x2, &y2); /* 读取第二次数据 */ /* 前后两次采样在+-TP_ERR_RANGE内 */ if (((x2 <= x1 && x1 < x2 + TP_ERR_RANGE) || (x1 <= x2 && x2 < x1 + TP_ERR_RANGE)) && ((y2 <= y1 && y1 < y2 + TP_ERR_RANGE) || (y1 <= y2 && y2 < y1 + TP_ERR_RANGE))) { *x = (x1 + x2) / 2; *y = (y1 + y2) / 2; return 1; } return 0; } /******************************************************************************************/ /* 与LCD部分有关的函数, 用来校准用的 */ /** * @brief 画一个校准用的触摸点(十字架) * @param x,y : 坐标 * @param color : 颜色 * @retval 无 */ static void tp_draw_touch_point(uint16_t x, uint16_t y, uint16_t color) { lcd_draw_line(x - 12, y, x + 13, y, color); /* 横线 */ lcd_draw_line(x, y - 12, x, y + 13, color); /* 竖线 */ lcd_draw_point(x + 1, y + 1, color); lcd_draw_point(x - 1, y + 1, color); lcd_draw_point(x + 1, y - 1, color); lcd_draw_point(x - 1, y - 1, color); lcd_draw_circle(x, y, 6, color); /* 画中心圈 */ } /** * @brief 画一个大点(2*2的点) * @param x,y : 坐标 * @param color : 颜色 * @retval 无 */ void tp_draw_big_point(uint16_t x, uint16_t y, uint16_t color) { lcd_draw_point(x, y, color); /* 中心点 */ lcd_draw_point(x + 1, y, color); lcd_draw_point(x, y + 1, color); lcd_draw_point(x + 1, y + 1, color); } /******************************************************************************************/ /** * @brief 触摸按键扫描 * @param mode: 坐标模式 * @arg 0, 屏幕坐标; * @arg 1, 物理坐标(校准等特殊场合用) * * @retval 0, 触屏无触摸; 1, 触屏有触摸; */ uint8_t tp_scan(uint8_t mode) { if (T_PEN == 0) /* 有按键按下 */ { if (mode) /* 读取物理坐标, 无需转换 */ { tp_read_xy2(&tp_dev.x[0], &tp_dev.y[0]); } else if (tp_read_xy2(&tp_dev.x[0], &tp_dev.y[0])) /* 读取屏幕坐标, 需要转换 */ { /* 将X轴 物理坐标转换成逻辑坐标(即对应LCD屏幕上面的X坐标值) */ tp_dev.x[0] = (signed short)(tp_dev.x[0] - tp_dev.xc) / tp_dev.xfac + lcddev.width / 2; /* 将Y轴 物理坐标转换成逻辑坐标(即对应LCD屏幕上面的Y坐标值) */ tp_dev.y[0] = (signed short)(tp_dev.y[0] - tp_dev.yc) / tp_dev.yfac + lcddev.height / 2; } if ((tp_dev.sta & TP_PRES_DOWN) == 0) /* 之前没有被按下 */ { tp_dev.sta = TP_PRES_DOWN | TP_CATH_PRES; /* 按键按下 */ tp_dev.x[CT_MAX_TOUCH - 1] = tp_dev.x[0]; /* 记录第一次按下时的坐标 */ tp_dev.y[CT_MAX_TOUCH - 1] = tp_dev.y[0]; } } else { if (tp_dev.sta & TP_PRES_DOWN) /* 之前是被按下的 */ { tp_dev.sta &= ~TP_PRES_DOWN; /* 标记按键松开 */ } else /* 之前就没有被按下 */ { tp_dev.x[CT_MAX_TOUCH - 1] = 0; tp_dev.y[CT_MAX_TOUCH - 1] = 0; tp_dev.x[0] = 0xffff; tp_dev.y[0] = 0xffff; } } return tp_dev.sta & TP_PRES_DOWN; /* 返回当前的触屏状态 */ } /* TP_SAVE_ADDR_BASE定义触摸屏校准参数保存在EEPROM里面的位置(起始地址) * 占用空间 : 13字节. */ #define TP_SAVE_ADDR_BASE 40 /** * @brief 保存校准参数 * @note 参数保存在EEPROM芯片里面(24C02),起始地址为TP_SAVE_ADDR_BASE. * 占用大小为13字节 * @param 无 * @retval 无 */ void tp_save_adjust_data(void) { uint8_t *p = (uint8_t *)&tp_dev.xfac; /* 指向首地址 */ /* p指向tp_dev.xfac的地址, p+4则是tp_dev.yfac的地址 * p+8则是tp_dev.xoff的地址,p+10,则是tp_dev.yoff的地址 * 总共占用12个字节(4个参数) * p+12用于存放标记电阻触摸屏是否校准的数据(0X0A) * 往p[12]写入0X0A. 标记已经校准过. */ at24cxx_write(TP_SAVE_ADDR_BASE, p, 12); /* 保存12个字节数据(xfac,yfac,xc,yc) */ at24cxx_write_one_byte(TP_SAVE_ADDR_BASE + 12, 0X0A); /* 保存校准值 */ } /** * @brief 获取保存在EEPROM里面的校准值 * @param 无 * @retval 0,获取失败,要重新校准 * 1,成功获取数据 */ uint8_t tp_get_adjust_data(void) { uint8_t *p = (uint8_t *)&tp_dev.xfac; uint8_t temp = 0; /* 由于我们是直接指向tp_dev.xfac地址进行保存的, 读取的时候,将读取出来的数据 * 写入指向tp_dev.xfac的首地址, 就可以还原写入进去的值, 而不需要理会具体的数 * 据类型. 此方法适用于各种数据(包括结构体)的保存/读取(包括结构体). */ at24cxx_read(TP_SAVE_ADDR_BASE, p, 12); /* 读取12字节数据 */ temp = at24cxx_read_one_byte(TP_SAVE_ADDR_BASE + 12); /* 读取校准状态标记 */ if (temp == 0X0A) { return 1; } return 0; } /* 提示字符串 */ char *const TP_REMIND_MSG_TBL = "Please use the stylus click the cross on the screen.The cross will always move until the screen adjustment is completed."; /** * @brief 提示校准结果(各个参数) * @param xy[5][2]: 5个物理坐标值 * @param px,py : x,y方向的比例因子(约接近1越好) * @retval 无 */ static void tp_adjust_info_show(uint16_t xy[5][2], double px, double py) { uint8_t i; char sbuf[20]; for (i = 0; i < 5; i++) /* 显示5个物理坐标值 */ { sprintf(sbuf, "x%d:%d", i + 1, xy[i][0]); lcd_show_string(40, 160 + (i * 20), lcddev.width, lcddev.height, 16, sbuf, RED); sprintf(sbuf, "y%d:%d", i + 1, xy[i][1]); lcd_show_string(40 + 80, 160 + (i * 20), lcddev.width, lcddev.height, 16, sbuf, RED); } /* 显示X/Y方向的比例因子 */ lcd_fill(40, 160 + (i * 20), lcddev.width - 1, 16, WHITE); /* 清除之前的px,py显示 */ sprintf(sbuf, "px:%0.2f", px); sbuf[7] = 0; /* 添加结束符 */ lcd_show_string(40, 160 + (i * 20), lcddev.width, lcddev.height, 16, sbuf, RED); sprintf(sbuf, "py:%0.2f", py); sbuf[7] = 0; /* 添加结束符 */ lcd_show_string(40 + 80, 160 + (i * 20), lcddev.width, lcddev.height, 16, sbuf, RED); } /** * @brief 触摸屏校准代码 * @note 使用五点校准法(具体原理请百度) * 本函数得到x轴/y轴比例因子xfac/yfac及物理中心坐标值(xc,yc)等4个参数 * 我们规定: 物理坐标即AD采集到的坐标值,范围是0~4095. * 逻辑坐标即LCD屏幕的坐标, 范围为LCD屏幕的分辨率. * * @param 无 * @retval 无 */ void tp_adjust(void) { uint16_t pxy[5][2]; /* 物理坐标缓存值 */ uint8_t cnt = 0; short s1, s2, s3, s4; /* 4个点的坐标差值 */ double px, py; /* X,Y轴物理坐标比例,用于判定是否校准成功 */ uint16_t outtime = 0; cnt = 0; lcd_clear(WHITE); /* 清屏 */ lcd_show_string(40, 40, 160, 100, 16, TP_REMIND_MSG_TBL, RED); /* 显示提示信息 */ tp_draw_touch_point(20, 20, RED); /* 画点1 */ tp_dev.sta = 0; /* 消除触发信号 */ while (1) /* 如果连续10秒钟没有按下,则自动退出 */ { tp_dev.scan(1); /* 扫描物理坐标 */ if ((tp_dev.sta & 0xc000) == TP_CATH_PRES) /* 按键按下了一次(此时按键松开了.) */ { outtime = 0; tp_dev.sta &= ~TP_CATH_PRES; /* 标记按键已经被处理过了. */ pxy[cnt][0] = tp_dev.x[0]; /* 保存X物理坐标 */ pxy[cnt][1] = tp_dev.y[0]; /* 保存Y物理坐标 */ cnt++; switch (cnt) { case 1: tp_draw_touch_point(20, 20, WHITE); /* 清除点1 */ tp_draw_touch_point(lcddev.width - 20, 20, RED); /* 画点2 */ break; case 2: tp_draw_touch_point(lcddev.width - 20, 20, WHITE); /* 清除点2 */ tp_draw_touch_point(20, lcddev.height - 20, RED); /* 画点3 */ break; case 3: tp_draw_touch_point(20, lcddev.height - 20, WHITE); /* 清除点3 */ tp_draw_touch_point(lcddev.width - 20, lcddev.height - 20, RED); /* 画点4 */ break; case 4: lcd_clear(WHITE); /* 画第五个点了, 直接清屏 */ tp_draw_touch_point(lcddev.width / 2, lcddev.height / 2, RED); /* 画点5 */ break; case 5: /* 全部5个点已经得到 */ s1 = pxy[1][0] - pxy[0][0]; /* 第2个点和第1个点的X轴物理坐标差值(AD值) */ s3 = pxy[3][0] - pxy[2][0]; /* 第4个点和第3个点的X轴物理坐标差值(AD值) */ s2 = pxy[3][1] - pxy[1][1]; /* 第4个点和第2个点的Y轴物理坐标差值(AD值) */ s4 = pxy[2][1] - pxy[0][1]; /* 第3个点和第1个点的Y轴物理坐标差值(AD值) */ px = (double)s1 / s3; /* X轴比例因子 */ py = (double)s2 / s4; /* Y轴比例因子 */ if (px < 0)px = -px; /* 负数改正数 */ if (py < 0)py = -py; /* 负数改正数 */ if (px < 0.95 || px > 1.05 || py < 0.95 || py > 1.05 || /* 比例不合格 */ abs(s1) > 4095 || abs(s2) > 4095 || abs(s3) > 4095 || abs(s4) > 4095 || /* 差值不合格, 大于坐标范围 */ abs(s1) == 0 || abs(s2) == 0 || abs(s3) == 0 || abs(s4) == 0 /* 差值不合格, 等于0 */ ) { cnt = 0; tp_draw_touch_point(lcddev.width / 2, lcddev.height / 2, WHITE); /* 清除点5 */ tp_draw_touch_point(20, 20, RED); /* 重新画点1 */ tp_adjust_info_show(pxy, px, py); /* 显示当前信息,方便找问题 */ continue; } tp_dev.xfac = (float)(s1 + s3) / (2 * (lcddev.width - 40)); tp_dev.yfac = (float)(s2 + s4) / (2 * (lcddev.height - 40)); tp_dev.xc = pxy[4][0]; /* X轴,物理中心坐标 */ tp_dev.yc = pxy[4][1]; /* Y轴,物理中心坐标 */ lcd_clear(WHITE); /* 清屏 */ lcd_show_string(35, 110, lcddev.width, lcddev.height, 16, "Touch Screen Adjust OK!", BLUE); /* 校正完成 */ delay_ms(1000); tp_save_adjust_data(); lcd_clear(WHITE);/* 清屏 */ return;/* 校正完成 */ } } delay_ms(10); outtime++; if (outtime > 1000) { tp_get_adjust_data(); break; } } } /** * @brief 触摸屏初始化 * @param 无 * @retval 0,没有进行校准 * 1,进行过校准 */ uint8_t tp_init(void) { tp_dev.touchtype = 1; /* 默认设置(电阻屏 & 竖屏) */ rcu_periph_clock_enable(T_PEN_GPIO_CLK); /* 使能T_PEN脚时钟 */ rcu_periph_clock_enable(T_CS_GPIO_CLK); /* 使能T_CS脚时钟 */ rcu_periph_clock_enable(T_MISO_GPIO_CLK); /* 使能T_MISO脚时钟 */ rcu_periph_clock_enable(T_MOSI_GPIO_CLK); /* 使能T_MOSI脚时钟 */ rcu_periph_clock_enable(T_CLK_GPIO_CLK); /* 使能T_CLK脚时钟 */ gpio_mode_set(T_PEN_GPIO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, T_PEN_GPIO_PIN); /* T_PEN引脚模式设置,上拉输入 */ gpio_mode_set(T_MISO_GPIO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, T_MISO_GPIO_PIN); /* T_MISO引脚模式设置,上拉输入 */ /* 设置T_CLK 引脚推挽输出模式 */ gpio_mode_set(T_CLK_GPIO_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, T_CLK_GPIO_PIN); gpio_output_options_set(T_CLK_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, T_CLK_GPIO_PIN); /* 设置T_CS引脚 推挽输出模式 */ gpio_mode_set(T_CS_GPIO_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, T_CS_GPIO_PIN); gpio_output_options_set(T_CS_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, T_CS_GPIO_PIN); /* 设置T_MOSI引脚 推挽输出模式 */ gpio_mode_set(T_MOSI_GPIO_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, T_MOSI_GPIO_PIN); gpio_output_options_set(T_MOSI_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, T_MOSI_GPIO_PIN); tp_read_xy(&tp_dev.x[0], &tp_dev.y[0]); /* 第一次读取初始化 */ at24cxx_init(); /* 初始化24CXX */ if (tp_get_adjust_data()) { return 0; /* 已经校准 */ } else /* 未校准? */ { lcd_clear(WHITE); /* 清屏 */ tp_adjust(); /* 屏幕校准 */ tp_save_adjust_data(); } tp_get_adjust_data(); return 1; }     #include "gd32h7xx.h" #include "systick.h" #include <stdio.h> #include "gd32h759i_eval.h" #include "gd32h759i_lcd_eval.h" #include "./SYSTEM/delay/delay.h" #include "./BSP/TIMER/timer.h" #include "./BSP/24CXX/24cxx.h" #include "./BSP/LCD/lcd.h" #include "./BSP/TOUCH/touch.h" const uint8_t g_text_buf[] = {"gd32 IIC TEST"}; /* 要写入到 24c02 的字符串数组 */ #define TEXT_SIZE sizeof(g_text_buf) /* TEXT 字符串长度 */ /** * @brief 清空屏幕并在右上角显示"RST" * @param 无 * @retval 无 */ void load_draw_dialog(void) { lcd_clear(WHITE); /* 清屏 */ lcd_show_string(lcddev.width - 24, 0, 200, 16, 16, "RST", BLUE); /* 显示清屏区域 */ } /*! \brief toggle the led every 500ms \param[in] none \param[out] none \retval none */ void led_spark(void) { static __IO uint32_t timingdelaylocal = 0U; if(timingdelaylocal) { if(timingdelaylocal < 500U) { gd_eval_led_on(LED1); } else { gd_eval_led_off(LED1); } timingdelaylocal--; } else { timingdelaylocal = 1000U; } } /*! \brief enable the CPU cache \param[in] none \param[out] none \retval none */ void cache_enable(void) { /* enable i-cache */ SCB_EnableICache(); /* enable d-cache */ SCB_EnableDCache(); } /** * @brief 电阻触摸屏测试函数 * @param 无 * @retval 无 */ void rtp_test(void) { uint8_t key; uint8_t i = 0; gd_eval_key_init(KEY_WAKEUP, KEY_MODE_GPIO); while (1) { tp_dev.scan(0); if (tp_dev.sta & TP_PRES_DOWN) /* 触摸屏被按下 */ { if (tp_dev.x[0] < lcddev.width && tp_dev.y[0] < lcddev.height) { if (tp_dev.x[0] > (lcddev.width - 24) && tp_dev.y[0] < 16) { load_draw_dialog(); /* 清除 */ } else { printf("\r\nx %d y %d", tp_dev.x[0], tp_dev.y[0]); tp_draw_big_point(tp_dev.x[0], tp_dev.y[0], RED); /* 画点 */ } } } else { delay_ms(10); /* 没有按键按下的时候 */ } if(RESET == gd_eval_key_scan(KEY_WAKEUP)) { lcd_clear(WHITE); /* 清屏 */ tp_adjust(); /* 屏幕校准 */ tp_save_adjust_data(); load_draw_dialog(); } } } /*! \brief main function \param[in] none \param[out] none \retval none */ int main(void) { uint8_t x = 0; uint8_t datatemp[TEXT_SIZE]; /* enable the CPU cache */ cache_enable(); /* configure systick */ //systick_config(); delay_init(600); /* initilize the LEDs, USART and key */ gd_eval_led_init(LED1); gd_eval_led_init(LED2); gd_eval_com_init(EVAL_COM); gd_eval_key_init(KEY_WAKEUP, KEY_MODE_GPIO); gd_eval_key_init(KEY_TAMPER, KEY_MODE_GPIO); at24cxx_init(); timerx_int_init(10 - 1, 30000 - 1); printf("\r\nCK_SYS is %d", rcu_clock_freq_get(CK_SYS)); lcd_init(); tp_dev.init(); /* 触摸屏初始化 */ // while(1) // { // tp_dev.scan(1); // printf("\r\nx %d y %d", tp_dev.x[0], tp_dev.y[0]); // // } while (at24cxx_check()) { gd_eval_led_on(LED1); delay_ms(500);lcd_init(); gd_eval_led_off(LED1); delay_ms(500); } lcd_show_string(30, 50, 200, 16, 16, "STM32", RED); lcd_show_string(30, 70, 200, 16, 16, "TOUCH TEST", RED); lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED); if (tp_dev.touchtype != 0XFF) { lcd_show_string(30, 110, 200, 16, 16, "Press KEY0 to Adjust", RED); /* 电阻屏才显示 */ } delay_ms(1500); load_draw_dialog(); rtp_test(); /* 电阻屏测试 */ } /* retarget the C library printf function to the USART */ int fputc(int ch, FILE *f) { usart_data_transmit(EVAL_COM, (uint8_t)ch); while(RESET == usart_flag_get(EVAL_COM, USART_FLAG_TBE)); return ch; }   #include "gd32h7xx.h" #include "systick.h" #include <stdio.h> #include "gd32h759i_eval.h" #include "gd32h759i_lcd_eval.h" #include "./SYSTEM/delay/delay.h" #include "./BSP/TIMER/timer.h" #include "./BSP/24CXX/24cxx.h" #include "./BSP/LCD/lcd.h" #include "./BSP/TOUCH/touch.h" const uint8_t g_text_buf[] = {"gd32 IIC TEST"}; /* 要写入到 24c02 的字符串数组 */ #define TEXT_SIZE sizeof(g_text_buf) /* TEXT 字符串长度 */ /** * @brief 清空屏幕并在右上角显示"RST" * @param 无 * @retval 无 */ void load_draw_dialog(void) { lcd_clear(WHITE); /* 清屏 */ lcd_show_string(lcddev.width - 24, 0, 200, 16, 16, "RST", BLUE); /* 显示清屏区域 */ } /*! \brief toggle the led every 500ms \param[in] none \param[out] none \retval none */ void led_spark(void) { static __IO uint32_t timingdelaylocal = 0U; if(timingdelaylocal) { if(timingdelaylocal < 500U) { gd_eval_led_on(LED1); } else { gd_eval_led_off(LED1); } timingdelaylocal--; } else { timingdelaylocal = 1000U; } } /*! \brief enable the CPU cache \param[in] none \param[out] none \retval none */ void cache_enable(void) { /* enable i-cache */ SCB_EnableICache(); /* enable d-cache */ SCB_EnableDCache(); } /** * @brief 电阻触摸屏测试函数 * @param 无 * @retval 无 */ void rtp_test(void) { uint8_t key; uint8_t i = 0; gd_eval_key_init(KEY_WAKEUP, KEY_MODE_GPIO); while (1) { tp_dev.scan(0); if (tp_dev.sta & TP_PRES_DOWN) /* 触摸屏被按下 */ { if (tp_dev.x[0] < lcddev.width && tp_dev.y[0] < lcddev.height) { if (tp_dev.x[0] > (lcddev.width - 24) && tp_dev.y[0] < 16) { load_draw_dialog(); /* 清除 */ } else { printf("\r\nx %d y %d", tp_dev.x[0], tp_dev.y[0]); tp_draw_big_point(tp_dev.x[0], tp_dev.y[0], RED); /* 画点 */ } } } else { delay_ms(10); /* 没有按键按下的时候 */ } if(RESET == gd_eval_key_scan(KEY_WAKEUP)) { lcd_clear(WHITE); /* 清屏 */ tp_adjust(); /* 屏幕校准 */ tp_save_adjust_data(); load_draw_dialog(); } } } /*! \brief main function \param[in] none \param[out] none \retval none */ int main(void) { uint8_t x = 0; uint8_t datatemp[TEXT_SIZE]; /* enable the CPU cache */ cache_enable(); /* configure systick */ //systick_config(); delay_init(600); /* initilize the LEDs, USART and key */ gd_eval_led_init(LED1); gd_eval_led_init(LED2); gd_eval_com_init(EVAL_COM); gd_eval_key_init(KEY_WAKEUP, KEY_MODE_GPIO); gd_eval_key_init(KEY_TAMPER, KEY_MODE_GPIO); at24cxx_init(); timerx_int_init(10 - 1, 30000 - 1); printf("\r\nCK_SYS is %d", rcu_clock_freq_get(CK_SYS)); lcd_init(); tp_dev.init(); /* 触摸屏初始化 */ // while(1) // { // tp_dev.scan(1); // printf("\r\nx %d y %d", tp_dev.x[0], tp_dev.y[0]); // // } while (at24cxx_check()) { gd_eval_led_on(LED1); delay_ms(500);lcd_init(); gd_eval_led_off(LED1); delay_ms(500); } lcd_show_string(30, 50, 200, 16, 16, "STM32", RED); lcd_show_string(30, 70, 200, 16, 16, "TOUCH TEST", RED); lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED); if (tp_dev.touchtype != 0XFF) { lcd_show_string(30, 110, 200, 16, 16, "Press KEY0 to Adjust", RED); /* 电阻屏才显示 */ } delay_ms(1500); load_draw_dialog(); rtp_test(); /* 电阻屏测试 */ } /* retarget the C library printf function to the USART */ int fputc(int ch, FILE *f) { usart_data_transmit(EVAL_COM, (uint8_t)ch); while(RESET == usart_flag_get(EVAL_COM, USART_FLAG_TBE)); return ch; }   实验现象         

  • 发表了主题帖: 【兆易GD32H759I-EVAL】TLI与LCD显示实现(2)

    本帖最后由 尹小舟 于 2024-6-23 20:02 编辑 本实验移植正点原子LCD驱动   画点函数 void lcd_point_set(uint16_t xpos, uint16_t ypos, uint16_t color) { *(__IO uint16_t*)(current_framebuffer + 2*((LCD_PIXEL_WIDTH*ypos) + xpos)) = color; }   函数定义: 函数名:lcd_point_set 参数: xpos:一个无符号16位整数,表示点的x坐标。 ypos:一个无符号16位整数,表示点的y坐标。 color:一个无符号16位整数,表示要设置的颜色。 返回值:无(void) 函数功能: 根据给定的xpos和ypos,在LCD的当前帧缓冲区(current_framebuffer)中设置指定颜色的点。 参数详解: \param[in] xpos, ypos, color:这些是函数的输入参数,用于传递x位置、y位置和颜色值给函数。 该函数没有返回值(retval)。 函数体: *(__IO uint16_t*)(current_framebuffer + 2*((LCD_PIXEL_WIDTH*ypos) + xpos)) = color; 这行代码执行了实际的点设置操作。 __IO 是一个修饰符,通常用于指示编译器这个变量是“输入/输出”的 current_framebuffer 是一个指向帧缓冲区的指针(这个指针可能在其他地方定义)。帧缓冲区通常用于存储要在LCD上显示的像素数据。 LCD_PIXEL_WIDTH 是一个常量,表示LCD的像素宽度。 通过计算 (LCD_PIXEL_WIDTH*ypos) + xpos,我们可以找到特定点在帧缓冲区中的位置(索引)。但是,因为我们每个像素用2个字节(16位)来表示颜色,所以需要乘以2来获取字节地址。 然后,我们将计算出的地址强制转换为指向uint16_t的指针,并解引用它以写入颜色值。 代码的移植关键是实现LCD的初始化,和实现画点函数   #ifndef __LCD_H #define __LCD_H #include "stdlib.h" #include "gd32h7xx.h" #include "./BSP/LCD/tli.h" /******************************************************************************************/ //设置RGB屏方向 //0为竖屏 1为横屏 #define RGB_DIR 1 /* LCD重要参数集 */ typedef struct { uint16_t width; /* LCD 宽度 */ uint16_t height; /* LCD 高度 */ uint16_t id; /* LCD ID */ uint8_t dir; /* 横屏还是竖屏控制:0,竖屏;1,横屏。 */ uint16_t wramcmd; /* 开始写gram指令 */ uint16_t setxcmd; /* 设置x坐标指令 */ uint16_t setycmd; /* 设置y坐标指令 */ } _lcd_dev; /* LCD参数 */ extern _lcd_dev lcddev; /* 管理LCD重要参数 */ /* LCD的画笔颜色和背景色 */ extern uint32_t g_point_color; /* 默认红色 */ extern uint32_t g_back_color; /* 背景颜色,默认为白色 */ /* LCD背光控制 */ #define LCD_BL(x) do{ x ? \ gpio_bit_write(GPIOJ, GPIO_PIN_10, SET) : \ gpio_bit_write(GPIOJ, GPIO_PIN_10, RESET); \ }while(0) /* LCD复位引脚 */ #define LCD_RST(x) do{ x ? \ gpio_bit_write(GPIOJ, GPIO_PIN_11, SET) : \ gpio_bit_write(GPIOJ, GPIO_PIN_11, RESET); \ }while(0) /* LCD地址结构体 */ typedef struct { volatile uint16_t LCD_REG; volatile uint16_t LCD_RAM; } LCD_TypeDef; /******************************************************************************************/ /* EXMC相关参数 定义 * 注意: 我们默认是通过EXMC块0来连接LCD, 块0有4个片选: EXMC_NE0~3 * */ #define LCD_EXMC_NEX 3 /* 使用EXMC_NE3接LCD_CS,取值范围只能是: 0~3 */ #define LCD_EXMC_AX 10 /* 使用EXMC_A10接LCD_RS,取值范围是: 0 ~ 25 */ /* LCD_BASE的详细计算方法: * 我们一般使用EXMC的块0(BANK0)来驱动TFTLCD液晶屏(MCU屏), 块0地址范围总大小为256MB,均分成4块: * 存储块0(EXMC_NE0)地址范围: 0X6000 0000 ~ 0X63FF FFFF * 存储块1(EXMC_NE1)地址范围: 0X6400 0000 ~ 0X67FF FFFF * 存储块2(EXMC_NE2)地址范围: 0X6800 0000 ~ 0X6BFF FFFF * 存储块3(EXMC_NE3)地址范围: 0X6C00 0000 ~ 0X6FFF FFFF * * 我们需要根据硬件连接方式选择合适的片选(连接LCD_CS)和地址线(连接LCD_RS) * 开发板使用EXMC_NE3连接LCD_CS, EXMC_A10连接LCD_RS ,16位数据线,计算方法如下: * 首先EXMC_NE3的基地址为: 0X6C00 0000; NEx的基址为(x=0/1/2/3): 0X6000 0000 + (0X400 0000 * x) * EXMC_A10对应地址值: 2^10 * 2 = 0X800; EXMC_Ay对应的地址为(y = 0 ~ 25): 2^y * 2 * * LCD->LCD_REG,对应LCD_RS = 0(LCD寄存器); LCD->LCD_RAM,对应LCD_RS = 1(LCD数据) * 则 LCD->LCD_RAM的地址为: 0X6C00 0000 + 2^10 * 2 = 0X6C00 0800 * LCD->LCD_REG的地址可以为 LCD->LCD_RAM之外的任意地址. * 由于我们使用结构体管理LCD_REG 和 LCD_RAM(REG在前,RAM在后,均为16位数据宽度) * 因此 结构体的基地址(LCD_BASE) = LCD_RAM - 2 = 0X6C00 0800 -2 * * 更加通用的计算公式为((片选脚EXMC_NEx)x=0/1/2/3, (RS接地址线EXMC_Ay)y=0~25): * LCD_BASE = (0X6000 0000 + (0X400 0000 * x)) | (2^y * 2 -2) * 等效于(使用移位操作) * LCD_BASE = (0X6000 0000 + (0X400 0000 * x)) | ((1 << y) * 2 -2) */ #define LCD_BASE (uint32_t)((0X60000000 + (0X4000000 * LCD_EXMC_NEX)) | (((1 << LCD_EXMC_AX) * 2) -2)) #define LCD ((LCD_TypeDef *) LCD_BASE) /******************************************************************************************/ /* LCD扫描方向和颜色 定义 */ /* 扫描方向定义 */ #define L2R_U2D 0 /* 从左到右,从上到下 */ #define L2R_D2U 1 /* 从左到右,从下到上 */ #define R2L_U2D 2 /* 从右到左,从上到下 */ #define R2L_D2U 3 /* 从右到左,从下到上 */ #define U2D_L2R 4 /* 从上到下,从左到右 */ #define U2D_R2L 5 /* 从上到下,从右到左 */ #define D2U_L2R 6 /* 从下到上,从左到右 */ #define D2U_R2L 7 /* 从下到上,从右到左 */ #define DFT_SCAN_DIR L2R_U2D /* 默认的扫描方向 */ #if TLI_PIXFORMAT == TLI_PIXFORMAT_RGB565 /* 常用画笔颜色 */ #define WHITE 0xFFFF /* 白色 */ #define BLACK 0x0000 /* 黑色 */ #define RED 0xF800 /* 红色 */ #define GREEN 0x07E0 /* 绿色 */ #define BLUE 0x001F /* 蓝色 */ #define MAGENTA 0XF81F /* 品红色/紫红色 = BLUE + RED */ #define YELLOW 0XFFE0 /* 黄色 = GREEN + RED */ #define CYAN 0X07FF /* 青色 = GREEN + BLUE */ /* 非常用颜色 */ #define BROWN 0XBC40 /* 棕色 */ #define BRRED 0XFC07 /* 棕红色 */ #define GRAY 0X8430 /* 灰色 */ #define DARKBLUE 0X01CF /* 深蓝色 */ #define LIGHTBLUE 0X7D7C /* 浅蓝色 */ #define GRAYBLUE 0X5458 /* 灰蓝色 */ #define LIGHTGREEN 0X841F /* 浅绿色 */ #define LGRAY 0XC618 /* 浅灰色(PANNEL),窗体背景色 */ #define LGRAYBLUE 0XA651 /* 浅灰蓝色(中间层颜色) */ #define LBBLUE 0X2B12 /* 浅棕蓝色(选择条目的反色) */ #elif TLI_PIXFORMAT == TLI_PIXFORMAT_RGB888 /* 常用画笔颜色 */ #define WHITE 0xFFFFFF /* 白色 */ #define BLACK 0x000000 /* 黑色 */ #define RED 0xFF0000 /* 红色 */ #define GREEN 0x00FF00 /* 绿色 */ #define BLUE 0x0000FF /* 蓝色 */ #define MAGENTA 0XFF00FF /* 品红色/紫红色 = BLUE + RED */ #define YELLOW 0XFFFF00 /* 黄色 = GREEN + RED */ #define CYAN 0X00FFFF /* 青色 = GREEN + BLUE */ /* 非常用颜色 */ #define BROWN 0xB88800 /* 棕色 */ #define BRRED 0XFA8038 /* 棕红色 */ #define GRAY 0X808080 /* 灰色 */ #define DARKBLUE 0X00383C /* 深蓝色 */ #define LIGHTBLUE 0X3CACE0 /* 浅蓝色 */ #define GRAYBLUE 0X5088C0 /* 灰蓝色 */ #define LIGHTGREEN 0X8080FA /* 浅绿色 */ #define LGRAY 0XC0C0C0 /* 浅灰色(PANNEL),窗体背景色 */ #define LGRAYBLUE 0XA0C888 /* 浅灰蓝色(中间层颜色) */ #define LBBLUE 0x286090 /* 浅棕蓝色(选择条目的反色) */ //#define BROWN 0xA52A2A /* 棕色 */ //#define BRRED 0XFA8072 /* 棕红色 */ //#define GRAY 0X808080 /* 灰色 */ //#define DARKBLUE 0X003399 /* 深蓝色 */ //#define LIGHTBLUE 0XADD8E6 /* 浅蓝色 */ //#define GRAYBLUE 0X7AB8CC /* 灰蓝色 */ //#define LIGHTGREEN 0X90EE90 /* 浅绿色 */ //#define LGRAY 0XC0C0C0 /* 浅灰色(PANNEL),窗体背景色 */ //#define LGRAYBLUE 0X778899 /* 浅灰蓝色(中间层颜色) */ //#define LBBLUE 0x004D99 /* 浅棕蓝色(选择条目的反色) */ #else #define WHITE 0xFFFFFFFF /* 白色 */ #define BLACK 0xFF000000 /* 黑色 */ #define RED 0xFFFF0000 /* 红色 */ #define GREEN 0xFF00FF00 /* 绿色 */ #define BLUE 0xFF0000FF /* 蓝色 */ #define MAGENTA 0XFFFF00FF /* 品红色/紫红色 = BLUE + RED */ #define YELLOW 0XFFFFFF00 /* 黄色 = GREEN + RED */ #define CYAN 0XFF00FFFF /* 青色 = GREEN + BLUE */ #define BROWN 0xFFB88800 /* 棕色 */ #define BRRED 0XFFFA8038 /* 棕红色 */ #define GRAY 0XFF808080 /* 灰色 */ #define DARKBLUE 0XFF00383C /* 深蓝色 */ #define LIGHTBLUE 0XFF3CACE0 /* 浅蓝色 */ #define GRAYBLUE 0XFF5088C0 /* 灰蓝色 */ #define LIGHTGREEN 0XFF8080FA /* 浅绿色 */ #define LGRAY 0XFFC0C0C0 /* 浅灰色(PANNEL),窗体背景色 */ #define LGRAYBLUE 0XFFA0C888 /* 浅灰蓝色(中间层颜色) */ #define LBBLUE 0xFF286090 /* 浅棕蓝色(选择条目的反色) */ #define color16M_black 0xFF000000 #define color16M_white 0xFFFFFFFF #define color16M_red 0x00FF0000 #define color16M_green 0x0000FF00 #define color16M_blue 0x000000FF #define color16M_blue2 0xFF00BFFF #define color16M_yellow color16M_red|color16M_green #define color16M_cyan color16M_green|color16M_blue #define color16M_magenta color16M_red|color16M_blue #endif /******************************************************************************************/ /* 函数声明 */ void lcd_init(void); /* 初始化LCD */ void lcd_display_on(void); /* 开显示 */ void lcd_display_off(void); /* 关显示 */ void lcd_display_dir(uint8_t dir); /* 设置屏幕显示方向 */ uint16_t lcd_read_point(uint16_t x, uint16_t y); /* 读点(32位颜色,兼容TLI) */ void lcd_draw_point(uint16_t x, uint16_t y, uint32_t color); /* 画点(32位颜色,兼容TLI) */ void lcd_clear(uint16_t color); /* LCD清屏 */ void lcd_fill_circle(uint16_t x, uint16_t y, uint16_t r, uint32_t color); /* 填充实心圆 */ void lcd_draw_circle(uint16_t x0, uint16_t y0, uint8_t r, uint32_t color); /* 画圆 */ void lcd_draw_hline(uint16_t x, uint16_t y, uint16_t len, uint32_t color); /* 画水平线 */ void lcd_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint32_t color); /* 纯色填充矩形(32位颜色,兼容TLI) */ void lcd_color_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t *color); /* 彩色填充矩形 */ void lcd_draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint32_t color); /* 画直线 */ void lcd_draw_rectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint32_t color); /* 画矩形 */ void lcd_show_char(uint16_t x, uint16_t y, char chr, uint8_t size, uint8_t mode, uint32_t color); /* 显示一个字符 */ void lcd_show_num(uint16_t x, uint16_t y, uint32_t num, uint8_t len, uint8_t size, uint32_t color); /* 显示数字 */ void lcd_show_xnum(uint16_t x, uint16_t y, uint32_t num, uint8_t len, uint8_t size, uint8_t mode, uint32_t color); /* 扩展显示数字 */ void lcd_show_string(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t size, char *p, uint32_t color); /* 显示字符串 */ #endif     #include "stdlib.h" #include "./BSP/LCD/lcd.h" #include "./BSP/LCD/lcdfont.h" #include "gd32h759i_lcd_eval.h" #include "gd32h759i_eval_exmc_sdram.h" /* LCD的画笔颜色和背景色 */ uint32_t g_point_color = RED; /* 画笔颜色 */ uint32_t g_back_color = 0XFFFFFFFF; /* 背景色 */ /* 管理LCD重要参数 */ _lcd_dev lcddev; #define LCD_FRAME_BUFFER ((uint32_t)0xC0000000) #define BUFFER_OFFSET ((uint32_t)0x7F800) //static font_struct *current_font; static uint16_t current_textcolor = 0x0000; static uint16_t current_backcolor = 0xFFFF; static uint32_t current_framebuffer = LCD_FRAME_BUFFER; static uint32_t current_layer = LCD_LAYER_BACKGROUND; static void lcd_char_draw(uint16_t xpos, uint16_t ypos, const uint16_t *c); static void lcd_vertical_char_draw(uint16_t xpos, uint16_t ypos, const uint16_t *c); static void pixel_set(int16_t x, int16_t y); #define HORIZONTAL_SYNCHRONOUS_PULSE 41 #define HORIZONTAL_BACK_PORCH 2 #define ACTIVE_WIDTH 480 #define HORIZONTAL_FRONT_PORCH 2 #define VERTICAL_SYNCHRONOUS_PULSE 10 #define VERTICAL_BACK_PORCH 2 #define ACTIVE_HEIGHT 272 #define VERTICAL_FRONT_PORCH 2 /*! \brief enable the LCD layer0 or layer1 \param[in] layer: LCD layer \arg LCD_LAYER_BACKGROUND \arg LCD_LAYER_FOREGROUND \param[out] none \retval none */ void lcd_layer_enable(uint32_t layer) { if(LCD_LAYER_BACKGROUND == layer){ tli_layer_enable(LAYER0); }else if(LCD_LAYER_FOREGROUND == layer){ tli_layer_enable(LAYER1); } tli_enable(); } /*! \brief set the LCD layer \param[in] layer: LCD layer \arg LCD_LAYER_BACKGROUND \arg LCD_LAYER_FOREGROUND \param[out] none \retval none */ void lcd_layer_set(uint32_t layer) { if(LCD_LAYER_BACKGROUND == layer){ current_framebuffer = LCD_FRAME_BUFFER; current_layer = LCD_LAYER_BACKGROUND; }else{ current_framebuffer = LCD_FRAME_BUFFER + BUFFER_OFFSET; current_layer = LCD_LAYER_FOREGROUND; } } /*! \brief set the transparency of LCD \param[in] trans: transparency of LCD, from 0 to 255 \param[out] none \retval none */ void lcd_transparency_set(uint8_t trans) { if (LCD_LAYER_BACKGROUND == current_layer){ TLI_LXSA(LAYER0) &= ~(TLI_LXSA_SA); TLI_LXSA(LAYER0) = trans; }else{ TLI_LXSA(LAYER1) &= ~(TLI_LXSA_SA); TLI_LXSA(LAYER1) = trans; } tli_reload_config(TLI_REQUEST_RELOAD_EN); } /*! \brief initialize TLI layer0 or layer1 \param[in] layer: LCD layer \arg LCD_LAYER_BACKGROUND \arg LCD_LAYER_FOREGROUND \param[in] width: width of the window \param[in] height: height of the window \param[out] none \retval none */ void lcd_layer_init(uint32_t layer, uint32_t width, uint32_t height) { tli_layer_parameter_struct tli_layer_init_struct; if(LCD_LAYER_BACKGROUND == layer){ /* TLI layer0 configuration */ tli_layer_init_struct.layer_window_leftpos = (HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH); tli_layer_init_struct.layer_window_rightpos = (width + HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH - 1); tli_layer_init_struct.layer_window_toppos = (VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH); tli_layer_init_struct.layer_window_bottompos = (height + VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH - 1); tli_layer_init_struct.layer_ppf = LAYER_PPF_RGB565; tli_layer_init_struct.layer_sa = 0xFF; tli_layer_init_struct.layer_default_blue = 0xFF; tli_layer_init_struct.layer_default_green = 0xFF; tli_layer_init_struct.layer_default_red = 0xFF; tli_layer_init_struct.layer_default_alpha = 0xFF; tli_layer_init_struct.layer_acf1 = LAYER_ACF1_PASA; tli_layer_init_struct.layer_acf2 = LAYER_ACF2_PASA; tli_layer_init_struct.layer_frame_bufaddr = LCD_FRAME_BUFFER; tli_layer_init_struct.layer_frame_line_length = ((width * 2) + 3); tli_layer_init_struct.layer_frame_buf_stride_offset = (width * 2); tli_layer_init_struct.layer_frame_total_line_number = height; tli_layer_init(LAYER0, &tli_layer_init_struct); }else if(LCD_LAYER_FOREGROUND == layer){ /* TLI layer1 configuration */ tli_layer_init_struct.layer_window_leftpos = (HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH); tli_layer_init_struct.layer_window_rightpos = (width + HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH - 1); tli_layer_init_struct.layer_window_toppos = (VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH); tli_layer_init_struct.layer_window_bottompos = (height + VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH - 1); tli_layer_init_struct.layer_ppf = LAYER_PPF_RGB565; tli_layer_init_struct.layer_sa = 0xFF; tli_layer_init_struct.layer_default_blue = 0xFF; tli_layer_init_struct.layer_default_green = 0xFF; tli_layer_init_struct.layer_default_red = 0xFF; tli_layer_init_struct.layer_default_alpha = 0x0; tli_layer_init_struct.layer_acf1 = LAYER_ACF1_PASA; tli_layer_init_struct.layer_acf2 = LAYER_ACF2_PASA; tli_layer_init_struct.layer_frame_bufaddr = LCD_FRAME_BUFFER + BUFFER_OFFSET; tli_layer_init_struct.layer_frame_line_length = ((width * 2) + 3); tli_layer_init_struct.layer_frame_buf_stride_offset = (width * 2); tli_layer_init_struct.layer_frame_total_line_number = height; tli_layer_init(LAYER1, &tli_layer_init_struct); } tli_reload_config(TLI_REQUEST_RELOAD_EN); // lcd_font_set(&LCD_DEFAULT_FONT); } /** * @brief 初始化LCD * @note 该初始化函数可以初始化各种型号的LCD(详见本.c文件最前面的描述) * * @param 无 * @retval 无 */ void lcd_init(void) { tli_parameter_struct tli_init_struct; lcddev.width = 480; lcddev.height = 272; /* enable GPIO clock */ rcu_periph_clock_enable(RCU_GPIOA); rcu_periph_clock_enable(RCU_GPIOB); rcu_periph_clock_enable(RCU_GPIOC); rcu_periph_clock_enable(RCU_GPIOD); rcu_periph_clock_enable(RCU_GPIOE); rcu_periph_clock_enable(RCU_GPIOF); rcu_periph_clock_enable(RCU_GPIOH); rcu_periph_clock_enable(RCU_GPIOG); /* configure HSYNC(PE15), VSYNC(PA7), PCLK(PG7) */ gpio_af_set(GPIOE, GPIO_AF_14, GPIO_PIN_15); gpio_af_set(GPIOA, GPIO_AF_14, GPIO_PIN_7); gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_7); gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_15); gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_15); gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7); gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_7); gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7); gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_7); /* configure LCD_R7(PG6), LCD_R6(PH12), LCD_R5(PH11), LCD_R4(PA5), LCD_R3(PH9),LCD_R2(PH8), LCD_R1(PH3), LCD_R0(PH2) */ gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_6); gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_12); gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_11); gpio_af_set(GPIOA, GPIO_AF_14, GPIO_PIN_5); gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_9); gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_8); gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_3); gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_2); gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6); gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_6); gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12); gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_12); gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11); gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_11); gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5); gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_5); gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9); gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_9); gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8); gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_8); gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3); gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_3); gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2); gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_2); /* configure LCD_G7(PD3), LCD_G6(PC7), LCD_G5(PC1), LCD_G4(PH15), LCD_G3(PH14), LCD_G2(PH13),LCD_G1(PB0), LCD_G0(PB1) */ gpio_af_set(GPIOD, GPIO_AF_14, GPIO_PIN_3); gpio_af_set(GPIOC, GPIO_AF_14, GPIO_PIN_7); gpio_af_set(GPIOC, GPIO_AF_14, GPIO_PIN_1); gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_15); gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_14); gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_13); gpio_af_set(GPIOB, GPIO_AF_14, GPIO_PIN_0); gpio_af_set(GPIOB, GPIO_AF_14, GPIO_PIN_1); gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3); gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_3); gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7); gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_7); gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1); gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_1); gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_15); gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_15); gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14); gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_14); gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13); gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_13); gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_0); gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_0); gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1); gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_1); /* configure LCD_B7(PB9), LCD_B6(PB8), LCD_B5(PB5), LCD_B4(PC11), LCD_B3(PG11),LCD_B2(PG10), LCD_B1(PG12), LCD_B0(PG14) */ gpio_af_set(GPIOB, GPIO_AF_14, GPIO_PIN_9); gpio_af_set(GPIOB, GPIO_AF_14, GPIO_PIN_8); gpio_af_set(GPIOB, GPIO_AF_3, GPIO_PIN_5); gpio_af_set(GPIOC, GPIO_AF_14, GPIO_PIN_11); gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_11); gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_10); gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_12); gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_14); gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9); gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_9); gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8); gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_8); gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5); gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_5); gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11); gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_11); gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11); gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_11); gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10); gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_10); gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12); gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_12); gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14); gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_14); /* configure LCD_DE(PF10) */ gpio_af_set(GPIOF, GPIO_AF_14, GPIO_PIN_10); gpio_mode_set(GPIOF, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10); gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_10); /* LCD PWM BackLight(PG13) */ gpio_mode_set(GPIOG, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_PIN_13); gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_13); gpio_bit_set(GPIOG, GPIO_PIN_13); rcu_periph_clock_enable(RCU_TLI); /* configure PLL2 to generate TLI clock 25MHz/25*192/3 = 64MHz*/ rcu_pll_input_output_clock_range_config(IDX_PLL1,RCU_PLL1RNG_1M_2M,RCU_PLL1VCO_192M_836M); if(ERROR == rcu_pll2_config(25,192,3,3,3)){ while(1){ } } rcu_pll_clock_output_enable(RCU_PLL2R); rcu_tli_clock_div_config(RCU_PLL2R_DIV8); rcu_osci_on(RCU_PLL2_CK); if(ERROR == rcu_osci_stab_wait(RCU_PLL2_CK)){ while(1){ } } /* configure the EXMC access mode */ exmc_synchronous_dynamic_ram_init(EXMC_SDRAM_DEVICE0); /* TLI initialization */ tli_init_struct.signalpolarity_hs = TLI_HSYN_ACTLIVE_LOW; tli_init_struct.signalpolarity_vs = TLI_VSYN_ACTLIVE_LOW; tli_init_struct.signalpolarity_de = TLI_DE_ACTLIVE_LOW; tli_init_struct.signalpolarity_pixelck = TLI_PIXEL_CLOCK_INVERTEDTLI; /* LCD display timing configuration */ tli_init_struct.synpsz_hpsz = HORIZONTAL_SYNCHRONOUS_PULSE - 1; tli_init_struct.synpsz_vpsz = VERTICAL_SYNCHRONOUS_PULSE - 1; tli_init_struct.backpsz_hbpsz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH - 1; tli_init_struct.backpsz_vbpsz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH - 1; tli_init_struct.activesz_hasz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH + ACTIVE_WIDTH - 1; tli_init_struct.activesz_vasz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH + ACTIVE_HEIGHT - 1; tli_init_struct.totalsz_htsz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH + ACTIVE_WIDTH + HORIZONTAL_FRONT_PORCH - 1; tli_init_struct.totalsz_vtsz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH + ACTIVE_HEIGHT + VERTICAL_FRONT_PORCH - 1; /* LCD background color configure*/ tli_init_struct.backcolor_red = 0xFF; tli_init_struct.backcolor_green = 0xFF; tli_init_struct.backcolor_blue = 0xFF; tli_init(&tli_init_struct); lcd_layer_init(LCD_LAYER_BACKGROUND, LCD_PIXEL_WIDTH, LCD_PIXEL_HEIGHT); lcd_layer_init(LCD_LAYER_FOREGROUND, LCD_PIXEL_WIDTH, LCD_PIXEL_HEIGHT); tli_layer_enable(LAYER0); tli_layer_enable(LAYER1); tli_enable(); lcd_layer_set(LCD_LAYER_BACKGROUND); lcd_transparency_set(255); lcd_clear(LCD_COLOR_WHITE); lcd_layer_set(LCD_LAYER_FOREGROUND); lcd_transparency_set(0); lcd_clear(LCD_COLOR_WHITE); lcd_layer_set(LCD_LAYER_BACKGROUND); lcd_clear(WHITE); /* 清屏 */ } /*! \brief configure the packeted pixel format \param[in] pixel_format: pixel format \arg LAYER_PPF_ARGB8888 \arg LAYER_PPF_RGB888 \arg LAYER_PPF_RGB565 \arg LAYER_PPF_ARGB1555 \arg LAYER_PPF_ARGB4444 \arg LAYER_PPF_L8 \arg LAYER_PPF_AL44 \arg LAYER_PPF_AL88 \param[out] none \retval none */ void lcd_pixel_format_config(uint32_t pixel_format) { if(LCD_LAYER_BACKGROUND == current_layer){ TLI_LXPPF(LAYER0) &= ~(TLI_LXPPF_PPF); TLI_LXPPF(LAYER0) = pixel_format; }else{ TLI_LXPPF(LAYER1) &= ~(TLI_LXPPF_PPF); TLI_LXPPF(LAYER1) = pixel_format; } } /*! \brief configure the frame buffer base address \param[in] address: frame buffer base address \param[out] none \retval none */ void lcd_address_config(uint32_t address) { if (LCD_LAYER_BACKGROUND == current_layer){ TLI_LXFBADDR(LAYER0) &= ~(TLI_LXFBADDR_FBADD); TLI_LXFBADDR(LAYER0) = address; }else{ TLI_LXFBADDR(LAYER1) &= ~(TLI_LXFBADDR_FBADD); TLI_LXFBADDR(LAYER1) = address; } tli_reload_config(TLI_REQUEST_RELOAD_EN); } /** * @brief 读取某个点的颜色值 * @param x,y:坐标 * @retval 此点的颜色(32位颜色,方便兼容TLI) */ uint16_t lcd_read_point(uint16_t x, uint16_t y) { return *(__IO uint16_t*)(current_framebuffer + 2*((LCD_PIXEL_WIDTH*y) + x)); } /** * @brief LCD开启显示 * @param 无 * @retval 无 */ void lcd_display_on(void) { tli_enable(); /* 使能TLI */ } /** * @brief LCD关闭显示 * @param 无 * @retval 无 */ void lcd_display_off(void) { tli_disable(); /* 禁能TLI */ } /** * @brief 画点 * @param x,y: 坐标 * @param color: 点的颜色(32位颜色,方便兼容TLI) * @retval 无 */ void lcd_draw_point(uint16_t x, uint16_t y, uint32_t color) { *(__IO uint16_t*)(current_framebuffer + 2*((LCD_PIXEL_WIDTH*y) + x)) = color; } /** * @brief 设置LCD显示方向 * @param dir:0,竖屏; 1,横屏 * @retval 无 */ void lcd_display_dir(uint8_t dir) { } /*! \brief clear the LCD with specified color \param[in] color: LCD color \arg LCD_COLOR_WHITE \arg LCD_COLOR_BLACK \arg LCD_COLOR_GREY \arg LCD_COLOR_BLUE \arg LCD_COLOR_BLUE2 \arg LCD_COLOR_RED \arg LCD_COLOR_MAGENTA \arg LCD_COLOR_GREEN \arg LCD_COLOR_CYAN \arg LCD_COLOR_YELLOW \param[out] none \retval none */ void lcd_clear(uint16_t color) { uint32_t index = 0; for (index = 0x00; index < BUFFER_OFFSET; index++){ *(__IO uint16_t*)(current_framebuffer + (2*index)) = color; } } /** * @brief 在指定区域内填充单个颜色 * @param (sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex - sx + 1) * (ey - sy + 1) * @param color: 要填充的颜色(32位颜色,方便兼容TLI) * @retval 无 */ void lcd_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint32_t color) { uint16_t i, j; uint16_t xlen = 0; for (i = sy; i <= ey; i++) { for (j = sx; j < ex; j++) { lcd_draw_point(j, i, color); } } } /** * @brief 在指定区域内填充指定颜色块 * @param (sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex - sx + 1) * (ey - sy + 1) * @param color: 要填充的颜色数组首地址 * @retval 无 */ void lcd_color_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t *color) { uint16_t height, width; uint16_t i, j; width = ex - sx + 1; /* 得到填充的宽度 */ height = ey - sy + 1; /* 高度 */ for (i = 0; i < height; i++) { for (j = 0; j < width; j++) { lcd_draw_point(sx+j, sy+i, color[i * width + j]); } } } /** * @brief 画线 * @param x1,y1: 起点坐标 * @param x2,y2: 终点坐标 * @param color: 线的颜色 * @retval 无 */ void lcd_draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint32_t color) { uint16_t t; int xerr = 0, yerr = 0, delta_x, delta_y, distance; int incx, incy, row, col; delta_x = x2 - x1; /* 计算坐标增量 */ delta_y = y2 - y1; row = x1; col = y1; if (delta_x > 0) incx = 1; /* 设置单步方向 */ else if (delta_x == 0) incx = 0; /* 垂直线 */ else { incx = -1; delta_x = -delta_x; } if (delta_y > 0) incy = 1; else if (delta_y == 0) incy = 0; /* 水平线 */ else { incy = -1; delta_y = -delta_y; } if ( delta_x > delta_y) distance = delta_x; /* 选取基本增量坐标轴 */ else distance = delta_y; for (t = 0; t <= distance + 1; t++ ) /* 画线输出 */ { lcd_draw_point(row, col, color); /* 画点 */ xerr += delta_x ; yerr += delta_y ; if (xerr > distance) { xerr -= distance; row += incx; } if (yerr > distance) { yerr -= distance; col += incy; } } } /** * @brief 画水平线 * @param x,y : 起点坐标 * @param len : 线长度 * @param color: 矩形的颜色 * @retval 无 */ void lcd_draw_hline(uint16_t x, uint16_t y, uint16_t len, uint32_t color) { if ((len == 0) || (x > lcddev.width) || (y > lcddev.height)) return; lcd_fill(x, y, x + len - 1, y, color); } /** * @brief 画矩形 * @param x1,y1: 起点坐标 * @param x2,y2: 终点坐标 * @param color: 矩形的颜色 * @retval 无 */ void lcd_draw_rectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint32_t color) { lcd_draw_line(x1, y1, x2, y1, color); lcd_draw_line(x1, y1, x1, y2, color); lcd_draw_line(x1, y2, x2, y2, color); lcd_draw_line(x2, y1, x2, y2, color); } /** * @brief 画圆 * @param x0,y0: 圆中心坐标 * @param r : 半径 * @param color: 圆的颜色 * @retval 无 */ void lcd_draw_circle(uint16_t x0, uint16_t y0, uint8_t r, uint32_t color) { int a, b; int di; a = 0; b = r; di = 3 - (r << 1); /* 判断下个点位置的标志 */ while (a <= b) { lcd_draw_point(x0 + a, y0 - b, color); /* 5 */ lcd_draw_point(x0 + b, y0 - a, color); /* 0 */ lcd_draw_point(x0 + b, y0 + a, color); /* 4 */ lcd_draw_point(x0 + a, y0 + b, color); /* 6 */ lcd_draw_point(x0 - a, y0 + b, color); /* 1 */ lcd_draw_point(x0 - b, y0 + a, color); lcd_draw_point(x0 - a, y0 - b, color); /* 2 */ lcd_draw_point(x0 - b, y0 - a, color); /* 7 */ a++; /* 使用Bresenham算法画圆 */ if (di < 0) { di += 4 * a + 6; } else { di += 10 + 4 * (a - b); b--; } } } /** * @brief 填充实心圆 * @param x,y : 圆中心坐标 * @param r : 半径 * @param color: 圆的颜色 * @retval 无 */ void lcd_fill_circle(uint16_t x, uint16_t y, uint16_t r, uint32_t color) { uint32_t i; uint32_t imax = ((uint32_t)r * 707) / 1000 + 1; uint32_t sqmax = (uint32_t)r * (uint32_t)r + (uint32_t)r / 2; uint32_t xr = r; lcd_draw_hline(x - r, y, 2 * r, color); for (i = 1; i <= imax; i++) { if ((i * i + xr * xr) > sqmax) { /* draw lines from outside */ if (xr > imax) { lcd_draw_hline (x - i + 1, y + xr, 2 * (i - 1), color); lcd_draw_hline (x - i + 1, y - xr, 2 * (i - 1), color); } xr--; } /* draw lines from inside (center) */ lcd_draw_hline(x - xr, y + i, 2 * xr, color); lcd_draw_hline(x - xr, y - i, 2 * xr, color); } } /** * @brief 在指定位置显示一个字符 * @param x,y : 坐标 * @param chr : 要显示的字符:' '--->'~' * @param size : 字体大小 12/16/24/32 * @param mode : 叠加方式(1); 非叠加方式(0); * @param color : 字符的颜色; * @retval 无 */ void lcd_show_char(uint16_t x, uint16_t y, char chr, uint8_t size, uint8_t mode, uint32_t color) { uint8_t temp, t1, t; uint16_t y0 = y; uint8_t csize = 0; uint8_t *pfont = 0; csize = (size / 8 + ((size % 8) ? 1 : 0)) * (size / 2); /* 得到字体一个字符对应点阵集所占的字节数 */ chr = chr - ' '; /* 得到偏移后的值(ASCII字库是从空格开始取模,所以-' '就是对应字符的字库) */ switch (size) { case 12: pfont = (uint8_t *)asc2_1206[chr]; /* 调用1206字体 */ break; case 16: pfont = (uint8_t *)asc2_1608[chr]; /* 调用1608字体 */ break; case 24: pfont = (uint8_t *)asc2_2412[chr]; /* 调用2412字体 */ break; case 32: pfont = (uint8_t *)asc2_3216[chr]; /* 调用3216字体 */ break; default: return ; } for (t = 0; t < csize; t++) { temp = pfont[t]; /* 获取字符的点阵数据 */ for (t1 = 0; t1 < 8; t1++) /* 一个字节8个点 */ { if (temp & 0x80) /* 有效点,需要显示 */ { lcd_draw_point(x, y, color); /* 画点出来,要显示这个点 */ } else if (mode == 0) /* 无效点并且选择非叠加方式 */ { lcd_draw_point(x, y, g_back_color); /* 画背景色,相当于这个点不显示(注意背景色由全局变量控制) */ } temp <<= 1; /* 移位, 以便获取下一个位的状态 */ y++; if (y >= lcddev.height) return; /* 超区域了 */ if ((y - y0) == size) /* 显示完一列了? */ { y = y0; /* y坐标复位 */ x++; /* x坐标递增 */ if (x >= lcddev.width) return; /* x坐标超区域了 */ break; } } } } /** * @brief 平方函数, m^n * @param m: 底数 * @param n: 指数 * @retval m的n次方 */ static uint32_t lcd_pow(uint8_t m, uint8_t n) { uint32_t result = 1; while (n--) result *= m; return result; } /** * @brief 显示len个数字(高位为0则不显示) * @param x,y : 起始坐标 * @param num : 数值(0 ~ 2^32) * @param len : 显示数字的位数 * @param size : 选择字体 12/16/24/32 * @param color : 数字的颜色; * @retval 无 */ void lcd_show_num(uint16_t x, uint16_t y, uint32_t num, uint8_t len, uint8_t size, uint32_t color) { uint8_t t, temp; uint8_t enshow = 0; for (t = 0; t < len; t++) /* 按总显示位数循环 */ { temp = (num / lcd_pow(10, len - t - 1)) % 10; /* 获取对应位的数字 */ if (enshow == 0 && t < (len - 1)) /* 没有使能显示,且还有位要显示 */ { if (temp == 0) { lcd_show_char(x + (size / 2) * t, y, ' ', size, 0, color); /* 显示空格,占位 */ continue; /* 继续下个一位 */ } else { enshow = 1; /* 使能显示 */ } } lcd_show_char(x + (size / 2) * t, y, temp + '0', size, 0, color); /* 显示字符 */ } } /** * @brief 扩展显示len个数字(高位是0也显示) * @param x,y : 起始坐标 * @param num : 数值(0 ~ 2^32) * @param len : 显示数字的位数 * @param size : 选择字体 12/16/24/32 * @param mode : 显示模式 * [7]:0,不填充;1,填充0. * [6:1]:保留 * [0]:0,非叠加显示;1,叠加显示. * @param color : 数字的颜色; * @retval 无 */ void lcd_show_xnum(uint16_t x, uint16_t y, uint32_t num, uint8_t len, uint8_t size, uint8_t mode, uint32_t color) { uint8_t t, temp; uint8_t enshow = 0; for (t = 0; t < len; t++) /* 按总显示位数循环 */ { temp = (num / lcd_pow(10, len - t - 1)) % 10; /* 获取对应位的数字 */ if (enshow == 0 && t < (len - 1)) /* 没有使能显示,且还有位要显示 */ { if (temp == 0) { if (mode & 0X80) /* 高位需要填充0 */ { lcd_show_char(x + (size / 2) * t, y, '0', size, mode & 0X01, color); /* 用0占位 */ } else { lcd_show_char(x + (size / 2) * t, y, ' ', size, mode & 0X01, color); /* 用空格占位 */ } continue; } else { enshow = 1; /* 使能显示 */ } } lcd_show_char(x + (size / 2) * t, y, temp + '0', size, mode & 0X01, color); } } /** * @brief 显示字符串 * @param x,y : 起始坐标 * @param width,height: 区域大小 * @param size : 选择字体 12/16/24/32 * @param p : 字符串首地址 * @param color : 字符串的颜色; * @retval 无 */ void lcd_show_string(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t size, char *p, uint32_t color) { uint8_t x0 = x; width += x; height += y; while ((*p <= '~') && (*p >= ' ')) /* 判断是不是非法字符! */ { if (x >= width) { x = x0; y += size; } if (y >= height) break; /* 退出 */ lcd_show_char(x, y, *p, size, 0, color); x += size / 2; p++; } }             #include "gd32h7xx.h" #include "systick.h" #include <stdio.h> #include "gd32h759i_eval.h" #include "gd32h759i_lcd_eval.h" #include "./SYSTEM/delay/delay.h" #include "./BSP/TIMER/timer.h" #include "./BSP/24CXX/24cxx.h" #include "./BSP/LCD/lcd.h" const uint8_t g_text_buf[] = {"gd32 IIC TEST"}; /* 要写入到 24c02 的字符串数组 */ #define TEXT_SIZE sizeof(g_text_buf) /* TEXT 字符串长度 */ /*! \brief toggle the led every 500ms \param[in] none \param[out] none \retval none */ void led_spark(void) { static __IO uint32_t timingdelaylocal = 0U; if(timingdelaylocal) { if(timingdelaylocal < 500U) { gd_eval_led_on(LED1); } else { gd_eval_led_off(LED1); } timingdelaylocal--; } else { timingdelaylocal = 1000U; } } /*! \brief enable the CPU cache \param[in] none \param[out] none \retval none */ void cache_enable(void) { /* enable i-cache */ SCB_EnableICache(); /* enable d-cache */ SCB_EnableDCache(); } /*! \brief main function \param[in] none \param[out] none \retval none */ int main(void) { uint8_t x = 0; uint8_t datatemp[TEXT_SIZE]; /* enable the CPU cache */ cache_enable(); /* configure systick */ //systick_config(); delay_init(600); /* initilize the LEDs, USART and key */ gd_eval_led_init(LED1); gd_eval_led_init(LED2); gd_eval_com_init(EVAL_COM); gd_eval_key_init(KEY_WAKEUP, KEY_MODE_GPIO); gd_eval_key_init(KEY_TAMPER, KEY_MODE_GPIO); at24cxx_init(); timerx_int_init(10 - 1, 30000 - 1); printf("\r\nCK_SYS is %d", rcu_clock_freq_get(CK_SYS)); lcd_init(); while (at24cxx_check()) { gd_eval_led_on(LED1); delay_ms(500);lcd_init(); gd_eval_led_off(LED1); delay_ms(500); } while(1) { if(RESET == gd_eval_key_scan(KEY_WAKEUP)) { printf("\r\nCK_SYS is 1"); at24cxx_write(0, (uint8_t *)g_text_buf, TEXT_SIZE); } if(RESET == gd_eval_key_scan(KEY_TAMPER)) { printf("\r\nCK_SYS is 2"); at24cxx_read(0, datatemp, TEXT_SIZE); printf("The Data Readed Is: %s\r\n",datatemp); } switch (x) { case 0: lcd_clear(WHITE); break; case 1: lcd_clear(BLACK); break; case 2: lcd_clear(BLUE); break; case 3: lcd_clear(RED); break; case 4: lcd_clear(MAGENTA); break; case 5: lcd_clear(GREEN); break; case 6: lcd_clear(CYAN); break; case 7: lcd_clear(YELLOW); break; case 8: lcd_clear(BRRED); break; case 9: lcd_clear(GRAY); break; case 10: lcd_clear(LGRAY); break; case 11: lcd_clear(BROWN); break; } lcd_show_string(10, 40, 240, 32, 32, "STM32", RED); lcd_show_string(10, 80, 240, 24, 24, "TFTLCD TEST", RED); lcd_show_string(10, 110, 240, 16, 16, "ATOM@ALIENTEK", RED); x++; if (x == 12) x = 0; delay_ms(1000); } } /* retarget the C library printf function to the USART */ int fputc(int ch, FILE *f) { usart_data_transmit(EVAL_COM, (uint8_t)ch); while(RESET == usart_flag_get(EVAL_COM, USART_FLAG_TBE)); return ch; }   实验现象      

  • 2024-06-22
  • 发表了主题帖: 【兆易GD32H759I-EVAL】TLI与LCD显示实现(1)

    本帖最后由 尹小舟 于 2024-6-23 15:19 编辑 LCD的是1种支持全彩显示的显示设备GD32H7开发板上的LCD显示尺寸是一块4.3寸的LCD显示屏分辨率为480乘 272,RGB接口不带液晶控制器,   TFT-LCD 接口(TLI)   TLI是GDS2F4701IH6微控制器自带的液晶控制器,它负责液晶显示(LCD)的控制以及与LCD模块之间的像素数据传输。 支持两个独立的显示层,实现背景和前景的分层显示效果。 通过与LCD模块的RGB接口相连,实现液晶控制及像素数据的直接传输。     TLI的引脚与时钟域   接口引脚描述 控制信号线: HS(HSYNC):对应LCD模块的LCD_HSYNC,用于水平同步信号。 VS(VSYNC):对应LCD模块的LCD_VSYNC,用于垂直同步信号。 DE(Data Enable):对应LCD模块的LCD_DE,表示数据有效信号。 PIXCLK            :对应LCD模块的LCD_CLK,表示像素时钟信号。 RGB双向数据线: RED[7:0]:对应LCD模块的LCD_R[7:0],用于传输红色分量数据。 GREEN[7:0]:对应LCD模块的LCD_G[7:0],用于传输绿色分量数据。 BLUE[7:0]:对应LCD模块的LCD_B[7:0],用于传输蓝色分量数据。 时钟域描述 TLI模块中包含了三个时钟域,每个时钟域服务于不同的模块和功能: AHB时钟域: 主要服务于像素DMA模块。 像素DMA模块从系统存储器获取像素数据时,需要使用AHB总线。 AHB总线提供高速数据传输能力,确保像素数据能够高效地从存储器传输到显示模块。 APB时钟域: 寄存器工作在此时钟域。 通过APB总线访问寄存器,实现对TLI模块的配置和控制。 APB总线通常用于访问微控制器的外设和配置寄存器。 TLI时钟域: 除了AHB和APB时钟域外的其他模块工作在此时钟域。 TLI时钟频率由PLLSAI-R分频得到,可以根据需要进行配置以满足LCD模块的时钟要求。 TLI时钟通过PIXCLK引脚输出,为LCD模块提供像素时钟信号。 图像处理单元 TLI的图像处理单元包含6个部分,分别为像素DMA单元、LCD时序控制器、像素处理单元0、像素处理单元1、窗口和混合单元以及抖动单元。下面依次对这6个部分进行介绍。 像素DMA单元(Pixel DMA Unit): 负责将保存在外部SDRAM中的显存数据传输到像素处理单元的缓冲区。 由于LCD显示模块自身不带显存,因此需要外部存储如SDRAM来存储待显示的图像数据。 SDRAM挂载到AHB总线上,使得像素DMA单元可以高效地从中读取数据。 LCD时序控制器(LCD Timing Controller): 用于产生符合LCD面板要求的RGB接口时序信号。 这些时序信号是LCD面板正确接收和显示图像所必需的。 像素处理单元0和像素处理单元1(Pixel Processing Unit 0 & 1): 分别对背景层和前景层的图像数据进行处理。 将RGB565格式的像素数据转换为ARGB8888格式。ARGB8888格式提供了更高的颜色深度和透明度控制(Alpha通道)。 如果源数据的通道位数小于8,则进行高位数据的复制和填充。 窗口和混合单元(Windowing and Blending Unit): 负责将像素处理单元0和像素处理单元1输出的两层ARGB8888图像数据进行混合。 可以通过设置不同的混合参数(如透明度)来实现复杂的显示效果,如半透明窗口等。 混合后的数据输出为RGB888格式,因为大多数LCD面板不支持Alpha通道的直接显示。 抖动单元(Dithering Unit): 主要功能是对大于液晶面板色深的数据进行抖动处理。 抖动是一种通过时间和空间上的颜色变化来模拟更高颜色深度的技术。 例如,当使用18位的LCD显示器时,抖动单元可以将24位的RGB888数据压缩为18位数据进行显示,从而在不增加面板硬件成本的情况下提升显示效果。 整体工作流程: 像素DMA单元从外部SDRAM中读取RGB565格式的图像数据。 像素处理单元将读取的数据转换为ARGB8888格式,并可能进行其他必要的处理。 窗口和混合单元将背景层和前景层的图像数据进行混合。 抖动单元对混合后的数据进行抖动处理,以匹配LCD面板的色深。 LCD时序控制器产生时序信号,将数据输出到LCD面板进行显示。 图像处理加速器(IPA) IPA为GD32H7微控制器自带的图像处理加速器,使用TLI控制液晶屏显示时由于显存中的像素数据容量非常大,因此当需要快速绘制矩形、直线、分层数据混合、图像数据格式转换时,可以选择利用IPA外设来进行,IPA运行方式与TLI图像处理单元过程相似,且效率更高。IPA支持4种转换模式: 复制某一源图像到目标图像中: 应用场景:当你需要将一个已经存在的图像(源图像)直接复制到另一个位置(目标图像)时,可以使用此模式。这通常用于图像的移动、复制或简单的屏幕更新。 特点:此模式不涉及任何格式转换或颜色混合,只是简单的图像数据复制。 复制某一源图像到目标图像中并同时进行特定的格式转换: 应用场景:当你需要将源图像复制到目标位置,并且同时需要将图像的格式(如颜色深度、色彩空间等)从一种格式转换为另一种格式时,此模式非常有用。 特点:除了图像数据的复制外,还包含了对图像数据的格式转换,这允许你在传输或显示图像之前改变其视觉属性。 将两个不同的源图像进行混合,并将得到的结果进行特定的颜色格式转换: 应用场景:图像混合在图形编辑、视频合成、增强现实(AR)等多种应用中都是必要的。此模式允许你将两个图像混合在一起,并可能改变混合后图像的颜色格式。 特点:支持分层数据的混合处理,允许你对不同来源的图像数据进行复杂的组合和颜色转换,创建出独特的视觉效果。 用特定的颜色填充目标图像区域: 应用场景:当你想在图像的特定区域上绘制纯色块(如背景色、文本框背景等)时,此模式非常有用。 特点:允许你指定一个颜色,并用这个颜色填充图像中的指定区域。这通常用于创建静态背景、文本渲染等任务。 使用IPA的好处: 性能:由于IPA是专为图像处理设计的硬件加速器,它通常比软件实现具有更高的性能和更低的功耗。 灵活性:通过支持多种转换模式和颜色格式,IPA能够处理广泛的图像处理任务。 效率:可与DMA配合使用,减少CPU介入,进一步提高数据处理效率。与通过TLI(或其他方式)进行软件图像处理相比,IPA通常能够更快地完成任务,从而提高了系统的整体效率。     1 . AHB接口概述 AHB(Advanced High-performance Bus)是一种高级高性能总线,专为连接高性能模块(如CPU、DMA和DSP等)而设计。在嵌入式系统设计中,AHB总线作为SoC(System on Chip,系统级芯片)的片上系统总线,提供了高带宽、高效率的数据传输能力。 IPA中的AHB接口 在IPA(图像处理加速器)中,AHB接口分为AHB从设备接口和AHB主设备接口,分别用于数据的输入和输出。 AHB从设备接口: 功能:连接IPA和AMBA AHB总线,允许系统中的AHB主设备(如CPU)对IPA进行读写访问。 数据传输:在AHB从设备接口上,数据通常从AHB总线流向IPA,实现外部数据源(如SDRAM)到IPA的数据传输。 AHB主设备接口: 功能:将IPA处理后的数据(如待显示的像素数据)输出到AHB总线,进而传输到其他系统组件(如LCD控制器)。 数据流向:在AHB主设备接口上,数据从IPA流向AHB总线,再传输到目标设备。 AHB总线数据源 AHB总线的数据源通常是SDRAM(同步动态随机存取存储器)。在图像处理系统中,待显示的像素数据一般保存在SDRAM的特定区域(用作LCD的显存)中。这些像素数据的大小通常至少为一帧像素数据的容量,确保在屏幕上显示完整的图像。   2 . LUT单元 LUT,即颜色查找表,是一个用于将输入像素值映射到输出颜色值的表。在图像处理中,LUT常用于颜色空间转换、颜色校正、对比度增强等场景。当图像数据使用非直接像素格式(如索引颜色模式)时,LUT尤其有用。 LUT的工作原理 在LUT中,每个输入像素值(或索引)都与一个输出颜色值相对应。例如,如果图像数据中的每个像素使用8位数据表示(即256种可能的颜色值),那么LUT就可以包含256个条目,每个条目都存储了一个对应的颜色值(如ARGB8888或RGB888格式)。 当处理图像时,系统会根据每个像素的8位值在LUT中查找对应的颜色值,并将其用于显示。这样,即使原始图像数据只有8位深度,也可以显示具有更高颜色深度的图像(如24位或32位颜色)。 LUT的优点 提高颜色深度:如上所述,LUT允许使用较少的位数来表示更多的颜色。 颜色校正:通过修改LUT中的条目,可以调整图像的亮度、对比度、饱和度等属性。 实时处理:由于LUT是一个预计算的表,因此颜色转换可以在硬件级别上快速完成,实现实时图像处理。 LUT的限制 颜色范围限制:由于LUT中的颜色数据种类是固定的,因此实际显示图像的颜色只能局限于LUT中的颜色种类。这可能导致某些颜色在图像中无法准确表示。 存储需求:LUT的大小取决于输入像素值的范围和所需的颜色深度。对于较大的图像或较高的颜色深度,LUT可能会占用大量存储空间。 灵活性限制:LUT通常适用于固定的颜色转换任务。对于需要更复杂颜色处理的任务(如非线性颜色校正),可能需要使用更高级的技术或算法。 LUT是一种强大而灵活的工具,可以在图像处理中发挥重要作用。然而,在使用LUT时需要注意其限制和约束条件,以确保获得最佳的图像质量和性能。   3 . PCE单元概述 PCE是图像处理系统中的一个重要单元,主要用于层像素通道的处理和扩展。它主要包括前景层和背景层两部分,用于处理从AHB从设备接口传输进来的像素数据。 PCE单元的主要功能 像素数据格式转换: 无论原像素数据的格式是什么(如RGB565、YUV420等),PCE单元都能将其转换为ARGB8888格式。ARGB8888是一种常用的32位颜色格式,其中A代表Alpha通道(透明度),R、G、B分别代表红、绿、蓝三种颜色分量,每种颜色分量占用8位,总共32位。 数据缓冲区FIFO: PCE单元的前景层和背景层都配备有数据缓冲区FIFO(First In, First Out,先进先出队列)。FIFO用于缓存从AHB从设备接口获取的像素数据,确保数据在传输过程中的稳定性和连续性。 FIFO的存在使得PCE单元在处理像素数据时能够更加高效和灵活。它允许数据在需要时进行缓存和暂存,避免了因数据传输速度不匹配而导致的数据丢失或延迟。 层像素通道处理: PCE单元能够同时处理前景层和背景层的像素数据,实现图像的分层处理。这对于实现复杂的图像效果(如文字叠加、图片合成等)非常有用。 总结 PCE单元是图像处理系统中一个重要的组成部分,它能够将输入的像素数据转换为统一的ARGB8888格式,并通过前景层和背景层的数据缓冲区FIFO实现高效的数据处理。这使得PCE单元在处理复杂图像效果时具有更高的灵活性和效率。   4 . 混合单元 混合单元用于将前景层PCE与背景层PCE输出的数据合成为单层数据输出,经过混合器后,两层数据合成为一层ARGB8888格式的图像。注意:TLI混合单元输出的教据格式为 RGB888 Alpha 通道值的混合基于下面的公式(AF 是前景层 alpha 值, AB 是背景层 alpha 值):   红,绿,蓝通道值的混合基于下面的公式(RF, GF, BF 是前景层的红,绿,蓝值; RB, GB, BB 是背景层的红,绿,蓝值): 5.PCC 单元 PCC为目标像素通道压缩单元,它能够将混合单元转换得到的ARGB8888像素图像数据转换成目标格式,如RGB888、RGB565、ARGB1555和ARGB4444。     TLI的配置和使用步骤 初始化步骤 系统时钟配置 确保PLL2(或TLI时钟的源头)已配置了适当的乘法和除法因子,以满足TLI模块对时钟速率的需求。 在RCU(复位和时钟控制单元)中启用PLL2和TLI的时钟。 GPIO配置 为TLI功能分配GPIO引脚,这通常包括RGB数据引脚、像素时钟(PIXCLK)、水平同步(HSYNC)、垂直同步(VSYNC)和显示使能(DE)等信号。 根据硬件参考手册或数据表,将这些GPIO引脚设置为适当的复用功能模式,并启用它们的高速输出能力(如果支持)。 TLI核心配置 启动TLI 通过设置TLI_CTL寄存器中的TLIEN位来启用TLI模块。 配置显示时序 编程TLI_SPSZ、TLI_BPSZ、TLI_ASZ和TLI_TSZ等寄存器,为水平和垂直同步定义正确的脉冲宽度、背景宽度、活动区域大小和总大小。这些参数通常与连接的LCD显示器的时序要求相匹配。 图层配置 定义图层属性 为每个图层设置颜色格式(如RGB565、ARGB8888等)、窗口大小(宽度和高度)、位置(在显示区域中的X和Y坐标)。 如果需要,配置颜色键控和CLUT(颜色查找表)。颜色键控允许您将特定颜色替换为透明或其他颜色,而CLUT则用于颜色映射或调色板功能。 图层控制和配置寄存器 使用如LxWHPCR、LxWVPCR、LxDCCR等寄存器来定义图层的窗口大小、位置和默认颜色等属性。 图层叠加和透明度设置 混合因素配置 使用LxPFCR和LxCAxCR等寄存器来配置图层的混合因素(也称为alpha值),以控制图层之间的透明度和混合效果。 颜色键控 如果需要,配置LxCKCR寄存器来启用颜色键控功能,并指定要使其透明的颜色。 启动和停止显示更新 开始显示 确保所有图层都已正确配置并启用。 启用TLI主控制,启动从内存到TLI的数据传输。这通常涉及DMA(直接内存访问)控制器,用于高效地传输像素数据。 TLI将接收到的像素数据输出到连接的LCD显示器。 停止显示更新 禁用一个或多个图层,或禁用TLI主控制,以停止数据传输和显示输出。 相关代码       相关代码在Utilities文件夹       #include "gd32h7xx.h" #include "systick.h" #include <stdio.h> #include "gd32h759i_eval.h" #include "gd32h759i_lcd_eval.h" /*! \brief toggle the led every 500ms \param[in] none \param[out] none \retval none */ void led_spark(void) { static __IO uint32_t timingdelaylocal = 0U; if(timingdelaylocal) { if(timingdelaylocal < 500U) { gd_eval_led_on(LED1); } else { gd_eval_led_off(LED1); } timingdelaylocal--; } else { timingdelaylocal = 1000U; } } /*! \brief enable the CPU cache \param[in] none \param[out] none \retval none */ void cache_enable(void) { /* enable i-cache */ SCB_EnableICache(); /* enable d-cache */ SCB_EnableDCache(); } /*! \brief main function \param[in] none \param[out] none \retval none */ int main(void) { /* enable the CPU cache */ cache_enable(); /* configure systick */ systick_config(); /* initilize the LEDs, USART and key */ gd_eval_led_init(LED1); gd_eval_led_init(LED2); gd_eval_com_init(EVAL_COM); gd_eval_key_init(KEY_WAKEUP, KEY_MODE_GPIO); gd_eval_lcd_init(); lcd_string_display(LCD_LINE_1,"GD32H7_EVAL_LCD"); lcd_vertical_char_display(LCD_LINE_6,48,'G'); while(1) { if(RESET == gd_eval_key_state_get(KEY_WAKEUP)) { delay_1ms(50); if(RESET == gd_eval_key_state_get(KEY_WAKEUP)) { gd_eval_led_toggle(LED2); } while(RESET == gd_eval_key_state_get(KEY_WAKEUP)) { } } } } /* retarget the C library printf function to the USART */ int fputc(int ch, FILE *f) { usart_data_transmit(EVAL_COM, (uint8_t)ch); while(RESET == usart_flag_get(EVAL_COM, USART_FLAG_TBE)); return ch; } /*! \file gd32h7xxi_lcd_eval.c \brief gd32h7xxi lcd driver \version 2024-01-05, V1.2.0, firmware for GD32H7xx */ /* Copyright (c) 2024, GigaDevice Semiconductor Inc. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "gd32h759i_lcd_eval.h" #include "gd32h759i_eval_exmc_sdram.h" #include <string.h> #define LCD_FRAME_BUFFER ((uint32_t)0xC0000000) #define BUFFER_OFFSET ((uint32_t)0x7F800) static font_struct *current_font; static uint16_t current_textcolor = 0x0000; static uint16_t current_backcolor = 0xFFFF; static uint32_t current_framebuffer = LCD_FRAME_BUFFER; static uint32_t current_layer = LCD_LAYER_BACKGROUND; static void lcd_char_draw(uint16_t xpos, uint16_t ypos, const uint16_t *c); static void lcd_vertical_char_draw(uint16_t xpos, uint16_t ypos, const uint16_t *c); static void pixel_set(int16_t x, int16_t y); #define HORIZONTAL_SYNCHRONOUS_PULSE 41 #define HORIZONTAL_BACK_PORCH 2 #define ACTIVE_WIDTH 480 #define HORIZONTAL_FRONT_PORCH 2 #define VERTICAL_SYNCHRONOUS_PULSE 10 #define VERTICAL_BACK_PORCH 2 #define ACTIVE_HEIGHT 272 #define VERTICAL_FRONT_PORCH 2 /*! \brief initializes the LCD of GD EVAL board \param[in] none \param[out] none \retval none */ void gd_eval_lcd_init(void) { lcd_init(); lcd_layer_init(LCD_LAYER_BACKGROUND, LCD_PIXEL_WIDTH, LCD_PIXEL_HEIGHT); lcd_layer_init(LCD_LAYER_FOREGROUND, LCD_PIXEL_WIDTH, LCD_PIXEL_HEIGHT); tli_layer_enable(LAYER0); tli_layer_enable(LAYER1); tli_enable(); lcd_layer_set(LCD_LAYER_BACKGROUND); lcd_transparency_set(255); lcd_clear(LCD_COLOR_WHITE); lcd_layer_set(LCD_LAYER_FOREGROUND); lcd_transparency_set(0); lcd_clear(LCD_COLOR_WHITE); lcd_layer_set(LCD_LAYER_BACKGROUND); } /*! \brief initialize the LCD GPIO and TLI \param[in] none \param[out] none \retval none */ void lcd_init(void) { tli_parameter_struct tli_init_struct; /* enable GPIO clock */ rcu_periph_clock_enable(RCU_GPIOA); rcu_periph_clock_enable(RCU_GPIOB); rcu_periph_clock_enable(RCU_GPIOC); rcu_periph_clock_enable(RCU_GPIOD); rcu_periph_clock_enable(RCU_GPIOE); rcu_periph_clock_enable(RCU_GPIOF); rcu_periph_clock_enable(RCU_GPIOH); rcu_periph_clock_enable(RCU_GPIOG); /* configure HSYNC(PE15), VSYNC(PA7), PCLK(PG7) */ gpio_af_set(GPIOE, GPIO_AF_14, GPIO_PIN_15); gpio_af_set(GPIOA, GPIO_AF_14, GPIO_PIN_7); gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_7); gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_15); gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_15); gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7); gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_7); gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7); gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_7); /* configure LCD_R7(PG6), LCD_R6(PH12), LCD_R5(PH11), LCD_R4(PA5), LCD_R3(PH9),LCD_R2(PH8), LCD_R1(PH3), LCD_R0(PH2) */ gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_6); gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_12); gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_11); gpio_af_set(GPIOA, GPIO_AF_14, GPIO_PIN_5); gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_9); gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_8); gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_3); gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_2); gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6); gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_6); gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12); gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_12); gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11); gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_11); gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5); gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_5); gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9); gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_9); gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8); gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_8); gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3); gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_3); gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2); gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_2); /* configure LCD_G7(PD3), LCD_G6(PC7), LCD_G5(PC1), LCD_G4(PH15), LCD_G3(PH14), LCD_G2(PH13),LCD_G1(PB0), LCD_G0(PB1) */ gpio_af_set(GPIOD, GPIO_AF_14, GPIO_PIN_3); gpio_af_set(GPIOC, GPIO_AF_14, GPIO_PIN_7); gpio_af_set(GPIOC, GPIO_AF_14, GPIO_PIN_1); gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_15); gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_14); gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_13); gpio_af_set(GPIOB, GPIO_AF_14, GPIO_PIN_0); gpio_af_set(GPIOB, GPIO_AF_14, GPIO_PIN_1); gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3); gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_3); gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7); gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_7); gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1); gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_1); gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_15); gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_15); gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14); gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_14); gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13); gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_13); gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_0); gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_0); gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1); gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_1); /* configure LCD_B7(PB9), LCD_B6(PB8), LCD_B5(PB5), LCD_B4(PC11), LCD_B3(PG11),LCD_B2(PG10), LCD_B1(PG12), LCD_B0(PG14) */ gpio_af_set(GPIOB, GPIO_AF_14, GPIO_PIN_9); gpio_af_set(GPIOB, GPIO_AF_14, GPIO_PIN_8); gpio_af_set(GPIOB, GPIO_AF_3, GPIO_PIN_5); gpio_af_set(GPIOC, GPIO_AF_14, GPIO_PIN_11); gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_11); gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_10); gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_12); gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_14); gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9); gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_9); gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8); gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_8); gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5); gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_5); gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11); gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_11); gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11); gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_11); gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10); gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_10); gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12); gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_12); gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14); gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_14); /* configure LCD_DE(PF10) */ gpio_af_set(GPIOF, GPIO_AF_14, GPIO_PIN_10); gpio_mode_set(GPIOF, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10); gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_10); /* LCD PWM BackLight(PG13) */ gpio_mode_set(GPIOG, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_PIN_13); gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_13); gpio_bit_set(GPIOG, GPIO_PIN_13); rcu_periph_clock_enable(RCU_TLI); /* configure PLL2 to generate TLI clock 25MHz/25*192/3 = 64MHz*/ rcu_pll_input_output_clock_range_config(IDX_PLL1,RCU_PLL1RNG_1M_2M,RCU_PLL1VCO_192M_836M); if(ERROR == rcu_pll2_config(25,192,3,3,3)){ while(1){ } } rcu_pll_clock_output_enable(RCU_PLL2R); rcu_tli_clock_div_config(RCU_PLL2R_DIV8); rcu_osci_on(RCU_PLL2_CK); if(ERROR == rcu_osci_stab_wait(RCU_PLL2_CK)){ while(1){ } } /* configure the EXMC access mode */ exmc_synchronous_dynamic_ram_init(EXMC_SDRAM_DEVICE0); /* TLI initialization */ tli_init_struct.signalpolarity_hs = TLI_HSYN_ACTLIVE_LOW; tli_init_struct.signalpolarity_vs = TLI_VSYN_ACTLIVE_LOW; tli_init_struct.signalpolarity_de = TLI_DE_ACTLIVE_LOW; tli_init_struct.signalpolarity_pixelck = TLI_PIXEL_CLOCK_INVERTEDTLI; /* LCD display timing configuration */ tli_init_struct.synpsz_hpsz = HORIZONTAL_SYNCHRONOUS_PULSE - 1; tli_init_struct.synpsz_vpsz = VERTICAL_SYNCHRONOUS_PULSE - 1; tli_init_struct.backpsz_hbpsz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH - 1; tli_init_struct.backpsz_vbpsz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH - 1; tli_init_struct.activesz_hasz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH + ACTIVE_WIDTH - 1; tli_init_struct.activesz_vasz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH + ACTIVE_HEIGHT - 1; tli_init_struct.totalsz_htsz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH + ACTIVE_WIDTH + HORIZONTAL_FRONT_PORCH - 1; tli_init_struct.totalsz_vtsz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH + ACTIVE_HEIGHT + VERTICAL_FRONT_PORCH - 1; /* LCD background color configure*/ tli_init_struct.backcolor_red = 0xFF; tli_init_struct.backcolor_green = 0xFF; tli_init_struct.backcolor_blue = 0xFF; tli_init(&tli_init_struct); } /*! \brief initialize TLI layer0 or layer1 \param[in] layer: LCD layer \arg LCD_LAYER_BACKGROUND \arg LCD_LAYER_FOREGROUND \param[in] width: width of the window \param[in] height: height of the window \param[out] none \retval none */ void lcd_layer_init(uint32_t layer, uint32_t width, uint32_t height) { tli_layer_parameter_struct tli_layer_init_struct; if(LCD_LAYER_BACKGROUND == layer){ /* TLI layer0 configuration */ tli_layer_init_struct.layer_window_leftpos = (HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH); tli_layer_init_struct.layer_window_rightpos = (width + HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH - 1); tli_layer_init_struct.layer_window_toppos = (VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH); tli_layer_init_struct.layer_window_bottompos = (height + VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH - 1); tli_layer_init_struct.layer_ppf = LAYER_PPF_RGB565; tli_layer_init_struct.layer_sa = 0xFF; tli_layer_init_struct.layer_default_blue = 0xFF; tli_layer_init_struct.layer_default_green = 0xFF; tli_layer_init_struct.layer_default_red = 0xFF; tli_layer_init_struct.layer_default_alpha = 0xFF; tli_layer_init_struct.layer_acf1 = LAYER_ACF1_PASA; tli_layer_init_struct.layer_acf2 = LAYER_ACF2_PASA; tli_layer_init_struct.layer_frame_bufaddr = LCD_FRAME_BUFFER; tli_layer_init_struct.layer_frame_line_length = ((width * 2) + 3); tli_layer_init_struct.layer_frame_buf_stride_offset = (width * 2); tli_layer_init_struct.layer_frame_total_line_number = height; tli_layer_init(LAYER0, &tli_layer_init_struct); }else if(LCD_LAYER_FOREGROUND == layer){ /* TLI layer1 configuration */ tli_layer_init_struct.layer_window_leftpos = (HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH); tli_layer_init_struct.layer_window_rightpos = (width + HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH - 1); tli_layer_init_struct.layer_window_toppos = (VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH); tli_layer_init_struct.layer_window_bottompos = (height + VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH - 1); tli_layer_init_struct.layer_ppf = LAYER_PPF_RGB565; tli_layer_init_struct.layer_sa = 0xFF; tli_layer_init_struct.layer_default_blue = 0xFF; tli_layer_init_struct.layer_default_green = 0xFF; tli_layer_init_struct.layer_default_red = 0xFF; tli_layer_init_struct.layer_default_alpha = 0x0; tli_layer_init_struct.layer_acf1 = LAYER_ACF1_PASA; tli_layer_init_struct.layer_acf2 = LAYER_ACF2_PASA; tli_layer_init_struct.layer_frame_bufaddr = LCD_FRAME_BUFFER + BUFFER_OFFSET; tli_layer_init_struct.layer_frame_line_length = ((width * 2) + 3); tli_layer_init_struct.layer_frame_buf_stride_offset = (width * 2); tli_layer_init_struct.layer_frame_total_line_number = height; tli_layer_init(LAYER1, &tli_layer_init_struct); } tli_reload_config(TLI_REQUEST_RELOAD_EN); lcd_font_set(&LCD_DEFAULT_FONT); } /*! \brief enable the LCD layer0 or layer1 \param[in] layer: LCD layer \arg LCD_LAYER_BACKGROUND \arg LCD_LAYER_FOREGROUND \param[out] none \retval none */ void lcd_layer_enable(uint32_t layer) { if(LCD_LAYER_BACKGROUND == layer){ tli_layer_enable(LAYER0); }else if(LCD_LAYER_FOREGROUND == layer){ tli_layer_enable(LAYER1); } tli_enable(); } /*! \brief set the LCD layer \param[in] layer: LCD layer \arg LCD_LAYER_BACKGROUND \arg LCD_LAYER_FOREGROUND \param[out] none \retval none */ void lcd_layer_set(uint32_t layer) { if(LCD_LAYER_BACKGROUND == layer){ current_framebuffer = LCD_FRAME_BUFFER; current_layer = LCD_LAYER_BACKGROUND; }else{ current_framebuffer = LCD_FRAME_BUFFER + BUFFER_OFFSET; current_layer = LCD_LAYER_FOREGROUND; } } /*! \brief set the transparency of LCD \param[in] trans: transparency of LCD, from 0 to 255 \param[out] none \retval none */ void lcd_transparency_set(uint8_t trans) { if (LCD_LAYER_BACKGROUND == current_layer){ TLI_LXSA(LAYER0) &= ~(TLI_LXSA_SA); TLI_LXSA(LAYER0) = trans; }else{ TLI_LXSA(LAYER1) &= ~(TLI_LXSA_SA); TLI_LXSA(LAYER1) = trans; } tli_reload_config(TLI_REQUEST_RELOAD_EN); } /*! \brief configure the packeted pixel format \param[in] pixel_format: pixel format \arg LAYER_PPF_ARGB8888 \arg LAYER_PPF_RGB888 \arg LAYER_PPF_RGB565 \arg LAYER_PPF_ARGB1555 \arg LAYER_PPF_ARGB4444 \arg LAYER_PPF_L8 \arg LAYER_PPF_AL44 \arg LAYER_PPF_AL88 \param[out] none \retval none */ void lcd_pixel_format_config(uint32_t pixel_format) { if(LCD_LAYER_BACKGROUND == current_layer){ TLI_LXPPF(LAYER0) &= ~(TLI_LXPPF_PPF); TLI_LXPPF(LAYER0) = pixel_format; }else{ TLI_LXPPF(LAYER1) &= ~(TLI_LXPPF_PPF); TLI_LXPPF(LAYER1) = pixel_format; } } /*! \brief configure the frame buffer base address \param[in] address: frame buffer base address \param[out] none \retval none */ void lcd_address_config(uint32_t address) { if (LCD_LAYER_BACKGROUND == current_layer){ TLI_LXFBADDR(LAYER0) &= ~(TLI_LXFBADDR_FBADD); TLI_LXFBADDR(LAYER0) = address; }else{ TLI_LXFBADDR(LAYER1) &= ~(TLI_LXFBADDR_FBADD); TLI_LXFBADDR(LAYER1) = address; } tli_reload_config(TLI_REQUEST_RELOAD_EN); } /*! \brief clear the LCD with specified color \param[in] color: LCD color \arg LCD_COLOR_WHITE \arg LCD_COLOR_BLACK \arg LCD_COLOR_GREY \arg LCD_COLOR_BLUE \arg LCD_COLOR_BLUE2 \arg LCD_COLOR_RED \arg LCD_COLOR_MAGENTA \arg LCD_COLOR_GREEN \arg LCD_COLOR_CYAN \arg LCD_COLOR_YELLOW \param[out] none \retval none */ void lcd_clear(uint16_t color) { uint32_t index = 0; for (index = 0x00; index < BUFFER_OFFSET; index++){ *(__IO uint16_t*)(current_framebuffer + (2*index)) = color; } } /*! \brief set the text font \param[in] font: the text font \param[out] none \retval none */ void lcd_font_set(font_struct *font) { current_font = font; } /*! \brief get the text font \param[in] none \param[out] none \retval the text font */ font_struct* lcd_font_get(void) { return current_font; } /*! \brief set the text color \param[in] color: LCD color \arg LCD_COLOR_WHITE \arg LCD_COLOR_BLACK \arg LCD_COLOR_GREY \arg LCD_COLOR_BLUE \arg LCD_COLOR_BLUE2 \arg LCD_COLOR_RED \arg LCD_COLOR_MAGENTA \arg LCD_COLOR_GREEN \arg LCD_COLOR_CYAN \arg LCD_COLOR_YELLOW \param[out] none \retval none */ void lcd_text_color_set(uint16_t color) { current_textcolor = color; } /*! \brief get the current text color \param[in] none \param[out] none \retval LCD color \arg LCD_COLOR_WHITE \arg LCD_COLOR_BLACK \arg LCD_COLOR_GREY \arg LCD_COLOR_BLUE \arg LCD_COLOR_BLUE2 \arg LCD_COLOR_RED \arg LCD_COLOR_MAGENTA \arg LCD_COLOR_GREEN \arg LCD_COLOR_CYAN \arg LCD_COLOR_YELLOW */ uint16_t lcd_text_color_get(void) { return current_textcolor; } /*! \brief set the background color \param[in] color: LCD color \arg LCD_COLOR_WHITE \arg LCD_COLOR_BLACK \arg LCD_COLOR_GREY \arg LCD_COLOR_BLUE \arg LCD_COLOR_BLUE2 \arg LCD_COLOR_RED \arg LCD_COLOR_MAGENTA \arg LCD_COLOR_GREEN \arg LCD_COLOR_CYAN \arg LCD_COLOR_YELLOW \param[out] none \retval none */ void lcd_background_color_set(uint16_t color) { current_backcolor = color; } /*! \brief get the current background color \param[in] none \param[out] none \retval LCD color \arg LCD_COLOR_WHITE \arg LCD_COLOR_BLACK \arg LCD_COLOR_GREY \arg LCD_COLOR_BLUE \arg LCD_COLOR_BLUE2 \arg LCD_COLOR_RED \arg LCD_COLOR_MAGENTA \arg LCD_COLOR_GREEN \arg LCD_COLOR_CYAN \arg LCD_COLOR_YELLOW */ uint16_t lcd_background_color_get(void) { return current_backcolor; } /*! \brief set point with the specified position and color \param[in] xpos: position of x \param[in] ypos: position of y \param[in] color: LCD color \arg LCD_COLOR_WHITE \arg LCD_COLOR_BLACK \arg LCD_COLOR_GREY \arg LCD_COLOR_BLUE \arg LCD_COLOR_BLUE2 \arg LCD_COLOR_RED \arg LCD_COLOR_MAGENTA \arg LCD_COLOR_GREEN \arg LCD_COLOR_CYAN \arg LCD_COLOR_YELLOW \param[out] none \retval none */ void lcd_point_set(uint16_t xpos, uint16_t ypos, uint16_t color) { *(__IO uint16_t*)(current_framebuffer + 2*((LCD_PIXEL_WIDTH*ypos) + xpos)) = color; } /*! \brief get the color of point with the specified position \param[in] xpos: position of x \param[in] ypos: position of y \param[out] none \retval value of point(x, y) */ uint16_t lcd_point_get(uint16_t xpos, uint16_t ypos) { return *(__IO uint16_t*)(current_framebuffer + 2*((LCD_PIXEL_WIDTH*ypos) + xpos)); } /*! \brief draw a line on LCD \param[in] xpos: position of x \param[in] ypos: position of y \param[in] length: length of line \param[in] line_direction: direction of line \arg LCD_LINEDIR_HORIZONTAL \arg LCD_LINEDIR_VERTICAL \param[out] none \retval none */ void lcd_line_draw(uint16_t xpos, uint16_t ypos, uint16_t length, uint8_t line_direction) { if(LCD_LINEDIR_HORIZONTAL == line_direction){ uint16_t x; for(x = xpos; x < xpos + length; x++){ pixel_set(x, ypos); } }else{ uint16_t y; for(y = ypos; y < ypos + length; y++){ pixel_set(xpos, y); } } } /*! \brief draw a rectangle on LCD \param[in] xpos: position of x \param[in] ypos: position of y \param[in] width: width of rectangle \param[in] height: height of rectangle \param[out] none \retval none */ void lcd_rectangle_draw(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height) { /* draw horizontal lines */ lcd_line_draw(xpos, ypos, width, LCD_LINEDIR_HORIZONTAL); lcd_line_draw(xpos, (ypos+ height), width + 1, LCD_LINEDIR_HORIZONTAL); /* draw vertical lines */ lcd_line_draw(xpos, ypos, height, LCD_LINEDIR_VERTICAL); lcd_line_draw((xpos + width), ypos, height, LCD_LINEDIR_VERTICAL); } /*! \brief draw a circle on LCD using Bresenham algorithm \param[in] xpos: position of x \param[in] ypos: position of y \param[in] radius: radius of the circle \param[out] none \retval none */ void lcd_circle_draw(uint16_t xpos, uint16_t ypos, uint16_t radius) { int x, y, e; e = 3-2*radius; x = 0; y = radius; /* set four pixel (x-r, y), (x+r, y), (x, y-r), (x, y-r) */ pixel_set(-radius+xpos, ypos); pixel_set(radius+xpos, ypos); pixel_set(xpos, -radius+ypos); pixel_set(xpos, radius+ypos); while(x <= y){ if(e < 0){ /* choose the right of the current pixel as the next pixel */ e = e+4*x+6; x++; }else{ /* choose the right bottom of the current pixel as the next pixel */ e = e+4*(x-y)+10; x++; y--; } pixel_set(x+xpos, y+ypos); pixel_set(-x+xpos, y+ypos); pixel_set(-x+xpos, -y+ypos); pixel_set(x+xpos, -y+ypos); pixel_set(y+xpos, x+ypos); pixel_set(-y+xpos, x+ypos); pixel_set(-y+xpos, -x+ypos); pixel_set(y+xpos, -x+ypos); } } /*! \brief set plot point of ellipse \param[in] center_x: x position of ellipse center \param[in] center_y: y position of ellipse center \param[in] x: x value \param[in] y: y value \param[out] none \retval none */ static void plotpoint_set(int center_x,int center_y,int x,int y) { pixel_set(center_x+x, center_y+y); pixel_set(center_x-x, center_y+y); pixel_set(center_x+x, center_y-y); pixel_set(center_x-x, center_y-y); } /*! \brief draw a ellipse on LCD using the midpoint ellipse algorithm \param[in] xpos: x position of ellipse center \param[in] ypos: y position of ellipse center \param[in] axis1: major axis \param[in] axis2: minor axis \param[out] none \retval none */ void lcd_ellipse_draw(uint16_t xpos, uint16_t ypos, uint16_t axis1, uint16_t axis2) { int sq_axis1 = axis1*axis1, sq_axis2 = axis2*axis2; int p; int x = 0, y = axis2; int px = 0, py = 2*sq_axis1*y; /* draw four points on the long and short axis of the ellipse */ plotpoint_set(xpos, ypos, x, y); /* calculate the initial value in area 1 */ p = (int)((sq_axis2-(sq_axis1*axis2)+(0.25*sq_axis1))); while(px < py){ ++x; px += 2*sq_axis2; if(p<0){ p += sq_axis2+px; }else{ --y; py -= 2*sq_axis1; p += sq_axis2+px-py; } plotpoint_set(xpos, ypos, x, y); } /* calculate the initial value with the last point calculated in the area 1 */ p = (int)((sq_axis2*(x+0.5)*(x+0.5)+sq_axis1*(y-1)*(y-1)-sq_axis1*sq_axis2)); while(y > 0){ --y; py -= 2*sq_axis1; if(p > 0){ p += sq_axis1-py; }else{ ++x; px += 2*sq_axis2; p += sq_axis1-py+px; } plotpoint_set(xpos, ypos, x, y); } } /*! \brief fill the whole rectangle \param[in] xpos: position of x \param[in] ypos: position of y \param[in] width: width of the rectangle \param[in] height: height of the rectangle \param[out] none \retval none */ void lcd_rectangle_fill(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height) { uint16_t x, y; for(x = xpos; x < xpos + width; x++){ for(y = ypos; y < ypos + height; y++){ pixel_set(x, y); } } } /*! \brief display the string on LCD \param[in] stringline: line to display the character \arg LCD_LINE_x(x=0..39 for 480*272) \param[in] ptr: a pointer to the string \param[out] none \retval none */ void lcd_string_display(uint16_t stringline, uint8_t *ptr) { uint16_t column = 0; while((column < LCD_PIXEL_WIDTH) && (*ptr != 0)){ /* display character on LCD */ lcd_char_display(stringline, column, *ptr); column += current_font->width; ptr++; } } /*! \brief display the vertical string on LCD \param[in] stringline: line to display the character \arg LCD_LINE_x(x=0..39 for 480*272) \param[in] offset: offset value \param[in] ptr: a pointer to the string \param[out] none \retval none */ void lcd_vertical_string_display(uint16_t stringline, uint16_t offset, uint8_t *ptr) { uint16_t column = LCD_PIXEL_HEIGHT - (current_font->width + offset + 2); while((column > 0) && (*ptr != 0)){ /* display character on LCD */ lcd_vertical_char_display(stringline, column, *ptr); column -= current_font->width; ptr++; } } /*! \brief display the character on LCD \param[in] line: line to display the character \arg LCD_LINE_x(x=0..39 for 480*272) \param[in] column: column address \param[in] ascii: character ascii code(from 0x20 to 0x7E) \param[out] none \retval none */ void lcd_char_display(uint16_t line, uint16_t column, uint8_t ascii) { ascii -= 32; lcd_char_draw(line, column, &current_font->table[ascii * current_font->height]); } /*! \brief display the vertical character on LCD \param[in] line: line to display the character \arg LCD_LINE_x(x=0..39 for 480*272) \param[in] column: column address \param[in] ascii: character ascii code(from 0x20 to 0x7E) \param[out] none \retval none */ void lcd_vertical_char_display(uint16_t line, uint16_t column, uint8_t ascii) { ascii -= 32; lcd_vertical_char_draw(line, column, &current_font->table[ascii * current_font->height]); } /*! \brief draw the character on LCD \param[in] xpos: position of x \param[in] ypos: position of y \param[in] c: a pointer to the character \param[out] none \retval none */ static void lcd_char_draw(uint16_t xpos, uint16_t ypos, const uint16_t *c) { uint32_t index = 0, counter = 0, x = 0; uint32_t xaddress = 0; x = xpos * LCD_PIXEL_WIDTH * 2; xaddress += ypos; for(index = 0; index < current_font->height; index++){ for(counter = 0; counter < current_font->width; counter++){ if((((c[index] & ((0x80 << ((current_font->width / 12) * 8)) >> counter)) == 0x00) && (current_font->width <= 12))|| (((c[index] & (0x1 << counter)) == 0x00) && (current_font->width > 12))){ /* write the background color */ *(__IO uint16_t*) (current_framebuffer + (2*xaddress) + x) = current_backcolor; }else{ /* write the text color */ *(__IO uint16_t*) (current_framebuffer + (2*xaddress) + x) = current_textcolor; } xaddress++; } xaddress += (LCD_PIXEL_WIDTH - current_font->width); } } /*! \brief draw the vertical character on LCD \param[in] xpos: position of x \param[in] ypos: position of y \param[in] c: a pointer to the character \param[out] none \retval none */ static void lcd_vertical_char_draw(uint16_t xpos, uint16_t ypos, const uint16_t *c) { uint32_t index = 0, counter = 0; for(index = 0; index < current_font->height; index++){ for(counter = 0; counter < current_font->width; counter++){ if((((c[index] & ((0x80 << ((current_font->width / 12) * 8)) >> counter)) == 0x00) && (current_font->width <= 12))|| (((c[index] & (0x1 << counter)) == 0x00) && (current_font->width > 12))){ if((int16_t)(xpos + index) < 0 || (int16_t)(xpos + index) > (LCD_PIXEL_WIDTH - 1) || (int16_t)(ypos + current_font->width - counter) < 0 || (int16_t)(ypos + current_font->width - counter) > (LCD_PIXEL_HEIGHT - 1)){ return; } *(__IO uint16_t*)(current_framebuffer + 2*(LCD_PIXEL_WIDTH * (int16_t)(ypos + current_font->width - counter) + (int16_t)(xpos + index))) = current_backcolor; }else{ pixel_set(xpos + index, ypos + current_font->width - counter); } } } } /*! \brief set a pixel \param[in] x: x position \param[in] y: y position \param[out] none \retval none */ static void pixel_set(int16_t x, int16_t y) { if(x < 0 || x > (LCD_PIXEL_WIDTH - 1) || y < 0 || y > (LCD_PIXEL_HEIGHT - 1)){ return; } /* draw pixel with current text color */ *(__IO uint16_t*)(current_framebuffer + 2*(LCD_PIXEL_WIDTH * y + x)) = current_textcolor; }   代码效果       字体歪歪扭扭的        

  • 发表了主题帖: 【兆易GD32H759I-EVAL】 模拟IIC读写 EEPROOqM

    本帖最后由 尹小舟 于 2024-6-22 16:22 编辑 DEMO 目的                  学习读写带有 I2C 接口的 EEPROM       学习GD32H7 GPIO的接口 AT24C02接口电路     GD32H7xx系列微控制器是一款功能强大的微控制器,其提供了大量的GPIO(General Purpose Input/Output,通用输入输出)端口供开发者使用。这些GPIO端口允许开发者根据具体的应用需求,配置为不同的功能,如输入、输出、备用功能或模拟模式。 GPIO数量与分组 GD3H7xx系列微控制器有多组GPIO,这些GPIO被分为GPIOA、GPIOB、...、GPIOJ,GPIOK等多个组。其中,每组端口包含015共16个不同的引脚,而GPIOI包含011共12个不同的引脚。请注意,不同型号的GD32H7xx系列微控制器可能具有不同的端口组数和引脚数,具体可参阅相应芯片的数据手册。 GPIO配置 每个通用I/O端口都可以通过端口控制寄存器(GPIOxCTL)进行配置,以实现GPIO输入、GPIO输出、备用功能或模拟模式。J GPIO输入:当端口配置为输入模式时,引脚可以读取外部信号的状态。 GPIO输出:当端口配置为输出模式时,引脚可以向外部设备发送信号。此时,可以通过GPIO输出模式寄存器(GPIOx_OMOHDE)配置为推挽(Push-Pull)或开漏(Open-Drain)模式。此外,输出端口的最大速度可以通过GPIO输出速度寄存器(GPIOx_OSPD)进行配置。 备用功能:某些引脚支持特定的备用功能,如UART、SPI等通信接口。这些备用功能通常通过使能AFIO(Alternate Function Input/Output,复用输入输出端口)功能来实现。 模拟模式:某些引脚还可以配置为模拟模式,用于模拟信号的处理。 GPIO寄存器 在GD32H7系列微控制器中,GPIO的配置和操作通常涉及到多个寄存器。 GPIOxCTL:端口控制寄存器,用于配置端口的输入/输出模式、上拉/下拉电阻等。 GPIOx_OMODE:GPIO输出模式寄存器,用于配置输出端口的模式(推挽或开漏)。 GPIOx_OSPD:GPIO输出速度寄存器,用于配置输出端口的最大速度。 GPIOx_PUD:GPIO上/下拉寄存器,用于配置引脚的上拉/下拉电阻。   输出驱动   输出驱动既能够配置成推挽模式,也能够配置为开漏模式。          在输出驱动模块中,包含两个 MOS 晶体管,上方与 Vdd 相连的是 P-MOS 晶体管,下方与 Vss 相接的是 N-MOS 晶体管。这两个 MOS 晶体管共同构成一个 CMOS 反向器。当输出驱动模块的输出控制端处于高电平时,上方的 P-MOS 晶体管会截止,下方的 N-MOS 晶体管则导通,此时 I/O 引脚向外部输出低电平;而当输出控制端为低电平时,上方的 P-MOS 晶体管导通,下方的 N-MOS 晶体管截止,I/O 引脚便向外部输出高电平。在 I/O 引脚进行高、低电平切换时,两个 MOS 晶体管会交替导通,其中 P-MOS 晶体管负责灌电流,N-MOS 晶体管负责拉电流,这使得其负载能力和开关速度相比普通方式有了较大幅度的提升。推挽输出的低电平约为 0V,高电平约为 3.3V。 I/O引脚、ESD 保护及上拉/下拉电阻      进行 I/O 配置时,可以选择配置为上拉模式、下拉模式或悬空模式(无上拉和下拉),即通过控制上拉/下拉电阻的通断实现。上拉即是将引脚的默认电平设置为高电平(接近V),下拉即是将引脚的默认电平设置为低电平(接近V),悬空时,引脚的默认电平不定      I/O引脚上还集成了 ESD 保护模块,ESD 又称为静电放电,其显著特点是高电位和作用时间短,这不仅影响电子元器件的使用寿命,严重时甚至会导致元器件损坏。ESD 保护模块可有效防止静电放电对芯片产生不良影响。   DEMO 代码          参考正点原子的代码   main函数 #include "gd32h7xx.h" #include "systick.h" #include <stdio.h> #include "gd32h759i_eval.h" #include "gd32h759i_lcd_eval.h" #include "./SYSTEM/delay/delay.h" #include "./BSP/TIMER/timer.h" #include "./BSP/24CXX/24cxx.h" const uint8_t g_text_buf[] = {"gd32 IIC TEST"}; /* 要写入到 24c02 的字符串数组 */ #define TEXT_SIZE sizeof(g_text_buf) /* TEXT 字符串长度 */ /*! \brief toggle the led every 500ms \param[in] none \param[out] none \retval none */ void led_spark(void) { static __IO uint32_t timingdelaylocal = 0U; if(timingdelaylocal) { if(timingdelaylocal < 500U) { gd_eval_led_on(LED1); } else { gd_eval_led_off(LED1); } timingdelaylocal--; } else { timingdelaylocal = 1000U; } } /*! \brief enable the CPU cache \param[in] none \param[out] none \retval none */ void cache_enable(void) { /* enable i-cache */ SCB_EnableICache(); /* enable d-cache */ SCB_EnableDCache(); } /*! \brief main function \param[in] none \param[out] none \retval none */ int main(void) { uint8_t datatemp[TEXT_SIZE]; /* enable the CPU cache */ cache_enable(); /* configure systick */ //systick_config(); delay_init(600); /* initilize the LEDs, USART and key */ gd_eval_led_init(LED1); gd_eval_led_init(LED2); gd_eval_com_init(EVAL_COM); gd_eval_key_init(KEY_WAKEUP, KEY_MODE_GPIO); gd_eval_key_init(KEY_TAMPER, KEY_MODE_GPIO); at24cxx_init(); timerx_int_init(10 - 1, 30000 - 1); printf("\r\nCK_SYS is %d", rcu_clock_freq_get(CK_SYS)); while (at24cxx_check()) { gd_eval_led_on(LED1); delay_ms(500); gd_eval_led_off(LED1); delay_ms(500); } while(1) { if(RESET == gd_eval_key_scan(KEY_WAKEUP)) { printf("\r\nCK_SYS is 1"); at24cxx_write(0, (uint8_t *)g_text_buf, TEXT_SIZE); } if(RESET == gd_eval_key_scan(KEY_TAMPER)) { printf("\r\nCK_SYS is 2"); at24cxx_read(0, datatemp, TEXT_SIZE); printf("The Data Readed Is: %s\r\n",datatemp); } } } /* retarget the C library printf function to the USART */ int fputc(int ch, FILE *f) { usart_data_transmit(EVAL_COM, (uint8_t)ch); while(RESET == usart_flag_get(EVAL_COM, USART_FLAG_TBE)); return ch; }   模拟IIC代码 #include "./BSP/IIC/myiic.h" #include "./SYSTEM/delay/delay.h" /** * @brief 初始化IIC * @param 无 * @retval 无 */ void iic_init(void) { rcu_periph_clock_enable(RCU_GPIOH); /* GPIOB时钟使能 */ rcu_periph_clock_enable(RCU_GPIOB); /* GPIOB时钟使能 */ /* SCL引脚模式设置,推挽输出,上拉 */ gpio_mode_set(GPIOH, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_PIN_4); gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_4); /* SDA引脚模式设置,开漏输出,上拉, 这样就不用再设置IO方向了, 开漏输出的时候(=1), 也可以读取外部信号的高低电平 */ gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_PIN_11); gpio_output_options_set(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_60MHZ, GPIO_PIN_11); iic_stop(); /* 停止总线上所有设备 */ } /** * @brief IIC延时函数,用于控制IIC读写速度 * @param 无 * @retval 无 */ static void iic_delay(void) { delay_us(2); /* 2us的延时, 读写速度在250Khz以内 */ } /** * @brief 产生IIC起始信号 * @param 无 * @retval 无 */ void iic_start(void) { IIC_SDA(1); IIC_SCL(1); iic_delay(); IIC_SDA(0); /* START信号: 当SCL为高时, SDA从高变成低, 表示起始信号 */ iic_delay(); IIC_SCL(0); /* 钳住I2C总线,准备发送或接收数据 */ iic_delay(); } /** * @brief 产生IIC停止信号 * @param 无 * @retval 无 */ void iic_stop(void) { IIC_SDA(0); /* STOP信号: 当SCL为高时, SDA从低变成高, 表示停止信号 */ iic_delay(); IIC_SCL(1); iic_delay(); IIC_SDA(1); /* 发送I2C总线结束信号 */ iic_delay(); } /** * @brief 等待应答信号到来 * @param 无 * @retval 1,接收应答失败 * 0,接收应答成功 */ uint8_t iic_wait_ack(void) { uint8_t waittime = 0; uint8_t rack = 0; IIC_SDA(1); /* 主机释放SDA线(此时外部器件可以拉低SDA线) */ iic_delay(); IIC_SCL(1); /* SCL=1, 此时从机可以返回ACK */ iic_delay(); while (IIC_READ_SDA) /* 等待应答 */ { waittime++; if (waittime > 250) /* 没有收到应答信号 */ { iic_stop(); rack = 1; break; } } IIC_SCL(0); /* SCL=0, 结束ACK检查 */ iic_delay(); return rack; } /** * @brief 产生ACK应答 * @param 无 * @retval 无 */ void iic_ack(void) { IIC_SDA(0); /* SCL 0 -> 1 时 SDA = 0,表示应答 */ iic_delay(); IIC_SCL(1); /* 产生一个时钟 */ iic_delay(); IIC_SCL(0); iic_delay(); IIC_SDA(1); /* 主机释放SDA线 */ iic_delay(); } /** * @brief 不产生ACK应答 * @param 无 * @retval 无 */ void iic_nack(void) { IIC_SDA(1); /* SCL 0 -> 1 时 SDA = 1,表示不应答 */ iic_delay(); IIC_SCL(1); /* 产生一个时钟 */ iic_delay(); IIC_SCL(0); iic_delay(); } /** * @brief IIC发送一个字节 * @param data: 要发送的数据 * @retval 无 */ void iic_send_byte(uint8_t data) { uint8_t t; for (t = 0; t < 8; t++) { IIC_SDA((data & 0x80) >> 7); /* 高位先发送 */ iic_delay(); IIC_SCL(1); iic_delay(); IIC_SCL(0); data <<= 1; /* 左移1位,用于下一次发送 */ } IIC_SDA(1); /* 发送完成, 主机释放SDA线 */ } /** * @brief IIC读取一个字节 * @param ack: ack=1时,发送ack; ack=0时,发送nack * @retval 接收到的数据 */ uint8_t iic_read_byte(uint8_t ack) { uint8_t i, receive = 0; for (i = 0; i < 8; i++ ) /* 接收1个字节数据 */ { receive <<= 1; /* 高位先输出,所以先收到的数据位要左移 */ IIC_SCL(1); iic_delay(); if (IIC_READ_SDA) { receive++; } IIC_SCL(0); iic_delay(); } if (!ack) { iic_nack(); /* 发送nACK */ } else { iic_ack(); /* 发送ACK */ } return receive; /* 返回读到的数据 */ }     #ifndef __MYIIC_H #define __MYIIC_H #include "gd32h7xx.h" /* IO操作函数 */ #define IIC_SCL(x) do{ x ? \ gpio_bit_write(GPIOH, GPIO_PIN_4, SET) : \ gpio_bit_write(GPIOH, GPIO_PIN_4, RESET); \ }while(0) /* SCL */ #define IIC_SDA(x) do{ x ? \ gpio_bit_write(GPIOB, GPIO_PIN_11, SET) : \ gpio_bit_write(GPIOB, GPIO_PIN_11, RESET); \ }while(0) /* SDA */ #define IIC_READ_SDA gpio_input_bit_get(GPIOB,GPIO_PIN_11) /* 读取SDA */ /* IIC所有操作函数 */ void iic_init(void); /* 初始化IIC的IO口 */ void iic_start(void); /* 发送IIC开始信号 */ void iic_stop(void); /* 发送IIC停止信号 */ void iic_ack(void); /* IIC发送ACK信号 */ void iic_nack(void); /* IIC不发送ACK信号 */ uint8_t iic_wait_ack(void); /* IIC等待ACK信号 */ void iic_send_byte(uint8_t txd); /* IIC发送一个字节 */ uint8_t iic_read_byte(uint8_t ack); /* IIC读取一个字节 */ #endif   EEPROM 读写函数 #include "./BSP/IIC/myiic.h" #include "./BSP/24CXX/24cxx.h" #include "./SYSTEM/delay/delay.h" #include "systick.h" /** * @brief 初始化IIC接口 * @param 无 * @retval 无 */ void at24cxx_init(void) { iic_init(); } /** * @brief 在AT24CXX指定地址读出一个数据 * @param addr: 开始读数的地址 * @retval 读到的数据 */ uint8_t at24cxx_read_one_byte(uint16_t addr) { uint8_t temp = 0; iic_start(); /* 发送起始信号 */ /* 根据不同的24CXX型号, 发送高位地址 * 1, 24C16以上的型号, 分2个字节发送地址 * 2, 24C16及以下的型号, 分1个低字节地址 + 占用器件地址的bit1~bit3位 用于表示高位地址, 最多11位地址 * 对于24C01/02, 其器件地址格式(8bit)为: 1 0 1 0 A2 A1 A0 R/W * 对于24C04, 其器件地址格式(8bit)为: 1 0 1 0 A2 A1 a8 R/W * 对于24C08, 其器件地址格式(8bit)为: 1 0 1 0 A2 a9 a8 R/W * 对于24C16, 其器件地址格式(8bit)为: 1 0 1 0 a10 a9 a8 R/W * R/W : 读/写控制位 0,表示写; 1,表示读; * A0/A1/A2 : 对应器件的1,2,3引脚(只有24C01/02/04/8有这些脚) * a8/a9/a10: 对应存储整列的高位地址, 11bit地址最多可以表示2048个位置,可以寻址24C16及以内的型号 */ if (EE_TYPE > AT24C16) /* 24C16以上的型号, 分2个字节发送地址 */ { iic_send_byte(0XA0); /* 发送写命令, IIC规定最低位是0, 表示写入 */ iic_wait_ack(); /* 每次发送完一个字节,都要等待ACK */ iic_send_byte(addr >> 8); /* 发送高字节地址 */ } else { iic_send_byte(0XA0 + ((addr >> 8) << 1)); /* 发送器件 0XA0 + 高位a8/a9/a10地址,写数据 */ } iic_wait_ack(); /* 每次发送完一个字节,都要等待ACK */ iic_send_byte(addr % 256); /* 发送低位地址 */ iic_wait_ack(); /* 等待ACK, 此时地址发送完成了 */ iic_start(); /* 重新发送起始信号 */ iic_send_byte(0XA1); /* 进入接收模式, IIC规定最低位是1, 表示读取 */ iic_wait_ack(); /* 每次发送完一个字节,都要等待ACK */ temp = iic_read_byte(0); /* 接收一个字节数据 */ iic_stop(); /* 产生一个停止条件 */ return temp; } /** * @brief 在AT24CXX指定地址写入一个数据 * @param addr: 写入数据的目的地址 * @param data: 要写入的数据 * @retval 无 */ void at24cxx_write_one_byte(uint16_t addr, uint8_t data) { /* 原理说明见:at24cxx_read_one_byte函数, 本函数完全类似 */ iic_start(); /* 发送起始信号 */ if (EE_TYPE > AT24C16) /* 24C16以上的型号, 分2个字节发送地址 */ { iic_send_byte(0XA0); /* 发送写命令, IIC规定最低位是0, 表示写入 */ iic_wait_ack(); /* 每次发送完一个字节,都要等待ACK */ iic_send_byte(addr >> 8); /* 发送高字节地址 */ } else { iic_send_byte(0XA0 + ((addr >> 8) << 1)); /* 发送器件 0XA0 + 高位a8/a9/a10地址,写数据 */ } iic_wait_ack(); /* 每次发送完一个字节,都要等待ACK */ iic_send_byte(addr % 256); /* 发送低位地址 */ iic_wait_ack(); /* 等待ACK, 此时地址发送完成了 */ /* 因为写数据的时候,不需要进入接收模式了,所以这里不用重新发送起始信号了 */ iic_send_byte(data); /* 发送1字节 */ iic_wait_ack(); /* 等待ACK */ iic_stop(); /* 产生一个停止条件 */ delay_ms(10); /* 注意: EEPROM 写入比较慢,必须等到10ms后再写下一个字节 */ } /** * @brief 检查AT24CXX是否正常 * @note 检测原理: 在器件的末地址写如0X55, 然后再读取, 如果读取值为0X55 * 则表示检测正常. 否则,则表示检测失败. * * @param 无 * @retval 检测结果 * 0: 检测成功 * 1: 检测失败 */ uint8_t at24cxx_check(void) { uint8_t temp; uint16_t addr = EE_TYPE; temp = at24cxx_read_one_byte(addr); /* 避免每次开机都写AT24CXX */ if (temp == 0X55) /* 读取数据正常 */ { return 0; } else /* 排除第一次初始化的情况 */ { at24cxx_write_one_byte(addr, 0X55); /* 先写入数据 */ temp = at24cxx_read_one_byte(255); /* 再读取数据 */ if (temp == 0X55)return 0; } return 1; } /** * @brief 在AT24CXX里面的指定地址开始读出指定个数的数据 * @param addr : 开始读出的地址 对24c02为0~255 * @param pbuf : 数据数组首地址 * @param datalen : 要读出数据的个数 * @retval 无 */ void at24cxx_read(uint16_t addr, uint8_t *pbuf, uint16_t datalen) { while (datalen--) { *pbuf++ = at24cxx_read_one_byte(addr++); } } /** * @brief 在AT24CXX里面的指定地址开始写入指定个数的数据 * @param addr : 开始写入的地址 对24c02为0~255 * @param pbuf : 数据数组首地址 * @param datalen : 要写入数据的个数 * @retval 无 */ void at24cxx_write(uint16_t addr, uint8_t *pbuf, uint16_t datalen) { while (datalen--) { at24cxx_write_one_byte(addr, *pbuf); addr++; pbuf++; } }       #ifndef __24CXX_H #define __24CXX_H #include "gd32h7xx.h" #define AT24C01 127 #define AT24C02 255 #define AT24C04 511 #define AT24C08 1023 #define AT24C16 2047 #define AT24C32 4095 #define AT24C64 8191 #define AT24C128 16383 #define AT24C256 32767 /* 开发板使用的是24C02,所以定义EE_TYPE为AT24C02 */ #define EE_TYPE AT24C02 void at24cxx_init(void); /* 初始化IIC */ uint8_t at24cxx_check(void); /* 检查器件 */ uint8_t at24cxx_read_one_byte(uint16_t addr); /* 指定地址读取一个字节 */ void at24cxx_write_one_byte(uint16_t addr,uint8_t data); /* 指定地址写入一个字节 */ void at24cxx_write(uint16_t addr, uint8_t *pbuf, uint16_t datalen); /* 从指定地址开始写入指定长度的数据 */ void at24cxx_read(uint16_t addr, uint8_t *pbuf, uint16_t datalen); /* 从指定地址开始读出指定长度的数据 */ #endif   DEMO 执行结果  

  • 2024-06-19
  • 回复了主题帖: 【国民技术车规MCU N32A455开发板】 N32A455的 IAP

    我是根据他的文档改的

  • 回复了主题帖: 【国民技术车规MCU N32A455开发板】 N32A455的 IAP

    柠溪skr 发表于 2024-6-19 17:46 您好楼主 我想请问一下 我移植到G430为什么不能成功跳转呢 你是啥身份,有没有FAE服务你,一般都有现成的

学过的课程

统计信息

已有93人来访过

  • 芯积分:301
  • 好友:2
  • 主题:46
  • 回复:43

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言