我爱下载

  • 2021-02-28
  • 回复了主题帖: 【GD32E503评测】+sdio读tf卡出现超时问题

    我看st的sdio都有一个命令执行延时的东西,gd32这个有没有类似的功能

  • 2021-02-23
  • 回复了主题帖: 【GD32E503评测】+音乐播放器

    freebsder 发表于 2021-2-22 23:49 看样子做得很完整啊,赞一个!
    功能是完整的,但是细节需要完善

  • 2021-02-21
  • 回复了主题帖: 【GD32E503评测】+音乐播放器

    附上本测试的源码,工程较大,必须分卷压缩了。

  • 2021-02-20
  • 回复了主题帖: 开工大吉,抢楼有礼!预测:2021年电子热门关键词

    人工智能

  • 2021-02-13
  • 回复了主题帖: 【GD32E503评测】+sdio读tf卡出现超时问题

    具体跟踪了一下,发现是CMD16设置块长度命令返回超时。

  • 2021-02-07
  • 回复了主题帖: 【GD32E503评测】+音乐播放器

    annysky2012 发表于 2021-2-7 09:02 很赞很赞。
    谢谢鼓励,继续努力

  • 回复了主题帖: 【GD32E503评测】+音乐播放器

    okhxyyo 发表于 2021-2-7 08:34 主要是因为编辑器的编码跟word的编码是不一样的。所以有些地方word复制过来的编码在编辑器这边代表的含义 ...
    不过那个markdown好像还是挺好的,对于格式的编辑挺不错

  • 回复了主题帖: 【GD32E503评测】+音乐播放器

    okhxyyo 发表于 2021-2-6 21:25 可以把视频上传到大学堂,然后用视频功能把链接贴进来
    感觉咱们这个发帖系统应该在完善一下,对于word格式的识别挺不方便。

  • 回复了主题帖: 【GD32E503评测】+音乐播放器

    okhxyyo 发表于 2021-2-6 21:25 辛苦啦~~很赞~~看视频里你都在擦汗了呀~~
    哈哈,那是放松一下的感觉,在我们这地方,现在想要出点汗还是相当不容易的

  • 回复了主题帖: 【GD32E503评测】+音乐播放器

    okhxyyo 发表于 2021-2-6 21:25 可以把视频上传到大学堂,然后用视频功能把链接贴进来
    好的,下次把视频直接放大学堂里

  • 2021-02-06
  • 回复了主题帖: 【GD32E503评测】+音乐播放器

    不知道如何往帖子中增加视频播放功能,没法子,上传到优酷去,在跟两贴

  • 回复了主题帖: 【GD32E503评测】+音乐播放器

    GUI图形方式播放  

  • 回复了主题帖: 【GD32E503评测】+音乐播放器

    shell方式播放  

  • 发表了主题帖: 【GD32E503评测】+音乐播放器

    本帖最后由 我爱下载 于 2021-2-6 15:41 编辑 # 音乐播放器 本次试用的终极试用项目是一个简单的音乐播放器。由于GD32E503V-Eval评估板上已经集成了TF卡接口,I2S接口及DA输出,LCD显示器和触摸屏,很适合完成一个音乐播放器。 整个音乐播放器系统基于TencentOS-tiny操作系统,使用了如下的组件,包括Shell接口,LCD显示驱动及GUI组件,触摸屏组件,TF卡驱动和Fatfs文件系统组件,I2S接口的PCM文件播放器。 在试用过程中,TOS操作系统,shell接口,LCD显示和GUI组件已经已经正确的驱动和移植了,下面把TF卡驱动和Fatfs文件系统,I2S音乐播放器组件简单介绍一下。 # 关键组件移植 ## TF卡驱动 在试用SDIO的过程中遇到了读取块数据超时的问题始终没有解决,所以本次试用采用SPI接口的方式完成TF卡的驱动。 ## Fatfs文件系统移植 ### 1)驱动接口移植 为了驱动Fatfs,需要完成diskio.c文件的移植,其中包括disk_initalize函数、disk_read函数、disk_write函数、disk_ioctl函数。 1. disk_initalize函数的移植 主要是存储设备的初始化,这里指TF卡的初始化。 ```c DSTATUS disk_initialize (         BYTE pdrv                                /* Physical drive nmuber to identify the drive */ ) {         int result;     status = RES_OK;     result = SD_Init_Config();     if(result == 0xbb)         status = STA_NODISK;     else if(result != 0)         status = STA_NOINIT;     return status; } ``` 2. disk_read TF卡扇区读操作函数。 ```c /*-------------------------------------------------------------*/ /* Read Sector(s)                                              */ /*------------------------------------------------------------*/ DRESULT disk_read (         BYTE pdrv,                /* Physical drive nmuber to identify the drive */         BYTE *buff,                /* Data buffer to store read data */         LBA_t sector,        /* Start sector in LBA */         UINT count                /* Number of sectors to read */ ) {         DRESULT res;         res =  SD_ReadDisk2(buff, sector, count);     return res; } ``` 3. disk_write TF卡扇区写函数。如果希望Fatfs系统是只读的,这个函数可以不实现,要将 FF_FS_READONLY 宏定义置为 1。 ```c /*--------------------------------------------------------------*/ /* Write Sector(s)                                              */ /*--------------------------------------------------------------*/ #if FF_FS_READONLY == 0 DRESULT disk_write (         BYTE pdrv,                        /* Physical drive nmuber to identify the drive */         const BYTE *buff,        /* Data to be written */         LBA_t sector,                /* Start sector in LBA */         UINT count                        /* Number of sectors to write */ ) {         DRESULT res;         res = SD_WriteDisk2((uint8_t*)buff, sector, count);     return res; } #endif ``` 4. disk_ioctl TF卡的一些特性获取和控制接口。 ```c DRESULT disk_ioctl (         BYTE pdrv,                /* Physical drive nmuber (0..) */         BYTE cmd,                /* Control code */         void *buff                /* Buffer to send/receive control data */ ) {     DRESULT res;     if (pdrv) return RES_PARERR;                 /* Check parameter */     if (status & STA_NOINIT) return RES_NOTRDY;   /* Check if drive is ready */     res = RES_ERROR;     switch (cmd) {     case CTRL_SYNC :        /* Wait for end of internal write process of the drive */         if (SD_Select()) res = RES_OK;         SD_DisSelect();         break;     case GET_SECTOR_COUNT : /* Get drive capacity in unit of sector (DWORD) */         *(LBA_t*)buff = SD_GetSectorCount();         if(*(LBA_t*)buff > 0)             res = RES_OK;         break;     case GET_BLOCK_SIZE :   /* Get erase block size in unit of sector (DWORD) */         *(DWORD*)buff = SD_GetBlockSize();         if(*(DWORD*)buff > 0)             res = RES_OK;         break;     case CTRL_TRIM :    /* Erase a block of sectors (used when _USE_ERASE == 1) */         res = RES_OK;   /* FatFs does not check result of this command */         break;     default:         res = RES_PARERR;     }     SD_DisSelect();     return res; } ``` ### 2) Fatfs的基本试用方式 首先需要完成存储设备初始化,res = disk_initialize(0); 然后是文件系统挂在,res = f_mount(&Fatfs,“0:”,0); 此时可以开始在Fatfs的管理下使用文件系统了。下面以读取文件的操作过程为例介绍通过Fatfs读取指定目录下指定文件。 FIL fil;           // File object FRESULT res;  // API result code char *path = “/1.pcm”; char line[256]; //打开一个文件 res = f_open(&fil,path, FA_READ); if (res) {         printf("open file failed.\r\n");         return res; } //获取文件大小 res = f_size(&fil); printf("file size:%d Bytes.\r\n",res); //读取文件内容 while(f_read(&fil,line,255,&i) == FR_OK) {         if(i == 0)             break;         printf("%s\r\n",line);         memset(line,0x0,256); }      //文件读取完,关闭文件句柄 // Close the file f_close(&fil); ## I2S音乐播放器组件 ### 1)        I2S 介绍 I2S(Inter—IC Sound)总线, 又称 集成电路内置音频总线,是飞利浦公司为数字音频设备之间的音频数据传输而制定的一种总线标准,该总线专门用于音频设备之间的数据传输,广泛应用于各种多媒体系统。它采用了沿独立的导线传输时钟与数据信号的设计,通过将数据和时钟信号分离,避免了因时差诱发的失真,为用户节省了购买抵抗音频抖动的专业设备的费用。 I2S接口类似SPI,通常包含三个信号,有时还有主时钟信号: 1、串行时钟SCLK,也叫位始终(BCLK),对应数字音频的每一位数据,SCLK都有一个脉冲。SCLK的频率=2 * 采样频率 * 采样位深。 2、帧时钟LRCK(也称WS),用于切换左右声道的数据。LRCK为‘1’表示传输右声道数据,为“0”则是左声道。LRCK的频率 = 采样频率 3、串行数据(SDATA),就是用二进制补码表示的音频数据,(MSB ---> LSB:数据由高位到低位依次传输) 4、一般还有MCLK,主时钟。 我们本次使用的GD32E503V处理器集成了I2S外设,可以让我们比较方便的实现这样的功能。 2)        PCM文件 PCM文件是模拟音频信号经模数转换(A/D变换)直接形成的二进制序列,该文件没有附加的文件头和文件结束标志。 PCM格式音频文件和WAV文件不同,他是没有文件头的和结束的标志的。为了播放PCM格式文件,需要了解文件的生成规则。通常的PCM格式为16bit,立体声,采样率为44.1KHz。下图为打开一个PCM格式文件中包含的内容,每个16bit为一个音频采样值,两个16位数据就是一个立体声左右两个声道的采样值,我们将这两个值送到DA中,就会发出相应的音频信号了。送入DA的速度就是采样率。 如何从MP3生成所需的PCM格式文件呢?通过ffmpeg这个工具软件可以非常方便的生成所需的PCM格式文件。 ffmpeg -i jiaxunfeihong.mp3 -f s16le -ar 16000 -ac 2 -acodec pcm_s16le pcm16k.pcm 其中-f为存储类型,s16le指的是16位整形数据,le代表的是小端序,对应的是be大端序,一般默认是le小端序。如果搞错了,生成的pcm文件是一串噪音; 其中-ar 是音频采样率,一般有8k,16k等各种不同的采样率; -ac: 通道数,1指单通道 2指立体声 -acodec:生成文件格式,pcm_s16le指的是pcm文件,s16le对应前面-f部分 由于PCM文件的特殊性,我们需要一个缺省的PCM文件描述作为此类文件播放的一个配置。 //default struct wave_pcm_hdr default_pcmwavhdr = {     .riff = { 'R', 'I', 'F', 'F' },     .size_8 = 0,            .wave = {'W', 'A', 'V', 'E'},     .fmt = {'f', 'm', 't', ' '},     .dwFmtSize = 16,     .format_tag = 1,           //PCM:1     .channels = 2,           //通道数     .samples_per_sec = 16000,        //采样率:16000     .avg_bytes_per_sec = 32000,        //平均流量:samples_per_sec * dwFmtSize / 8     .block_align = 2,     .bits_per_sample = 16,     .data = {'d', 'a', 't', 'a'},     .data_size = 0                  //播放文件大小 }; ### 3)        设备初始化 I2S设置初始化,包括设备时钟初始化、端口引脚初始化、I2S设备初始化。对于I2S设备的初始化需要注意,采样时钟需要根据播放的文件中标示的采样率来初始化,对于我们这里的PCM文件,由于我们生成的文件都是固定采样率16000;每通道数据长度16bit。 ```c void i2s_config() {     /* enable the GPIO clock */     rcu_periph_clock_enable(RCU_GPIOB);     rcu_periph_clock_enable(RCU_GPIOC);     rcu_periph_clock_enable(RCU_AF);     // enable I2S1 clock     rcu_periph_clock_enable(RCU_SPI1);     /* configure I2S1 pins: PB12(I2S_WS), PB13(I2S_CK), PB15(I2S_DIN) */     gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,                                                   GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_15); // PC6(I2S_MCK)     gpio_init(GPIOC, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6);    spi_i2s_deinit(SPI1);    // I2S1 peripheral configuration     i2s_psc_config(SPI1, i2saudiofreq, I2S_FRAMEFORMAT_DT16B_CH16B, I2S_MCLKOUTPUT);     i2s_init(SPI1, I2S_MODE_MASTERTX, I2S_STANDARD, I2S_CKPL_HIGH);        // enable the I2S1 peripheral     i2s_enable(SPI1); } ``` ### 4)        环形缓冲区 由于我们的采用从文件中读取音频数据,实时的通过I2S接口送入DA中进行音频播放,考虑到设备间存在数据消耗量速度的不同,因此,这里设计了一个环形缓冲区,完成文件数据缓冲。缓冲区大小为8K,考虑到数据实际的传送过程中为16位的,所以最小更新单位为2字节。缓冲区的填充以缓冲区消耗量的一半作为填充数据的触发条件。 ```c #define WAVE_BUFF_MAX        8192 volatile uint32_t wavebuf_tail = WAVE_BUFF_MAX-2; volatile uint32_t wavebuf_head = 0; uint8_t wavetestdata[WAVE_BUFF_MAX]; void fill_wave_buf(FIL *fp) {         uint32_t len = (wavebuf_head>=wavebuf_tail)?(wavebuf_head - wavebuf_tail):(WAVE_BUFF_MAX + wavebuf_head - wavebuf_tail);         uint8_t line[256];         uint32_t i;         if(fp == NULL)                 return ;         if(len > WAVE_BUFF_MAX/2)         {                 while(wavebuf_head != wavebuf_tail)                 {                         if(f_read(fp,line,2,&i) == FR_OK)                         {                                 memcpy(wavetestdata+ wavebuf_tail,line,2);                                 wavebuf_tail+=2;                                 if(wavebuf_tail >= WAVE_BUFF_MAX)                                         wavebuf_tail = 0;                         }                 }                         } } ``` ### 5)        中断数据发送 为了保证数据传输过程的实时性,采用中断方式完成数据传递,由于I2S数据传输过程中是要考虑到左右声道的,因此对于单声道和立体声数据内容的传递是有差别的。 ```c uint16_t read_half_word_buf(void) {        static  uint32_t monovar = 0, tmpvar = 0;     /* test if the left channel is to be sent */     if(0 == monovar)        //左声道         {         tmpvar = (*(__IO uint16_t *)(wavetestdata + wavebuf_head));         /* increment the mono variable only if the file is in mono format */         if(CHANNEL_MONO == wave_struct.numchannels)             /* increment the monovar variable */             monovar++;                     }         else/* right channel */         {         tmpvar = (*(__IO uint16_t *)(wavetestdata + wavebuf_head));        /* reset the monovar variable */         monovar = 0; //        /* return the previous read data in mono format */ //        return tmpvar;     }                 wavebuf_head+=2;         if(wavebuf_head >= WAVE_BUFF_MAX)                 wavebuf_head = 0;      /* return the read value */      return tmpvar>>1; } ``` ##音乐播放器工作机制 音乐播放器在前面几个测试研究基础上,解决了操作系统、shell终端、图形库移植等使用问题后,发展而来的。它以Tos操作系统为基础,构建了shell处理任务,gui处理任务,音频播放消息处理任务等几个关键任务。支持通过shell终端的播放器控制方式。同时支持gui图形界面的播放器控制方式。 # 音乐播放器展示 本音乐播放器支持通过shell终端和gui图形界面两种控制播放方法。 ## shell方式播放 https://v.youku.com/v_show/id_XNTA5OTEyMzg0OA==.html ## gui图形方式播放 https://v.youku.com/v_show/id_XNTA5OTEyNDU3Ng==.html 补充内容 (2021-2-21 17:14): 17楼附上本测试的源码

  • 2021-02-02
  • 回复了主题帖: 【GD32E503评测】+sdio读tf卡出现超时问题

    littleshrimp 发表于 2021-2-1 13:13 问题后来解决了吗 用不用联系厂家给你看看?
    没有解决,不知道是不是使用问题。

  • 2021-01-26
  • 回复了主题帖: 【GD32E503评测】+Letter-shell移植到gd32e503v

    freebsder 发表于 2021-1-25 22:26 谢谢分享,是个好东西呢。
    谢谢鼓励,后面的音频播放器基本完成,完善一下准备出结项的帖子了

  • 回复了主题帖: 【GD32E503评测】+Letter-shell移植到gd32e503v

    mig29 发表于 2021-1-25 19:41 楼主太强了,介绍了不少好东西!
    拿来学习,都是高人的东西移植到这里,希望对大家有用

  • 2021-01-22
  • 发表了主题帖: 【GD32E503评测】+Letter-shell移植到gd32e503v

    移植Letter-shell Letter-shell 是一个开源项目,功能强大的嵌入式shell,遵循MIT开源许可协议。 Letter-shell 3.0是一个C语言编写的可以嵌入在程序中的嵌入式shell,可以通过串口命令行完成用户函数的调用、运行。 目前支持的功能有:     命令自动补全 快捷键功能定义 命令权限管理 用户管理 变量支持 代理函数和参数代理解析 源代码获取地址:https://github.com/NevermindZZT/letter-shell 基于tos的shell移植       前面我们成功的移植了tos到GD32E503V上,下面基于任务级别的shell移植。 串口驱动 官方例程中已经做了串口的初始化部分,这里不详细描述。为了更好的移植shell,我们需要增加串口输出的处理。 void uart_output(char dat) {    usart_data_transmit(EVAL_COM0, (uint8_t)dat);    while(RESET == usart_flag_get(EVAL_COM0, USART_FLAG_TBE));  } Shell_port.c文件中关键函数的移植 为了让shell成功的运行起来,不许完成shell_port.c中如下几个关键函数的移植工作,包括, userShellWrite,userShellRead。 /**  * @brief 用户shell写  *  * @param data 数据  */ void userShellWrite(char data) { //    serialTransmit(&debugSerial, (uint8_t *)&data, 1, 0xFF); uart_output(data); }   /**  * @brief 用户shell读  *  * @param data 数据  * @return char 状态  */ signed char userShellRead(char *data) {     return 0; } /**  * @brief 用户shell初始化  *  */ void userShellInit(void) {     shell.write = userShellWrite; //    shell.read = userShellRead;     shellInit(&shell, shellBuffer, 512); } 可见,userShellRead函数我们没有处理,那么shell是如何获取到串口输入数据的呢? 因为shell提供了一个可以在中断级调用的处理接口,可以直接获取串口数据进入shell的处理流程中。 我们只需要在串口的接收中断函数中调用如下函数: shellHandler(&shell, rx); 创建shell的任务 官方网站中如下介绍,对于运行在操作系统的情况,建立shellTask任务(确保sell_cfg.h中的配置无误),任务参数为shell对象 OsTaskCreate(shellTask, &shell, ...); 对于裸机环境,在主循环中调用shellTask,或者在接收到数据时,调用shellHandler 我们利用tos的任务方式建立了shellTask任务。 #define SHELL_TASK_STK_SIZE       2048 extern void shell_task_entry(void *arg); osThreadDef(shell_task_entry, osPriorityBelowNormal, 1, SHELL_TASK_STK_SIZE); //shell处理任务 void shell_task_entry(void *arg) {   userShellInit();   while(1)   {                  tos_task_delay(10);        shellTask(&shell);     } }   其它配置       定义宏SHELL_GET_TICK()为获取系统tick函数,使能tab双击操作,用户长帮助补全 /** * @brief 获取系统时间(ms) *       定义此宏为获取系统Tick,如`HAL_GetTick()` * @NOTE 此宏不定义时无法使用双击tab补全命令help,无法使用shell超时锁定 */ #define     SHELL_GET_TICK()            SysTick_GetCount()   Shell的任务创建方式    函数创建和调用方式 创建可管理用户函数方法: int func(intargc, char *argv[]) {         printf("%dparameter(s)\r\n", argc);      for (char i =1; i < argc; i++)         {             printf("%s\r\n",argv);         } } 导出定义方式: SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN),func, func, test);     调用方式:         letter:/$ func "hello world" 变量创建和调用方式 导出变量: 变量导出使用SHELL_EXPORT_VAR宏,支持整形(char,short, int),字符串,指针以及节点变量,变量导出需要使用引用的方式,如果不允许对变量进行修改,在属性中添加SHELL_CMD_READ_ONLY int varInt = 0; SHELL_EXPORT_VAR(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_VAR_INT), varInt, &varInt, test); char str[] = "test string"; SHELL_EXPORT_VAR(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_VAR_STRING), varStr, str, test); Log log; SHELL_EXPORT_VAR(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_VAR_POINT), log, &log, test); 查看变量: 在命令行直接输入导出的变量名即可查看变量当前的值 letter:/$ varIntvarInt = 0, 0x00000000 letter:/$ varStrvarStr = "test string" 修改变量: 使用setVar命令修改变量的值,对于字符串型变量,请确认字符串有分配足够的空间,指针类型的变量不可修改 letter:/$ setVar varInt 45678 varInt = 45678, 0x0000b26e letter:/$ setVar varStr "hello" varStr = "hello" 使用变量: letter shell 3.0的变量可以在命令中作为参数传递,对于需要传递结构体引用到命令中的场景特别适用,使用$+变量名的方式传递 letter:/$ shellPrint $shell "hello world\r\n"hello world   移植效果展示    

  • 2021-01-18
  • 回复了主题帖: 【GD32E503评测】+sdio读tf卡出现超时问题

    littleshrimp 发表于 2021-1-18 15:36 没有这个板子,没法测试,我猜网方的全程应该是验证过的吧。 你再看看手册的这些参考和你的SD卡有没有不 ...
    测试了2个tf卡,容量分别为8G和32G。实测是现象相同,我可以正确的获取tf卡的容量,扇区等相关信息,可以读取地址为0的数据内容,但是其它地址,地址为512的整数倍开始地址,反馈信息为超时。

  • 2021-01-16
  • 发表了主题帖: 【GD32E503评测】+ littlegl移植到GD32E503V-EVAL

    LittlevGL移植到GD32E503V 前面已经成功的移植了tos到GD32E503V处理器上,基于tos,将LittlevGL移植到GD32E503V-eval开发板。 LittlevGL介绍 LittlevGL是一个免费的开放源代码图形库,它提供创建嵌入式GUI所需的一切,它具有易于使用的图形元素,精美的视觉效果和低内存占用。 特性: 16, 32 or 64 bit microcontroller or processor 16 MHz clock speed 8 kB RAM for static data and >2 KB RAM for dynamic data (graphical objects) 64 kB program memory (flash) 支持GPU 硬件连接关系     LittleGL系统需要LCD显示器和触摸屏输入两样主要的硬件设备。 图 LCD和触摸屏接口 GD32E503V-EVAL开发板使用EXMC模块来控制LCD。通过GPIO模拟SPI接口连接触摸屏。 LCD和触摸屏接口 GD32E503V 端口 D0~D15 EXMC_D0~EXMC_D15 LCD_CS PD7 LCD_RS EXMC_A23 LCD_WR PD5 LCD_RD PD4 LCD_RST NRST TP_MISO PA6 TP_MOSI PA7 TP_SCK PA5 TP_INT PE5 TP_CS PE6   驱动代码 LCD驱动 我们在官方的驱动中已经包含了LCD的驱动代码,这里不详细解释。 触摸屏驱动 官方驱动中包含了触摸屏驱动代码,直接使用就可以,不详细解释。 LittleGL移植 目录结构 还包括一个example目录,我们在测试的时候会选择其中的一个加入工程中。 输入设备的移植 输入设备在lvgl/port/lv_port_indev.c中,包括触摸屏,鼠标,键盘,编码器,和按键输入五中输入手段。 本次仅移植触摸屏输入方式,在void lv_port_indev_init(void)函数中,除触摸屏输入的初始化外,其它输入方式暂时隐藏起来。 触摸屏需要完成如下几个函数的移植: static void touchpad_init(void)函数的移植,这个函数用来完成触摸屏的初始化工作,可以将官方提供的触摸屏初始化函数在此处调用。 touch_panel_gpio_configure(); static bool touchpad_is_pressed(void)函数的移植,这个函数用来完成触摸屏被点击后的检测通知函数,将触摸屏的中断输入引脚PE5的状态检测放到这里。 static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)函数的移植,这个函数用来读取点击的触摸屏的位置信息。 static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y) {     /*Your code comes here*/ uint16_t touch_ad_x,touch_ad_y; touch_ad_xy_get(&touch_ad_x, &touch_ad_y);     (*x) = touch_coordinate_x_get(touch_ad_x);     (*y) = LCD_Y - touch_coordinate_y_get(touch_ad_y); } 显示设备的移植 显示设备在lvgl/port/lv_port_disp.c中,包括显示设备初始化,和显示内容刷新及显示缓存的定义。 在void lv_port_disp_init(void)中,重点需要移植的内容: 显示设备的初始化 static void disp_init(void)函数,将官方驱动中,显示设备的初始化函数放在这个函数中,完成显示设备初始化。 显示缓存设置 这个是显存初始化中的一段说明,如下说明了显存包含3种类型的缓存,第一种为分配若干行的显示内容大小的内存作为显存,这是最小的显存分配方式,对于嵌入式系统中使用最广范的一种,通常分配为20行的内存空间; 第二种为分配两个大小相同的显示缓冲区,每个显示缓冲区为若干行显示内容对应的内存。这种方式通常采用DMA的方式刷新显示混存。 第三种为分配两个大小相同的显示缓冲区,每个显示缓冲区的大小为整个屏幕对应的显示内容对应的内存大小,这种方式对于屏幕的刷新速度是最快的,但是占用的内存也是最多的。 /* LittlevGL requires a buffer where it draws the objects. The buffer's has to be greater than 1 display row      *      * There are three buffering configurations:      * 1. Create ONE buffer with some rows:      *      LittlevGL will draw the display's content here and writes it to your display      *      * 2. Create TWO buffer with some rows:      *      LittlevGL 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 LittlevGL 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. Create TWO screen-sized buffer:      *      Similar to 2) but the buffer have to be screen sized. When LittlevGL is ready it will give the      *      whole frame to display. This way you only need to change the frame buffer's address instead of      *      copying the pixels.      * */       /* Example for 1) */     static lv_disp_buf_t disp_buf_1;     static lv_color_t buf1_1[LV_HOR_RES_MAX * 20];                      /*A buffer for 20 rows*/     lv_disp_buf_init(&disp_buf_1, buf1_1, NULL, LV_HOR_RES_MAX * 20);   /*Initialize the display buffer*/       /* Example for 2) */ //    static lv_disp_buf_t disp_buf_2; //    static lv_color_t buf2_1[LV_HOR_RES_MAX * 40];                        /*A buffer for 10 rows*/ //    static lv_color_t buf2_2[LV_HOR_RES_MAX * 40];                        /*An other buffer for 10 rows*/ //    lv_disp_buf_init(&disp_buf_2, buf2_1, buf2_2, LV_HOR_RES_MAX * 40);   /*Initialize the display buffer*/       /* Example for 3) */ //    static lv_disp_buf_t disp_buf_3; //    static lv_color_t buf3_1[LV_HOR_RES_MAX * LV_VER_RES_MAX];            /*A screen sized buffer*/ //    static lv_color_t buf3_2[LV_HOR_RES_MAX * LV_VER_RES_MAX];            /*An other screen sized buffer*/ //    lv_disp_buf_init(&disp_buf_3, buf3_1, buf3_2, LV_HOR_RES_MAX * LV_VER_RES_MAX);   /*Initialize the display buffer*/ 对于本次的显示缓存,采用第一种分配方式,20行的显示内容对应的内存大小。 屏幕尺寸大小定义 这个地方重新定义了屏幕的尺寸,GD32E503V-eval的评估板对应的显示器为320x240,因此填充为如下。     /*Set the resolution of the display*/     disp_drv.hor_res = 240;     disp_drv.ver_res = 320; 显示刷新函数的移植 static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)的移植是最重要的物理设备接口函数,完成从显示缓存到LCD显示内容刷新的接口函数,利用官方提供的驱动接口,填充为: static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) {     /*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_point_set(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); } 运行的其它移植 为了保证LittleGL的正确运行,还需要添加如下几个部分的添加。 1ms定时器的添加 在系统的1ms定时器,必须添加lv_tick_inc(1);的调用,用于gui的内容定时。 运行任务的添加 为了让gui显示允许,需要创建一个任务来定时的刷新gui的函数。 基于tos的gui运行任务,通常要求任务堆栈不小于4K,优先级应略高。 #define LVGL_TASK_STK_SIZE       4096 extern void lvgl_task_entry(void *arg); osThreadDef(lvgl_task_entry, osPriorityNormal, 1, LVGL_TASK_STK_SIZE); 在任务中应定时调用gui的处理函数。 void lvgl_task_entry(void *arg) { lv_init();     lv_port_disp_init();     lv_port_indev_init(); demo_create();     while (1) {     lv_task_handler();         tos_task_delay(30);     } }     到此LittleGL的gui移植已经基本完成。   LittleGL demon测试展示 我们在前面移植的基础上,添加example中的demo来展示一下实际的显示效果。          

最近访客

< 1/6 >

统计信息

已有242人来访过

  • 芯币:1076
  • 好友:4
  • 主题:41
  • 回复:206
  • 课时:--
  • 资源:1

留言

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


现在还没有留言