DDZZ669

  • 2020-10-18
  • 回复了主题帖: 【GD32450I-EVAL】+ 09以太网模块测试

    w494143467 发表于 2020-10-18 08:47 感觉还不错,搭建起来应该挺快的吧?
    用的资料里的例子程序,搭建起来较快,换下跳线帽,改下IP就行了,如果要自己再添加功能,需要研究研究程序,了解网络的知识。

  • 2020-10-17
  • 发表了主题帖: 【GD32450I-EVAL】+10SD卡测试

    本帖最后由 DDZZ669 于 2020-10-17 21:37 编辑 SD卡,即安全数码卡(Secure Digital ),GD32的SDIO接口可以用来驱动SD卡,另外,SDIO还定义了SD I/O卡、多媒体卡(MMC)和CE-ATA卡主机接口。   1 SD卡基础介绍 1.1 接口 板子上的SD卡接口原理图: 1.2 SD卡与microSD卡 microSD卡又叫TF卡,是SD卡的缩小版,平时用的较多也是microSD,也经常习惯把microSD卡叫做SD卡。 1.3 容量 SDSC:标准容量卡(Secure Digital Standard Capacity),上限至2GB,支持FAT 12, 16格式 SDHC:高容量卡(Secure Digital High Capacity),2GB至 32GB,支持FAT 32格式 SDXC:容量扩大化卡(Secure Digital eXtended Capacity)32GB至2TB,支持exFAT格式   2 SDIO 2.1 基本结构 GD32的SDIO 的结构框图如下图,主要由APB 接口和SDIO 适配器两大部分组成。 APB 接口:包括通过APB2 总线访问的寄存器、用于数据传输的FIFO 单元以及产生中断和DMA 请求信号。 寄存器单元:包含所有的系统寄存器,生成信号用于控制卡与控制器之间的通信。 数据FIFO 单元:有一个数据缓冲区,用于发送和接收 FIFO SDIO 适配器:由控制单元、命令单元和数据单元组成。控制单元管理时钟信号,命令单元管理命令的传输,数据单元管理数据的传输。 SDIO_CLK:SDIO 控制器提供给卡的时钟。每个时钟周期在命令线(SDIO_CMD)和所有的数据线(SDIO_DAT)上直接发送一位命令或数据。对于SD卡可以在0 MHz 到25 MHz。 SDIO_CMD:该信号是双向命令通道,用于卡的初始化和命令的传输。命令从SDIO 控制器发送到卡,响应从卡发送到主机。 SDIO_DAT[7:0]:这些信号线都是双向数据通道。数据信号线操作在推挽模式。每次只有卡或者主机会驱动这些信号。默认情况下,上电或者复位后仅 DAT0 用于数据传输。 2.2 寄存器 SD卡有一些特殊功能寄存器 CID:宽度128位,卡标识号,每个读 写( RW )卡应具有唯一的标识号。 RCA:宽度16位,卡相对地址,在初始化的时候确定。 CSD:宽度128位,卡描述数据:卡操作条件的信息。 SCR:宽度64位,SD卡配置寄存器:SD卡特定信息数据。 OCR:宽度32位,操作条件寄存。 2.3 命令 SDIO通过发送各种命令的方式来操作SD卡,如下图:   程序中的命令列表: /* SD memory card bus commands index */ #define SD_CMD_GO_IDLE_STATE ((uint8_t)0) /* CMD0, GO_IDLE_STATE */ #define SD_CMD_ALL_SEND_CID ((uint8_t)2) /* CMD2, ALL_SEND_CID */ #define SD_CMD_SEND_RELATIVE_ADDR ((uint8_t)3) /* CMD3, SEND_RELATIVE_ADDR */ #define SD_CMD_SET_DSR ((uint8_t)4) /* CMD4, SET_DSR */ #define SD_CMD_SWITCH_FUNC ((uint8_t)6) /* CMD6, SWITCH_FUNC */ #define SD_CMD_SELECT_DESELECT_CARD ((uint8_t)7) /* CMD7, SELECT_DESELECT_CARD */ #define SD_CMD_SEND_IF_COND ((uint8_t)8) /* CMD8, SEND_IF_COND */ #define SD_CMD_SEND_CSD ((uint8_t)9) /* CMD9, SEND_CSD */ #define SD_CMD_SEND_CID ((uint8_t)10) /* CMD10, SEND_CID */ #define SD_CMD_STOP_TRANSMISSION ((uint8_t)12) /* CMD12, STOP_TRANSMISSION */ #define SD_CMD_SEND_STATUS ((uint8_t)13) /* CMD13, SEND_STATUS */ #define SD_CMD_GO_INACTIVE_STATE ((uint8_t)15) /* CMD15, GO_INACTIVE_STATE */ #define SD_CMD_SET_BLOCKLEN ((uint8_t)16) /* CMD16, SET_BLOCKLEN */ #define SD_CMD_READ_SINGLE_BLOCK ((uint8_t)17) /* CMD17, READ_SINGLE_BLOCK */ #define SD_CMD_READ_MULTIPLE_BLOCK ((uint8_t)18) /* CMD18, READ_MULTIPLE_BLOCK */ #define SD_CMD_WRITE_BLOCK ((uint8_t)24) /* CMD24, WRITE_BLOCK */ #define SD_CMD_WRITE_MULTIPLE_BLOCK ((uint8_t)25) /* CMD25, WRITE_MULTIPLE_BLOCK */ #define SD_CMD_PROG_CSD ((uint8_t)27) /* CMD27, PROG_CSD */ #define SD_CMD_SET_WRITE_PROT ((uint8_t)28) /* CMD28, SET_WRITE_PROT */ #define SD_CMD_CLR_WRITE_PROT ((uint8_t)29) /* CMD29, CLR_WRITE_PROT */ #define SD_CMD_SEND_WRITE_PROT ((uint8_t)30) /* CMD30, SEND_WRITE_PROT */ #define SD_CMD_ERASE_WR_BLK_START ((uint8_t)32) /* CMD32, ERASE_WR_BLK_START */ #define SD_CMD_ERASE_WR_BLK_END ((uint8_t)33) /* CMD33, ERASE_WR_BLK_END */ #define SD_CMD_ERASE ((uint8_t)38) /* CMD38, ERASE */ #define SD_CMD_LOCK_UNLOCK ((uint8_t)42) /* CMD42, LOCK_UNLOCK */ #define SD_CMD_APP_CMD ((uint8_t)55) /* CMD55, APP_CMD */ #define SD_CMD_GEN_CMD ((uint8_t)56) /* CMD56, GEN_CMD */ /* SD memory card application specific commands index */ #define SD_APPCMD_SET_BUS_WIDTH ((uint8_t)6) /* ACMD6, SET_BUS_WIDTH */ #define SD_APPCMD_SD_STATUS ((uint8_t)13) /* ACMD13, SD_STATUS */ #define SD_APPCMD_SEND_NUM_WR_BLOCKS ((uint8_t)22) /* ACMD22, SEND_NUM_WR_BLOCKS */ #define SD_APPCMD_SET_WR_BLK_ERASE_COUNT ((uint8_t)23) /* ACMD23, SET_WR_BLK_ERASE_COUNT */ #define SD_APPCMD_SD_SEND_OP_COND ((uint8_t)41) /* ACMD41, SD_SEND_OP_COND */ #define SD_APPCMD_SET_CLR_CARD_DETECT ((uint8_t)42) /* ACMD42, SET_CLR_CARD_DETECT */ #define SD_APPCMD_SEND_SCR ((uint8_t)51) /* ACMD51, SEND_SCR */ 3 例子程序分析 3.1 SD卡初始化 sd_error_enum sd_config(void) { sd_error_enum status = SD_OK; uint32_t cardstate = 0; /* initialize the card */ status = sd_init();//SD卡初始化---GPIO、SDIO if(SD_OK == status) { status = sd_card_information_get(&sd_cardinfo);//获取SD卡信息 } if(SD_OK == status) { status = sd_card_select_deselect(sd_cardinfo.card_rca);//挂载SD卡 } status = sd_cardstatus_get(&cardstate); if(cardstate & 0x02000000) { printf("\r\n the card is locked!"); while (1){ } } if ((SD_OK == status) && (!(cardstate & 0x02000000))) { /* set bus mode */ status = sd_bus_mode_config(SDIO_BUSMODE_4BIT);//配置总线模式 } if (SD_OK == status) { /* set data transfer mode */ status = sd_transfer_mode_config( SD_POLLING_MODE );//配置传输模式 } return status; } 3.2 读取SD卡基本信息 包括sd卡的版本,类型,容量,块大小与数量等。 void card_info_get(void) { uint8_t sd_spec, sd_spec3, sd_spec4, sd_security; uint32_t block_count, block_size; uint16_t temp_ccc; printf("\r\n Card information:"); //SD卡版本 sd_spec = (sd_scr[1] & 0x0F000000) >> 24; sd_spec3 = (sd_scr[1] & 0x00008000) >> 15; sd_spec4 = (sd_scr[1] & 0x00000400) >> 10; if(2 == sd_spec) { if(1 == sd_spec3) { if(1 == sd_spec4) { printf("\r\n## Card version 4.xx ##"); } else { printf("\r\n## Card version 3.0x ##"); } } else { printf("\r\n## Card version 2.00 ##"); } } else if(1 == sd_spec) { printf("\r\n## Card version 1.10 ##"); } else if(0 == sd_spec) { printf("\r\n## Card version 1.0x ##"); } //SD卡类型 sd_security = (sd_scr[1] & 0x00700000) >> 20; if(2 == sd_security) { printf("\r\n## SDSC card ##");//标准容量卡SDSC,最大2G } else if(3 == sd_security) { printf("\r\n## SDHC card ##");//高容量卡SDHC,最大32G } else if(4 == sd_security) { printf("\r\n## SDXC card ##"); } block_count = (sd_cardinfo.card_csd.c_size + 1)*1024; block_size = 512; //SD卡容量 printf("\r\n## Device size is %dKB ##", sd_card_capacity_get()); //SD卡 块的大小 printf("\r\n## Block size is %dB ##", block_size); //SD卡 块的个数 printf("\r\n## Block count is %d ##", block_count); if(sd_cardinfo.card_csd.read_bl_partial) { printf("\r\n## Partial blocks for read allowed ##" ); } if(sd_cardinfo.card_csd.write_bl_partial) { printf("\r\n## Partial blocks for write allowed ##" ); } //SD卡 卡命令集 temp_ccc = sd_cardinfo.card_csd.ccc; printf("\r\n## CardCommandClasses is: %x ##", temp_ccc); if((SD_CCC_BLOCK_READ & temp_ccc) && (SD_CCC_BLOCK_WRITE & temp_ccc)) { printf("\r\n## Block operation supported ##"); } if(SD_CCC_ERASE & temp_ccc) { printf("\r\n## Erase supported ##"); } if(SD_CCC_WRITE_PROTECTION & temp_ccc) { printf("\r\n## Write protection supported ##"); } if(SD_CCC_LOCK_CARD & temp_ccc) { printf("\r\n## Lock unlock supported ##"); } if(SD_CCC_APPLICATION_SPECIFIC & temp_ccc) { printf("\r\n## Application specific supported ##"); } if(SD_CCC_IO_MODE & temp_ccc) { printf("\r\n## I/O mode supported ##"); } if(SD_CCC_SWITCH & temp_ccc) { printf("\r\n## Switch function supported ##"); } } 3.3 SD卡测试 //开始SD卡测试 printf("\r\n\r\n Card test:"); /* single block operation test =============单个块读写测试============= */ sd_error = sd_block_write(buf_write, 100*512, 512);//---------写入数据 if(SD_OK != sd_error) { printf("\r\n Block write fail!"); /* turn on LED1, LED3 and turn off LED2 */ gd_eval_led_on(LED1); gd_eval_led_on(LED3); gd_eval_led_off(LED2); while (1) { } } else { printf("\r\n Block write success!"); } sd_error = sd_block_read(buf_read, 100*512, 512);//-----------读取数据 if(SD_OK != sd_error) { printf("\r\n Block read fail!"); /* turn on LED1, LED3 and turn off LED2 */ gd_eval_led_on(LED1); gd_eval_led_on(LED3); gd_eval_led_off(LED2); while (1) { } }else { printf("\r\n Block read success!"); //打印读取到的数据 pdata = (uint8_t *)buf_read; /* print data by USART */ printf("\r\n"); for(i = 0; i < 128; i++) { printf(" %3d %3d %3d %3d ", *pdata, *(pdata+1), *(pdata+2), *(pdata+3)); pdata += 4; if(0 == (i + 1) % 4) { printf("\r\n"); } } } /* lock and unlock operation test */ if(SD_CCC_LOCK_CARD & sd_cardinfo.card_csd.ccc) { /* lock the card */ sd_error = sd_lock_unlock(SD_LOCK); //-----------SD卡加锁 if(SD_OK != sd_error) { printf("\r\n Lock failed!"); /* turn on LED1, LED3 and turn off LED2 */ gd_eval_led_on(LED1); gd_eval_led_on(LED3); gd_eval_led_off(LED2); while (1) { } }else { printf("\r\n The card is locked!"); } sd_error = sd_erase(100*512, 101*512);//-------------SD卡擦除 if(SD_OK != sd_error) { printf("\r\n Erase failed!"); } else { printf("\r\n Erase success!"); } /* unlock the card */ sd_error = sd_lock_unlock(SD_UNLOCK);//--------------SD卡解锁 if(SD_OK != sd_error) { printf("\r\n Unlock failed!"); /* turn on LED1, LED3 and turn off LED2 */ gd_eval_led_on(LED1); gd_eval_led_on(LED3); gd_eval_led_off(LED2); while (1){ } } else { printf("\r\n The card is unlocked!"); } sd_error = sd_erase(100*512, 101*512);//------------SD卡擦除 if(SD_OK != sd_error){ printf("\r\n Erase failed!"); }else{ printf("\r\n Erase success!"); } sd_error = sd_block_read(buf_read, 100*512, 512);//--------------SD卡读取 if(SD_OK != sd_error) { printf("\r\n Block read fail!"); /* turn on LED1, LED3 and turn off LED2 */ gd_eval_led_on(LED1); gd_eval_led_on(LED3); gd_eval_led_off(LED2); while (1){ } } else { printf("\r\n Block read success!"); pdata = (uint8_t *)buf_read; /* print data by USART */ printf("\r\n"); for(i = 0; i < 128; i++) { printf(" %3d %3d %3d %3d ", *pdata, *(pdata+1), *(pdata+2), *(pdata+3)); pdata += 4; if(0 == (i + 1) % 4) { printf("\r\n"); } } } } /* multiple blocks operation test =============多个块读写测试============= */ sd_error = sd_multiblocks_write(buf_write, 200*512, 512, 3);//----------多个块写入数据 if(SD_OK != sd_error) { printf("\r\n Multiple block write fail!"); /* turn on LED1, LED3 and turn off LED2 */ gd_eval_led_on(LED1); gd_eval_led_on(LED3); gd_eval_led_off(LED2); while (1){ } } else { printf("\r\n Multiple block write success!"); } sd_error = sd_multiblocks_read(buf_read, 200*512, 512, 3);//------------多个块读取数据 if(SD_OK != sd_error) { printf("\r\n Multiple block read fail!"); /* turn on LED1, LED3 and turn off LED2 */ gd_eval_led_on(LED1); gd_eval_led_on(LED3); gd_eval_led_off(LED2); while (1){ } } else { printf("\r\n Multiple block read success!"); pdata = (uint8_t *)buf_read; /* print data by USART */ printf("\r\n"); for(i = 0; i < 512; i++) { printf(" %3d %3d %3d %3d ", *pdata, *(pdata+1), *(pdata+2), *(pdata+3)); pdata += 4; if(0 == (i + 1) % 4) { printf("\r\n"); } } } 3.4 测试结果 通过串口打印测试结果,从结果来看,测试用到的microSD卡为:4.xx版的SDHC大容量卡,16GB(15441920KB),支持块操作,支持加锁解锁操作。 数据读写测试了单个块写入读取与多个块写入读取正常,在加锁后则无法读取,解锁后可正常读取。     Card init success! Card information: ## Card version 4.xx ## ## SDHC card ## ## Device size is 15441920KB ## ## Block size is 512B ## ## Block count is 30883840 ## ## CardCommandClasses is: db7 ## ## Block operation supported ## ## Erase supported ## ## Lock unlock supported ## ## Application specific supported ## ## Switch function supported ## Card test: Block write success! Block read success! 0 0 0 0 1 0 0 0 2 0 0 0 3 0 0 0 4 0 0 0 5 0 0 0 6 0 0 0 7 0 0 0 ...略去若干行 112 0 0 0 113 0 0 0 114 0 0 0 115 0 0 0 116 0 0 0 117 0 0 0 118 0 0 0 119 0 0 0 120 0 0 0 121 0 0 0 122 0 0 0 123 0 0 0 124 0 0 0 125 0 0 0 126 0 0 0 127 0 0 0 The card is locked! Erase failed! The card is unlocked! Erase success! Block read success! 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ...略去若干行 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Multiple block write success! Multiple block read success! 0 0 0 0 1 0 0 0 2 0 0 0 3 0 0 0 4 0 0 0 5 0 0 0 6 0 0 0 7 0 0 0 ...略去若干行 112 0 0 0 113 0 0 0 114 0 0 0 115 0 0 0 116 0 0 0 117 0 0 0 118 0 0 0 119 0 0 0 120 0 0 0 121 0 0 0 122 0 0 0 123 0 0 0 124 0 0 0 125 0 0 0 126 0 0 0 127 0 0 0 128 0 0 0 129 0 0 0 130 0 0 0 131 0 0 0 132 0 0 0 133 0 0 0 134 0 0 0 135 0 0 0 ...略去若干行 236 0 0 0 237 0 0 0 238 0 0 0 239 0 0 0 240 0 0 0 241 0 0 0 242 0 0 0 243 0 0 0 244 0 0 0 245 0 0 0 246 0 0 0 247 0 0 0 248 0 0 0 249 0 0 0 250 0 0 0 251 0 0 0 252 0 0 0 253 0 0 0 254 0 0 0 255 0 0 0 0 1 0 0 1 1 0 0 2 1 0 0 3 1 0 0 4 1 0 0 5 1 0 0 6 1 0 0 7 1 0 0 ...略去若干行 112 1 0 0 113 1 0 0 114 1 0 0 115 1 0 0 116 1 0 0 117 1 0 0 118 1 0 0 119 1 0 0 120 1 0 0 121 1 0 0 122 1 0 0 123 1 0 0 124 1 0 0 125 1 0 0 126 1 0 0 127 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ...略去若干行 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  

  • 发表了主题帖: 【GD32450I-EVAL】+ 09以太网模块测试

    本帖最后由 DDZZ669 于 2020-10-17 21:27 编辑 1服务器/客户端测试 1.1跳帽切换到ETH 以太网模块的某些引脚与摄像头、LCD等模块共用引脚,需要切换跳线帽,将JP12,JP13,JP17,JP18,JP20,JP22 跳线帽切换到ETH,如下图: 1.2设置IP 电脑和板子要在同一个局域网内,我这里有一个无线路由器,电脑通过无线连接路由器,板子通过网线连接路由器。电脑中先通过ipconfig命令查看电脑的IP,我的是192.168.5.102,板子可以随意设置一个同网段IP,如192.168.5.112,将IP信息在程序中作相应修改,如下图所示: 修改后,程序烧录板子,连接好网线,然后电脑端先使用ping命令查看网络是否正常: 上图黄框是电脑的IP,红框是板子的IP,ping有回复,网络通了。 1.3测试数据收发 测试需要用到一个网络调试助手软件,网络协议类型选择TCP Client,这个程序,电脑是作为客户端的,然后远程主机IP即板子的IP,板子作为服务器,服务器端口号为8000。最后点击连接,会收到板子发来的一些数据,如下图中的绿色字体。也可以测试发送,发送字符串“aa”给板子,如下图中的蓝色字体。 2 web服务测试 这个程序只有板子的IP: 烧录程序,连接好网线后,在电脑浏览器地址栏中,输入板子的IP地址,可以跳出如下web界面,这个界面可以通过这个网页来控制板子上3个LED的亮灭,如下图,此时板子上的3个LED都被点亮了: 切换到ADC电压采集的界面,可以将板子上采集的电压值显示在这个网页上: 以上是例子程序的效果,程序具体原理还没细看,涉及到TCP/IP网络通信,有时间再研究。

  • 2020-10-08
  • 发表了主题帖: 【GD32450I-EVAL】+ 08图像识别-彩色物体跟踪

    本帖最后由 DDZZ669 于 2020-10-8 21:34 编辑         上篇文章【GD32450I-EVAL】+ 07摄像头全屏显示测试介绍了摄像头的使用,本篇利用摄像头采集的图像,进行简单的图像识别,借助EasyTrace彩色物体检测算法,实现对彩色物体的跟踪检测。         先看一下最终的效果:   1 EasyTrace简介 使用EasyTrace可以对单一颜色物体进行实时跟踪识别。 1.1 EasyTrace特点 1.代码只有300行,非常简单 2.识别一次只需要2至5毫秒,实时性强 3.只有1个API,使用方便 4.可配置”容错率“ ”迭代次数“等参数,可在”时间“实时性”“ ”准确度“ ”识别率“ ”误识率“ 之间寻找平衡点 1.2 使用举例         使用EasyTrace,首先需要自己提供一个指定位置的颜色读取函数: //读取RBG格式颜色,唯一需要移植的函数 extern unsigned short GUI_ReadBit16Point(unsigned short x,unsigned short y);           然后使用如下3行就可以识别彩色物体了: RESULT Resu;//识别的结果 TARGET_CONDI Condition={60,100,20,120,10,160,40,40,320,240};//识别的条件 Trace(&Condition,&Resu);//彩色物体检测API           识别条件与识别结果的结构体定义为: typedef struct{ unsigned char H_MIN;//目标最小色调 unsigned char H_MAX;//目标最大色调 unsigned char S_MIN;//目标最小饱和度 unsigned char S_MAX;//目标最大饱和度 unsigned char L_MIN;//目标最小亮度 unsigned char L_MAX;//目标最大亮度 unsigned int WIDTH_MIN;//目标最小宽度 unsigned int HIGHT_MIN;//目标最小高度 unsigned int WIDTH_MAX;//目标最大宽度 unsigned int HIGHT_MAX;//目标最大高度 }TARGET_CONDI;//判定为的目标条件 typedef struct{ unsigned int x;//目标的x坐标 unsigned int y;//目标的y坐标 unsigned int w;//目标的宽度 unsigned int h;//目标的高度 }RESULT;//识别结果 //这里是图像搜索范围的定义 #define IMG_X 0 //图片x坐标 #define IMG_Y 0 //图片y坐标 #define IMG_W 480 //图片宽度 #define IMG_H 272 //图片高度   2 HSL色彩空间         EasyTrace识别彩色物体使用的是HSL色彩空间,即色调,饱和度,亮度。         色调(Hue)是色彩的基本属性,就是平常所说的颜色名称,如红色、黄色等。         饱和度(Saturation)是指色彩的纯度,越高色彩越纯,低则逐渐变灰。         亮度(Lightness)是指颜色的明亮程度。         关于色彩空间的具体细节,可以参考知乎问答: 色彩空间中的 HSL、HSV、HSB 有什么区别?   3 EasyTrace移植 3.1 GUI_ReadBit16Point函数         EasyTrace仅仅只有一个c文件和一个h文件,添加到自己的工程中即可,对于需要自己提供的“GUI_ReadBit16Point()"函数,资料例程中有一个lcd_point_get函数可以直接使用,封装一下即可: //用于EasyTracer unsigned short GUI_ReadBit16Point(unsigned short x,unsigned short y) { return lcd_point_get(x,y); }   3.2 颜色阈值确定         本实验准备识别蓝色物体,但蓝色物体的HSL值如何确定呢,一种方法是网上查找蓝色对于的HSL的值,另一种方式是直接用摄像头拍摄要识别的物体,并通过程序将RGB转换为HSL,因为EasyTrace里有现成的颜色转换函数,我们改造一下来用: while (1) { ReadColor(50,50,&testRGB); RGBtoHSL(&testRGB,&testHSL); printf("H=%d,S=%d,L=%d\r\n",testHSL.hue,testHSL.saturation,testHSL.luminance); }           上面的我程序的主函数,摄像头采集的图像显示在LCD上,将要识别的物体放到摄像头前面,并使物体的中心大致位于(50,50)的位置,然后读取该位置的像素值,并转换为HSL,通过串口输出,这样,就能确定要识别的物体的HSL的大致范围了。   3.3 物体识别         然后,就是写一个识别与画矩形圈住物体的函数: while (1) { if(Trace(&Condition1,&Resut)) //检测到了物体 { if((lastResut.x!=Resut.x)&&(lastResut.y!=Resut.y)&&(lastResut.w!=Resut.w)&&(lastResut.h!=Resut.h)) { lastResut.x = Resut.x; lastResut.y = Resut.y; lastResut.w = Resut.w; lastResut.h = Resut.h; lcd_layer_set(LCD_LAYER_FOREGROUND); //指定当前层为前景层(识别到物体后,在前景层画矩形块) lcd_clear(LCD_COLOR_CYAN);//ARGB155模式下,LCD_COLOR_CYAN就是透明色 lcd_rectangle_line(lastResut.x-(lastResut.w/2),lastResut.y-(lastResut.h/2),lastResut.w,lastResut.h); //画矩形框 lcd_layer_set(LCD_LAYER_BACKGROUND); //再切换到背景层,用于物体识别(摄像头的图像在背景层显示) } delay_1ms(100); } else //没有检测到 { delay_1ms(100); if(10 == ++t) //等了1秒一直没检测到 { t=0; lcd_layer_set(LCD_LAYER_FOREGROUND); lcd_clear(LCD_COLOR_CYAN); //清屏 (清除上次的矩形框块) lcd_layer_set(LCD_LAYER_BACKGROUND); } } }           这个程序中,摄像头的图像在背景层显示,识别到物体后,在前景层画矩形框,因为前景层使用了ARGB1555这种带透明度的格式,所以虽然有前景层,也能看到摄像头的图像,关于液晶屏的分层显示,可以先参考:【GD32450I-EVAL】+ 04液晶屏层叠显示与透明度调整测试   4 物体识别效果展示         演示视频:  

  • 2020-10-06
  • 发表了主题帖: 【GD32450I-EVAL】+ 07摄像头全屏显示测试

    本帖最后由 DDZZ669 于 2020-10-6 14:54 编辑         板子上配了一个OV2640摄像头,其最大像素尺寸为1600*1200,板子上的液晶屏的尺寸为480*272,而光盘资料里的照相机例程,只使用了屏幕中间的240*272的一块区域显示摄像头的图像。         本篇以资料中的照相机例子为基础,修改程序,测试摄像头在整个屏幕(480*272)上的显示效果,关于屏幕的使用基础,可先参考之前的文章:【GD32450I-EVAL】+ 04液晶屏层叠显示与透明度调整测试   1 例子程序分析         先看一下例子的效果:   1.1 主程序分析 //----------------------主函数----------------------------- int main(void) { ov2640_id_struct ov2640id; systick_config(); nvic_configuration(); /* init SDRAM*/ //SDRAM初始化,作为液晶屏的显存 exmc_synchronous_dynamic_ram_init(EXMC_SDRAM_DEVICE0); delay_1ms(1000); /* key configuration */ //按键初始化,用于拍照 key_config(); /* camera initialization */ //摄像头初始化,包括DMA传输配置 dci_ov2640_init(); dci_ov2640_id_read(&ov2640id); /* DMA interrupt and channel enable */ //使能DMA dma_interrupt_enable(DMA1, DMA_CH7,DMA_CHXCTL_FTFIE); dma_channel_enable(DMA1,DMA_CH7); /* DCI enable */ //使能摄像头获取图像 dci_enable(); dci_capture_enable(); delay_1ms(100); /* LCD configure and TLI enable */ //LCD配置 lcd_config(); tli_layer_enable(LAYER0); tli_layer_enable(LAYER1); tli_reload_config(TLI_REQUEST_RELOAD_EN); tli_enable(); while (1) //进入死循环,在DMA中断中显示图像 { } } //------------------------DMA中断----------------------- void DMA1_Channel7_IRQHandler(void) { /* 320*240 size image convert to 240*272 size image */ if(dma_interrupt_flag_get(DMA1,DMA_CH7,DMA_INTF_FTFIF)) { int i=0,x=0,y=0; dma_channel_disable(DMA1, DMA_CH7); for(x=0;x<320;x++)//摄像头宽(320) { for(y=0;y<240;y++)//摄像头高(240) { if(x<272) { *(uint16_t *)(0XC0400000+2*i)=*(uint16_t *)(0XC0000000+2*((320*y) + x));//layer1地址<-摄像头地址 i++; } } } dma_interrupt_flag_clear(DMA1,DMA_CH7,DMA_INTC_FTFIFC); dma_channel_enable(DMA1, DMA_CH7); } }         该程序需要用到外部SDRAM,因为这个程序需要用到的内存比较大,GD32F450自带的256K内存不够用,这个程序中: 摄像头的图像尺寸为320*240,图像格式为RGB565,即一个像素需要2个字节的空间,所以一帧图像需要的内存为: 320*240*2/1024=150K字节 摄像头采集的图像需要再裁剪为272*240并旋转方向,需要另一块内存,大小为: 272*240*2/1024=127K字节 对于拍照功能,也需要150K的大小内存进行图片暂存         所以,仅仅是前两项功能,所需内存超过了256K,因而必须使用SDRAM来扩展内存,关于SDRAM的基本使用,可以先查看我的上一篇文章:【GD32450I-EVAL】+ 06SDRAM介绍   1.2 几种地址的数据传递关系         注意,这个程序中,摄像头采集的图像,DMA传输时,不是直接从摄像头传输到了液晶屏的显存(0XC0400000)!而时先传输到一个暂存地址(0XC0000000),这时因为摄像头与液晶屏的安装位置是90度的关系,为了竖屏显示摄像头图像,需要手动将图像旋转90度,就是DMA中断函数中指针操作那一句(见上面程序)。         数据传递关系可用下图表示:   1.3 摄像头初始化程序         再来看一下摄像头的初始化: uint8_t dci_ov2640_init(void) { uint8_t i; sccb_config();//SCCB通信初始化 dci_config(); //摄像头配置,包括DMA的配置 ckout0_init(); delay_1ms(10); /* OV2640 reset */ //摄像头复位 if(dci_byte_write(0xff, 0x01)!=0) return 0xff; if(dci_byte_write(0x12, 0x80)!=0) return 0xff; delay_1ms(10); //摄像头的参数配置,通过SCCB协议对寄存器写入各种参数 for(i=0;i<sizeof(change_reg)/2;i++) { if(dci_byte_write(change_reg[0],change_reg[1])!=0) { return 0xff; } } delay_1ms(100); for(i=0;i<(sizeof(ov2640_rgb565_reg_tbl)/2);i++) { dci_byte_write(ov2640_rgb565_reg_tbl[0],ov2640_rgb565_reg_tbl[1]); } delay_1ms(100); //设置摄像头的输出大小 ov2640_outsize_set(320,240); return 0; }         摄像头需要用到一种SCCB通信,进行参数的配置,SCCB的具体通信原理这里暂不分析,可以先把它理解为一种类似于IIC通信进行参数配置的方式即可。接着是摄像头接口(DCI)的初始化,包括GPIO初始化于DMA传输初始化等。然后是通过SCCB进行摄像头复位,以及进行各种参数配置。         下面是摄像头的插座接口,可以看到,SCCB通信就是用的GD32的硬件IIC接口,数据传输为8根数据线。另外还有一些行同步、帧同步、像素时钟引脚。   2 修改为全屏显示         先看一下修改后的效果:   2.1 直接修改显示尺寸         修改为全屏显示,不能只是把摄像头和液晶屏的显示尺寸修改一下就完事了,因为尺寸变大以后,还要考虑DMA是否可以正常传输的问题。 例子程序中,DMA每次传说的大小为一帧图像,DMA的传输数据位宽为32位(4字节),则一帧的数据量为:320*240*2/4=38400。         而DMA每次传输也有最大的限制,为:2的16次方,即65536。         所以,如果是全屏图像,数据为480*272*2/4=65280,呃。。。好像刚刚够,小于65536。那就先直接修改显示尺寸看看效果。         DMA中断可以改下为如下,注意这里我直接让摄像头输出为272*480,不需要在手动裁剪图像: void DMA1_Channel7_IRQHandler(void) { int i=0, x=0, y=0; if(dma_interrupt_flag_get(DMA1,DMA_CH7,DMA_INTF_FTFIF)) { //将0xC0000000的内容,旋转一下方向,放到0xC0040000 for(x=0;x<272;x++) { for(y=0;y<480;y++) { *(uint16_t *)(0XC0040000+2*i)=*(uint16_t *)(0xC0000000+2*(272*y+x)); i++; } } dma_interrupt_flag_clear(DMA1,DMA_CH7,DMA_INTC_FTFIFC); } } //480*272*2 = 0x3FC00 //LCD的地址可以是 0x C004 0000         不过实际测试,图像静止时显示正常,但在图像快速变化的时候,显示的图像在某些区域会有些奇怪,如下图,在屏幕左侧有若干竖列显示的好像是之前的图像,在屏幕的右下角也有一块三角形区域显示的是之前的图,其刷新的速度没有正常区域刷新的快。只有当画面静止时,异常区域 的图像才能更新为正常图像。   2.2 修改DMA为每次传输一行         上面出现局部异常的原因位置,可以是一次传输整帧图像,DMA异样?那就减少每次DMA的数据量。改为每次传输一行,则传输480次后形成一幅图像。另外,利用摄像头的帧中断,强制从第1行重新开始传输,放防止摄像头于DMA的速率不一致导致图像错位,修改后的中断函数如下: //记录传输了多少行 static uint16_t line_num =0; void DMA1_Channel7_IRQHandler(void) { int i=0; int last_line=0; if(dma_interrupt_flag_get(DMA1,DMA_CH7,DMA_INTF_FTFIF)) { /*行计数*/ line_num++; if(line_num==480) { /*传输完一帧,计数复位*/ line_num=0; } dma_channel_disable(DMA1, DMA_CH7); dci_dma_config(0xC0000000+(272*2*line_num), 272*2/4);//每次传输一行 //将0xC0000000的内容,旋转一下方向,放到0xC0040000 //last_line = (0 == line_num)? 480 : (line_num-1); last_line = line_num; for(i=0;i<272;i++) { *(uint16_t *)(0XC0040000+2*(480*i+last_line))=*(uint16_t *)(0xC0000000+2*(272*last_line+i)); } dma_interrupt_flag_clear(DMA1,DMA_CH7,DMA_INTC_FTFIFC); dma_interrupt_enable(DMA1, DMA_CH7,DMA_CHXCTL_FTFIE); dma_channel_enable(DMA1, DMA_CH7); } } //480*272*2=0x3FC00 //LCD的地址可以是0xC0040000 //使用帧中断重置line_num,可防止有时掉数据的时候DMA传送行数出现偏移 void DCI_IRQHandler(void) { if( dci_interrupt_flag_get (DCI_INT_EF) == SET ) { /*传输完一帧,计数复位*/ line_num=0; //printf("-----------line_num:%d \r\n",line_num); dci_interrupt_clear(DCI_INT_EF); dci_interrupt_enable(DCI_INT_EF); } }         实测效果,这种方式,图像晃动时,不会出现局部异常显示区域,但会有明显的图像刷新现象。有时间再研究其它更合理的显示方式。  

  • 2020-10-04
  • 发表了主题帖: 【GD32450I-EVAL】+ 06SDRAM介绍

    本帖最后由 DDZZ669 于 2020-10-4 17:28 编辑 1 SDRAM          RAM,可以理解为内存,程序在运行时需要的空间,GD32F450IK自带256K的RAM,当需要大内存的场合,就需要扩展RAM了。这块开发板上就搭配了一颗MT48LC16M16A2P的SDRAM。 1.1 SDRAM数据存储基本原理           板子上的SDRAM型号为:MT48LC16M16A2P-6AIT,它的原理图如下:            每个引脚的作用如下表所示:          SDRAM内部分为多个叫做 Bank 的 区域,允许设备以交错的方式进行访问,以获取更大的并发性和数据传输量。 每个Bank 可以认为是一个矩阵,其中每个地址对应存储器存储宽度的空间,矩阵 由行和列构成,因此存储器的Bank大小可以认为是存储器数据宽度行数列 数。         如下图所示,对于这个存储阵列,我们可以将其看成是一个表格,只需要给定行地址和列地址,就可以确定其唯一位置,这就是SDRAM寻址的基本原理。而一个SDRAM芯片内部,一般又有4个这样的存储单元(BANK0~BANK3),所以,在SDRAM内部寻址的时候,先指定BANK号和行地址,然后再指定列地址,就可以查找到目标地址。       SDRAM寻址的时候:            1 首先RAS信号为低电平,选通行地址,地址线A0~A12所表示的地址,会被传输并锁存到行地址译码器里面,作为行地址,            2 同时BANK地址线上面的BS0,BS1所表示的BANK地址,也会被锁存,选中对应的BANK,            3 然后,CAS信号为低电平,选通列地址,地址线A0~A12所表示的地址,会被传输并锁存到列地址译码器里面,作为列地址,       这样,就完成了一次寻址。   1.2 板载SDRAM型号分析       再来看一下MT48LC16M16A2P-6AIT这个型号的命名含义,可以查看它的数据手册:       这里还有一些表格介绍:       MT48LC16M16A2P-6AIT的存储结构为: 行地址:8192个(8K) 列地址:512个 BANK数:4个 位宽:16位      这样,整个芯片的容量为8192*512*4*16=32M 字节。 1.3 SDRAM控制逻辑        控制SDRAM需要用到一系列的命令,各种信号线状态组合产生不同的控制命令。       由于数据易失的特性, SDRAM 需要周期性的刷新。 EXMC 支持两种刷新模式,自刷新和自动刷新 。 自刷新是在EXMC挂起的低功耗模式 中使用, 由 SDRAM 内部计数提供 时钟,内部进行刷新。 自动刷新是由EXMC 周期 性的 提供 刷新命令, 因为此时SDRAM 需要进行数据传输 ,刷新间隔由寄存器 EXMC_SDARI 位 ARINTV 决定 ,连续刷新次数由寄存器 EXMC_SDCMD 位NARF 决定 。          DRAM并不是上电后立即就可以开始读写数据的,它需要按步骤进行初始化,对存储矩阵进行预充电、刷新并设置模式寄存器等。         SDRAM初始化过程: 上电                给SDRAM供电,使能CLK时钟,注意上电后,要等待最少200us,再发送其他指令。 发送NOP                发送NOP(No Operation命令) 发送PRECHARGE               发送预充电命令,给所有Bank预充电。 发送AUTO REFRESH                至少要发送发送8次自刷新命令, 每一个自刷新命令之间的间隔时间为tRFC。 设置LOAD MODE REGISTER                发送模式寄存器的值,配置SDRAM的工作参数。配置完成后,需要等待tMRD(也叫tRSC),使模式寄存器的配置生效,才能发送其他命令。 完成               经过前面的操作,SDRAM的初始化就完成了,接下来就可以发送激活命令和读/写命令,进行数据的读/写了。 2 EXMC          GD32的外部存储器控制器 EXMC ,用来访问各种片外存储器,通过配置寄存器, EXMC 可以把 AMBA协议转换为专用的片外存储器通信协议, 包括 SRAM、ROM、NOR Flash、NAND Flash、PC Card 和 SDRAM 。用户还可以调整相关的时间参数来提高通信效率 。 EXMC模块划分为许多个子Bank ,每个Bank支持特定的存储器类型,用户可以通过对 Bank 的寄存器配置来控制外部存储器。          EXMC由 6 个模块组成: AHB总线 接口, EXMC 配置 寄存器, NOR/PSRAM 控制器 NAND/PC Card 控制器 SDRAM 控制器 和外部设备接口。 AHB 时钟 HCLK 是参考 时钟。           EXMC将外部存储器分成多个Bank,每个Bank占256M字节,其中Bank0又分为4个Region,每个Region占64M字节。Bank1和Bank2又都被分成2个Section,分别是属性存储空间和通用存储空间。Bank3分成3个Section,分别是属性存储空间,通用存储空间和I/O存储空间。每个Bank和Region都有独立的片选控制信号,也都能进行独立的配置。 Bank0用于访问NOR、PSRAM设备。 Bank1和Bank2用于连接NAND Flash,且每个Bank连接一个NAND。 Bank3用于连接PC卡。 SDRAM Device0和SDRAM Device1用于连接SDRAM。            EXMC 把SDRAM的存储区域分成了device0 和device01两块,SDRAM 地址映射如下图: 3 程序分析        看一下SDRAM例程,首先是主函数: int main(void) { uint16_t i = 0; /* LED initialize */ gd_eval_led_init(LED1); gd_eval_led_init(LED3); /* systick clock configure */ systick_config(); /* config the USART */ gd_eval_com_init(EVAL_COM1); /* fill txbuffer */ fill_buffer(txbuffer, BUFFER_SIZE, 0x0000); //===============SDRAM初始化=================== /* config the EXMC access mode */ exmc_synchronous_dynamic_ram_init(EXMC_SDRAM_DEVICE0);//SDRAM device0: 0x00000004U printf("\r\nSDRAM initialized!"); delay_1ms(1000); //===============写入SDRAM=================== printf("\r\nSDRAM write data..."); delay_1ms(1000); /* write data to SDRAM */ sdram_writebuffer_8(EXMC_SDRAM_DEVICE0,txbuffer, WRITE_READ_ADDR, BUFFER_SIZE); //===============读取SDRAM=================== printf("\r\nSDRAM read data..."); delay_1ms(1000); /* read data from SDRAM */ sdram_readbuffer_8(EXMC_SDRAM_DEVICE0,rxbuffer, WRITE_READ_ADDR, BUFFER_SIZE); //===============检查写入与读出的是否相同=================== printf("\r\nCheck the data!"); delay_1ms(1000); /* compare two buffers */ for(i = 0;i < BUFFER_SIZE;i++) { if (rxbuffer != txbuffer) { writereadstatus ++; break; } } if(writereadstatus) { printf("\r\nSDRAM test failed!"); /* failure, light on LED3 */ gd_eval_led_on(LED3); } else { printf("\r\nSDRAM test successed!"); delay_1ms(1000); printf("\r\nThe data is:\r\n"); delay_1ms(1000); for(i=0;i < BUFFER_SIZE;i++) { printf("%6x",rxbuffer); if(((i+1)%16) == 0) { printf("\r\n"); } } /* success, light on LED1 */ gd_eval_led_on(LED1); } while(1); }        先是各种外设初始化以及SDRAM的初始化,然后写入特定数据到SDRAM,再读出数据,最后比较写入与读出的是否相同。        这里再来看一下SDRAM初始化的具体过程: void exmc_synchronous_dynamic_ram_init(uint32_t sdram_device) { exmc_sdram_parameter_struct sdram_init_struct; exmc_sdram_timing_parameter_struct sdram_timing_init_struct; exmc_sdram_command_parameter_struct sdram_command_init_struct; uint32_t command_content = 0, bank_select; uint32_t timeout = SDRAM_TIMEOUT; /* enable EXMC clock*/ rcu_periph_clock_enable(RCU_EXMC); 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_GPIOG); rcu_periph_clock_enable(RCU_GPIOH); /* common GPIO configuration */ /* SDNE0(PC2),SDCKE0(PC5) pin configuration */ gpio_af_set(GPIOC, GPIO_AF_12, GPIO_PIN_2 | GPIO_PIN_5); gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_2 | GPIO_PIN_5); gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2 | GPIO_PIN_5); /* D2(PD0),D3(PD1),D13(PD8),D14(PD9),D15(PD10),D0(PD14),D1(PD15) pin configuration */ gpio_af_set(GPIOD, GPIO_AF_12, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15); gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15); gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15); /* NBL0(PE0),NBL1(PE1),D4(PE7),D5(PE8),D6(PE9),D7(PE10),D8(PE11),D9(PE12),D10(PE13),D11(PE14),D12(PE15) pin configuration */ gpio_af_set(GPIOE, GPIO_AF_12, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15); gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15); gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15); /* A0(PF0),A1(PF1),A2(PF2),A3(PF3),A4(PF4),A5(PF5),NRAS(PF11),A6(PF12),A7(PF13),A8(PF14),A9(PF15) pin configuration */ gpio_af_set(GPIOF, GPIO_AF_12, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15); gpio_mode_set(GPIOF, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15); gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15); /* A10(PG0),A11(PG1),A12(PG2),A14(PG4),A15(PG5),SDCLK(PG8),NCAS(PG15) pin configuration */ gpio_af_set(GPIOG, GPIO_AF_12, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_8 | GPIO_PIN_15); gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_8 | GPIO_PIN_15); gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_8 | GPIO_PIN_15); /* SDNWE(PH5) pin configuration */ gpio_af_set(GPIOH, GPIO_AF_12, GPIO_PIN_5); gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_5); gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5); /* specify which SDRAM to read and write */ if(EXMC_SDRAM_DEVICE0 == sdram_device) { bank_select = EXMC_SDRAM_DEVICE0_SELECT; } else { bank_select = EXMC_SDRAM_DEVICE1_SELECT; } /* EXMC SDRAM device initialization sequence --------------------------------*/ //【1】配置SDRAM时间寄存器 /* Step 1 : configure SDRAM timing registers --------------------------------*/ /* LMRD: 2 clock cycles */ sdram_timing_init_struct.load_mode_register_delay = 2; /* XSRD: min = 67ns */ sdram_timing_init_struct.exit_selfrefresh_delay = 7; /* RASD: min=42ns , max=120k (ns) */ sdram_timing_init_struct.row_address_select_delay = 5; /* ARFD: min=60ns */ sdram_timing_init_struct.auto_refresh_delay = 6; /* WRD: min=1 Clock cycles +6ns */ sdram_timing_init_struct.write_recovery_delay = 2; /* RPD: min=18ns */ sdram_timing_init_struct.row_precharge_delay = 2; /* RCD: min=18ns */ sdram_timing_init_struct.row_to_column_delay = 2; //【2】配置SDRAM控制寄存器 /* step 2 : configure SDRAM control registers ---------------------------------*/ sdram_init_struct.sdram_device = sdram_device; sdram_init_struct.column_address_width = EXMC_SDRAM_COW_ADDRESS_9; sdram_init_struct.row_address_width = EXMC_SDRAM_ROW_ADDRESS_13; sdram_init_struct.data_width = EXMC_SDRAM_DATABUS_WIDTH_16B; sdram_init_struct.internal_bank_number = EXMC_SDRAM_4_INTER_BANK; sdram_init_struct.cas_latency = EXMC_CAS_LATENCY_3_SDCLK; sdram_init_struct.write_protection = DISABLE; sdram_init_struct.sdclock_config = EXMC_SDCLK_PERIODS_2_HCLK; sdram_init_struct.brust_read_switch = ENABLE; sdram_init_struct.pipeline_read_delay = EXMC_PIPELINE_DELAY_1_HCLK; sdram_init_struct.timing = &sdram_timing_init_struct; /* EXMC SDRAM bank initialization */ exmc_sdram_init(&sdram_init_struct); //【3】配置CKE? /* step 3 : configure CKE high command---------------------------------------*/ sdram_command_init_struct.command = EXMC_SDRAM_CLOCK_ENABLE; sdram_command_init_struct.bank_select = bank_select; sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_1_SDCLK; sdram_command_init_struct.mode_register_content = 0; /* wait until the SDRAM controller is ready */ while((exmc_flag_get(sdram_device, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0)) { timeout--; } /* send the command */ exmc_sdram_command_config(&sdram_command_init_struct); //【4】延时10ms /* step 4 : insert 10ms delay----------------------------------------------*/ delay_1ms(10); //【5】配置预充电 /* step 5 : configure precharge all command----------------------------------*/ sdram_command_init_struct.command = EXMC_SDRAM_PRECHARGE_ALL; sdram_command_init_struct.bank_select = bank_select; sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_1_SDCLK; sdram_command_init_struct.mode_register_content = 0; /* wait until the SDRAM controller is ready */ timeout = SDRAM_TIMEOUT; while((exmc_flag_get(sdram_device, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0)) { timeout--; } /* send the command */ exmc_sdram_command_config(&sdram_command_init_struct); //【6】配置自动刷新 /* step 6 : configure Auto-Refresh command-----------------------------------*/ sdram_command_init_struct.command = EXMC_SDRAM_AUTO_REFRESH; sdram_command_init_struct.bank_select = bank_select; sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_8_SDCLK; sdram_command_init_struct.mode_register_content = 0; /* wait until the SDRAM controller is ready */ timeout = SDRAM_TIMEOUT; while((exmc_flag_get(sdram_device, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0)) { timeout--; } /* send the command */ exmc_sdram_command_config(&sdram_command_init_struct); //【7】配置装载模式寄存器 /* step 7 : configure load mode register command-----------------------------*/ /* program mode register */ command_content = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1 | SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL | SDRAM_MODEREG_CAS_LATENCY_3 | SDRAM_MODEREG_OPERATING_MODE_STANDARD | SDRAM_MODEREG_WRITEBURST_MODE_SINGLE; sdram_command_init_struct.command = EXMC_SDRAM_LOAD_MODE_REGISTER; sdram_command_init_struct.bank_select = bank_select; sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_1_SDCLK; sdram_command_init_struct.mode_register_content = command_content; /* wait until the SDRAM controller is ready */ timeout = SDRAM_TIMEOUT; while((exmc_flag_get(sdram_device, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0)) { timeout--; } /* send the command */ exmc_sdram_command_config(&sdram_command_init_struct); //【8】配置自动刷新频率 /* step 8 : set the auto-refresh rate counter--------------------------------*/ /* 64ms, 8192-cycle refresh, 64ms/8192=7.81us */ /* SDCLK_Freq = SYS_Freq/2 */ /* (7.81 us * SDCLK_Freq) - 20 */ exmc_sdram_refresh_count_set(761); /* wait until the SDRAM controller is ready */ timeout = SDRAM_TIMEOUT; while((exmc_flag_get(sdram_device, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0)) { timeout--; } } 4 运行效果        通过串口与LED显示运行效果,把读写的操作进行比较,如果数据一致,点亮LED1,否则点亮LED3,同时串口输出信息如下: SDRAM initialized! SDRAM write data... SDRAM read data... Check the data! SDRAM test successed! The data is: 0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f ...略        这个例程,有时会运行失败。SDRAM的操作过程有些复杂,我还么有完全搞懂,有时间继续研究。  

  • 2020-09-26
  • 发表了主题帖: 【GD32450I-EVAL】+ 05FreeRTOS移植与任务创建

    本帖最后由 DDZZ669 于 2020-9-26 15:34 编辑         FreeRTOS是一个迷你的实时操作系统内核。作为一个轻量级的操作系统,功能包括:任务管理、时间管理、信号量、消息队列、内存管理、记录功能、软件定时器、协程等,可基本满足较小系统的需要。   1 FreeRTOS基本结构         FreeRTOS系统简单小巧、文件数量少、通常情况下内核占用4~9k字节空间,代码主要由C编写,可移植性高,已实现在30多种架构的芯片上移植。         由于FreeRTOS的最新代码目前从官网下载的速度特别慢,这里先以之前的FreeRTOS v9.0.0版本为例进行实验。FreeRTOS代码包含FreeRTOS和FreeRTOS-Plus文件夹,后者是一些补充文件,一般用不到,可以先忽略。         在FreeRTOS文件夹中主要关注source文件夹,这里是FreeRTOS的全部源码,包括6个c文件和include文件夹下的多个h文件。另外,在portable文件夹下,是针对不同硬件平台的单独区分使用的代码,目前考虑使用Keil开发STM32F407,所以portable文件夹只需使用RVDS的ARM_CM4F以及MemMang。         关于各个c文件的主要用途: port.c : 针对不同硬件平台的接口 heap_4.c : 内存管理相关 croutine.c : 协程相关 event_groups.c : 事件标志组相关 list.c : 列表,FreeRTOS的一种基础数据结构 queue.c : 队列相关 tasks.c : 任务创建、挂起、恢复、调度相关 timers.c : 软件定时器相关         在Demo文件夹下还需要用到一个FreeRTOSConfig.h,该文件中通过各种宏定义的方式来配置FreeRTOS需要使用哪些资源。   2 FreeRTOS移植 2.1 准备一个基础工程         这里以光盘资料里的LED工程为基础,移植FreeRTOS系统。先来看一下LED基础工程的结构: 2.2 添加FreeRTOS库文件         把FreeRTOS源码中的Source文件夹复制出来并重命名为FreeRTOS(便于与GD32F4库文件区分),用于添加到LED基础工程中,Source文件夹中的portable子文件夹中,只保留RVDS与MemMang,其余的用不到可以删除。         另外还需要一个名为“FreeRTOSConfig.h”的配置文件,因为FreeRTOSv9.0中还没有GD32F450的Demo,可以先使用Demo文件夹下的CORTEX_M4F_STM32F407ZG-SK文件夹里的这个文件,因为它们同属M4内核。然后将该文件放到include子文件夹中。         将FreeRTOS源码中的c文件添加到LED基础工程中:           再添加头文件的路径: 2.3 编译与配置         添加FreeRTOS的C文件并添加H文件搜索路径后,编译一下工程,提示3个错误:                  port.c和gd32f4xx_it.c中的函数重复定义了,因为port.c的FreeRTOS中的文件,那我们不修改它,而是把gd32f4xx_it.c中的同名函数先注释掉,并再次编译,就不报错了:   3 任务创建示例         使用FreeRTOS开发,与裸机开发的最大区别就是,裸机程序是一条主线从上到下执行,再配合中断功能来实现紧急时间的处理。而使用FreeRTOS等实时操作系统,可以将所以实现的各个功能单独设计为一个个子任务,每个子任务都是一个死循环,通过任务的调度的方式来实现各个子任务的运行与信息交流。         以下是两个LED灯的测试程序,LED1子任务的功能是每隔500ms进行亮灭转换,LED2子任务的功能是亮200ms,灭800ms循环。这两个子任务通过系统调度的方式,各自运行,互不影响。         另外,FreeRTOS中首先需要一个起始任务,用来创建其它任务,创建完其它任务后,该任务将自己删除。 #include "gd32f4xx.h" #include "gd32f450i_eval.h" #include "FreeRTOS.h" #include "task.h" //任务参数-------------------------- //优先级 堆栈大小 任务句柄 任务函数 #define START_TASK_PRIO 1 #define START_STK_SIZE 128 TaskHandle_t StartTask_Handler; void start_task(void *pvParameters); #define TASK1_TASK_PRIO 2 #define TASK1_STK_SIZE 128 TaskHandle_t Task1Task_Handler; void task1_task(void *pvParameters); #define TASK2_TASK_PRIO 3 #define TASK2_STK_SIZE 128 TaskHandle_t Task2Task_Handler; void task2_task(void *pvParameters); int main(void) { //设置系统中断优先级分组4(FreeRTOS中的默认方式!) nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0); gd_eval_led_init(LED1); gd_eval_led_init(LED2); //创建开始任务 xTaskCreate((TaskFunction_t )start_task, //任务函数 (const char* )"start_task", //任务名称 (uint16_t )START_STK_SIZE, //任务堆栈大小 (void* )NULL, //传递给任务函数的参数 (UBaseType_t )START_TASK_PRIO, //任务优先级 (TaskHandle_t* )&StartTask_Handler); //任务句柄 //开启任务调度 vTaskStartScheduler(); } //开始任务任务函数 void start_task(void *pvParameters) { taskENTER_CRITICAL(); //进入临界区 //创建TASK1任务 xTaskCreate((TaskFunction_t )task1_task, (const char* )"task1_task", (uint16_t )TASK1_STK_SIZE, (void* )NULL, (UBaseType_t )TASK1_TASK_PRIO, (TaskHandle_t* )&Task1Task_Handler); //创建TASK2任务 xTaskCreate((TaskFunction_t )task2_task, (const char* )"task2_task", (uint16_t )TASK2_STK_SIZE, (void* )NULL, (UBaseType_t )TASK2_TASK_PRIO, (TaskHandle_t* )&Task2Task_Handler); vTaskDelete(StartTask_Handler); //删除开始任务 taskEXIT_CRITICAL(); //退出临界区 } //task1任务函数 void task1_task(void *pvParameters) { while(1) { gd_eval_led_toggle(LED1); vTaskDelay(500); //延时500ms } } //task2任务函数 void task2_task(void *pvParameters) { while(1) { gd_eval_led_on(LED2);; vTaskDelay(200); //延时200ms gd_eval_led_off(LED2);; vTaskDelay(800); //延时800ms } } 4 测试效果

  • 2020-09-20
  • 发表了主题帖: 【GD32450I-EVAL】+ 04液晶屏层叠显示与透明度调整测试

    本帖最后由 DDZZ669 于 2020-9-20 15:44 编辑 上篇 " 【GD32450I-EVAL】+ 03库函数基础使用方法-以按键中断为例" 介绍了GD32库开发的基础使用方法,本篇来研究一下液晶显示屏。     1 RGB液晶屏   GD32450I开发板上带了一块4.3英寸的480x272的RGB接口显示屏,类型为TFT-LCD(Thin Film Transistor-Liquid Crystal Display),即薄膜晶体管液晶显示器。 之前只用过MCU接口的屏幕,这里简单说一下区别: MCU-LCD:最初是为单片机(MCU)设计的,因单片机内存较小,把显存内置在LCD模块内部,通过专门的显示命令来更新显存,MCU屏不能做得很大。MCU屏显示图像,显示需要发送画点的命令来修改MCU内部RAM。 RGB-LCD:其显存是由系统内存充当,只要系统内存够大,RGB-LCD就可以做出较大尺寸。RGB屏显示图像,只需显存组织好数据,启动显示后,LCD-DMA会自动把显存通过RGB接口送到显示屏,因此RGB屏的刷新速度较快。 两种屏的工作方式示意图如下:   关于屏幕的两种驱动模式: RGB屏一般有2种驱动模式:DE模式和SYNC模式(或称HV模式)。DE模式使用DE信号来确定有效数据,而SYNC模式,则需要行同步(Hsync)和场同步(Vsync),来表示扫描的行和列。   关于屏幕的一些参数: 可以看到,虽然屏幕分辨率是480*272,但水平和垂直的总周期会大于这个数,边缘多出来的像素具体是什么作用可以先不用管,程序中屏幕初始化的时候会用到这些数,先知道如何对应起来即可。     2 GD32的TLI   TLI(TFT-LCD Interface)即液晶屏接口,它是GD32单片机自身提供的一种驱动RGB-LCD的一种控制接口。TLI支持两个独立的显示层,并支持层窗口和层混叠功能。其层叠显示过程如下图所示: 此图中,层0和层1即两个独立的显示层,另外还有一个BG层,即背景层,可以指定显示某种颜色,该层处于最底层。层0叠加在BG层之上,通过调节层0的透明度,可以与BG层融合显示。层1叠加在最上面,也可以调节透明度。这三个层叠加在一起,就是屏幕展现出来的效果。     3 图片转RGB565   这款LCD支持多种像素格式,具体如下表,此次测试先使用RGB565格式,即红、绿、蓝分别占用5、6、5位来表示颜色值,所以描述一个彩色像素需要16位,即2个字节,也就是一个字。   图片转换成RGB565的16进制数据,可以使用一些小工具来转换,转换后保存成数组。 转换方法如下,先找一张自己需要的图片,然后使用Picture2Hex这个小软件,指定转换后图片的宽度和高度,选择图片转换即可,然后产生一个logo.c文件,将该文件中的数组放到自己的代码工程中使用。   4 层叠与透明度显示测试   上面介绍了一些基本原理,下面写一个测试程序来看一下层叠显示的效果。   首先是主函数部分,就是各种初始化,最后在按键中断中会来改变3个层的颜色或透明度: int main(void) { system_clock_config(); systick_config(); mytimer_init(); key_config();//通过按键来改变3个层的颜色或透明度 lcd_init();//LCD的GPIO等的初始化 lcd_layer_init();//LCD的显示层的初始化 tli_layer_enable(LAYER0); tli_layer_enable(LAYER1); tli_enable();//使能两个显示层 //设置两个显示层的透明度 lcd_layer_set(LCD_LAYER_FOREGROUND); lcd_transparency_set(125);//0~255 lcd_layer_set(LCD_LAYER_BACKGROUND); lcd_transparency_set(155);//0~255 while (1) { //主循环,什么也不做,在按键中断中会只改变透明度 } }   初始化部分主要开看LCD的初始化和层的初始化。 先看LCD的初始化: void lcd_init(void) { //--------此处省去GPIO、时钟等的初始化 /* 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_TLI; /* LCD display timing configuration */ tli_init_struct.synpsz_hpsz = 40; tli_init_struct.synpsz_vpsz = 9; tli_init_struct.backpsz_hbpsz = 42; tli_init_struct.backpsz_vbpsz = 11; tli_init_struct.activesz_hasz = 522; tli_init_struct.activesz_vasz = 283; tli_init_struct.totalsz_htsz = 524; tli_init_struct.totalsz_vtsz = 285; /* 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); } 第1部分中,DE参数为Low,所以采用的是SYNC模式。 第2部分中,好多参数,可以先对照上面的屏幕参数表格。 第2部分中,就是叠加层中的“BG层”,即最底层,这里先都给0xFF,即白色。   再来看层的初始化: void lcd_layer_init(void) { tli_layer_parameter_struct tli_layer_init_struct; /* TLI layer1 configuration */ //layer1 前景层(最上层) tli_layer_init_struct.layer_window_leftpos = (0 + 43);//最左侧从0开始 tli_layer_init_struct.layer_window_rightpos = (0 + 400 + 43 - 1); //最右侧到400结束 tli_layer_init_struct.layer_window_toppos = (0 + 12);//最上侧从0开始 tli_layer_init_struct.layer_window_bottompos = (0 + 200 + 12 - 1);//最下侧到200结束 tli_layer_init_struct.layer_ppf = LAYER_PPF_RGB565;//RGB565格式 tli_layer_init_struct.layer_sa = 0x0;//其余部分透明显示 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;//LAYER_ACF1_SA; tli_layer_init_struct.layer_acf2 = LAYER_ACF2_PASA;//LAYER_ACF2_SA; tli_layer_init_struct.layer_frame_bufaddr = (uint32_t)image_eeworld;//eeworld图片的RGB565数组 tli_layer_init_struct.layer_frame_line_length = ((LCD_PIXEL_WIDTH * 2) + 3); tli_layer_init_struct.layer_frame_buf_stride_offset = (LCD_PIXEL_WIDTH * 2); tli_layer_init_struct.layer_frame_total_line_number = LCD_PIXEL_HEIGHT; tli_layer_init(LAYER1, &tli_layer_init_struct); /* TLI layer0 configuration */ //layer0 背景层(中间层) tli_layer_init_struct.layer_window_leftpos = (80 + 43);//最左侧从80开始 tli_layer_init_struct.layer_window_rightpos = (80 + 400 + 43 - 1); //最右侧到80+400结束 tli_layer_init_struct.layer_window_toppos = (72 + 12);//最上侧从72开始 tli_layer_init_struct.layer_window_bottompos = (72+ 200 + 12 - 1);//最下侧到72+200结束 tli_layer_init_struct.layer_ppf = LAYER_PPF_RGB565; tli_layer_init_struct.layer_sa = 0x0;//其余部分透明显示 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 = (uint32_t)image_gd32;//gd32图片的RGB565数组 tli_layer_init_struct.layer_frame_line_length = ((LCD_PIXEL_WIDTH * 2) + 3); tli_layer_init_struct.layer_frame_buf_stride_offset = (LCD_PIXEL_WIDTH * 2); tli_layer_init_struct.layer_frame_total_line_number = LCD_PIXEL_HEIGHT; tli_layer_init(LAYER0, &tli_layer_init_struct); tli_reload_config(TLI_REQUEST_RELOAD_EN); lcd_font_set(&LCD_DEFAULT_FONT); tli_dither_config(TLI_DITHER_ENABLE); } 这里的layer0与layer1即叠加层中的“层0”和“层1”,两个显示层都配置为宽400高200,其余的部分透明显示。   使用按键来修改BG层的颜色: void change_backcolor(int n) { int i = n%4; switch(i) { case 0: tli_init_struct.backcolor_red = 0xFF; tli_init_struct.backcolor_green = 0xFF; tli_init_struct.backcolor_blue = 0xFF; break; case 1: tli_init_struct.backcolor_red = 0xFF; tli_init_struct.backcolor_green = 0x00; tli_init_struct.backcolor_blue = 0x00; break; case 2: tli_init_struct.backcolor_red = 0x00; tli_init_struct.backcolor_green = 0xFF; tli_init_struct.backcolor_blue = 0x00; break; case 3: tli_init_struct.backcolor_red = 0x00; tli_init_struct.backcolor_green = 0x00; tli_init_struct.backcolor_blue = 0xFF; break; } tli_init(&tli_init_struct); }   使用按键来修改层0和层1的透明度: void lcd_layer_set(uint32_t layer) { if (layer == LCD_LAYER_BACKGROUND) { current_framebuffer = (uint32_t)image_gd32;//LCD_FRAME_BUFFER; current_layer = LCD_LAYER_BACKGROUND; }else { current_framebuffer = (uint32_t)image_eeworld;//LCD_FRAME_BUFFER + BUFFER_OFFSET; current_layer = LCD_LAYER_FOREGROUND; } } void lcd_transparency_set(uint8_t trans) { if (current_layer == LCD_LAYER_BACKGROUND) { TLI_LxSA(LAYER0) = trans; } else { TLI_LxSA(LAYER1) = trans; } tli_reload_config(TLI_REQUEST_RELOAD_EN); } //改变层0的透明度------------- void change_layer0_transparency(int n) { int i = n%6;//0 1 2 3 4 5 lcd_layer_set(LCD_LAYER_BACKGROUND); lcd_transparency_set(i*50); } //改变层1的透明度------------- void change_layer1_transparency(int n) { int i = n%6;//0 1 2 3 4 5 lcd_layer_set(LCD_LAYER_FOREGROUND); lcd_transparency_set(i*50); }     5 下载验证   编译一下程序: compiling picture.c... linking... Program Size: Code=4360 RO-data=334236 RW-data=80 ZI-data=1088 FromELF: creating hex file... ".\GD32F4xx-OBJ\USBH-HID.axf" - 0 Error(s), 5 Warning(s). Build Time Elapsed: 00:00:03 可以看到Code为4360字节,即4K多点,而RO-data有334236字节,即326K多,主要是RGB565的数组占用了较大的空间。 这里用到了了2个400x200的RGB565的数组,总大小为400x200x2x2=320000字节,即320K,占据了RO-data的大部分。 关于这几个大小的含义,可参考上篇 "【GD32450I-EVAL】+ 03库函数基础使用方法-以按键中断为例"中 "程序编译下载" 处的介绍。   由于图片占用较大空间,下载程序时间较长(约半分钟),之后可以考虑如何先把图片烧写到flash的某个位置,然后程序来读取,这样不用每次都花时间来烧写图片数据。   演示视频如下,通过调节各层的透明度,可以清楚的看到各个层的叠加关系:  

  • 2020-09-18
  • 发表了主题帖: 【GD32450I-EVAL】+ 03库函数基础使用方法-以按键中断为例

    本帖最后由 DDZZ669 于 2020-9-18 22:05 编辑 上篇 "【GD32450I-EVAL】+ 02软件开发环境配置(KEIL 5)与流水灯测试" 介绍了软件开发环境的配置,这篇我们来看看GD32库函数的基础使用方法。 之前使用过STM32单片机,对比现在的GD32,使用库函数开发,编程思路基本一致,只是用到的库函数名称不一样, 下面以光盘资料中的 "03_GPIO_KeyBoard_Interrupt_mode"为例,分析一下库函数的使用方法。   1 程序分析 首先看一下例程中的主函数部分: int main(void) { gd_eval_key_init(KEY_TAMPER, KEY_MODE_EXTI); gd_eval_led_init(LED1); while(1) { } }   逻辑很简单,就是按键和LED进行初始化配置,然后就进入while(1)死循环中,监测中断的发生。   1.1 按键配置 先来看一下按键的初始化部分: void gd_eval_key_init(key_typedef_enum key_num, keymode_typedef_enum key_mode) { /* enable the key clock */ rcu_periph_clock_enable(KEY_CLK[key_num]); rcu_periph_clock_enable(RCU_SYSCFG); /* configure button pin as input */ gpio_mode_set(KEY_PORT[key_num], GPIO_MODE_INPUT, GPIO_PUPD_NONE,KEY_PIN[key_num]); if (key_mode == KEY_MODE_EXTI) { /* enable and set key EXTI interrupt to the lowest priority */ nvic_irq_enable(KEY_IRQn[key_num], 2U, 0U); /* connect key EXTI line to key GPIO pin */ syscfg_exti_line_config(KEY_PORT_SOURCE[key_num], KEY_PIN_SOURCE[key_num]); /* configure key EXTI line */ exti_init(KEY_EXTI_LINE[key_num], EXTI_INTERRUPT, EXTI_TRIG_FALLING); exti_interrupt_flag_clear(KEY_EXTI_LINE[key_num]); } }   1.1.1 时钟配置 首先是使能按键引脚的时钟源,包括GPIO时钟和RCU时钟,通过rcu_periph_clock_enable() 调用函数来完成。 该函数就是操作寄存器: void rcu_periph_clock_enable(rcu_periph_enum periph) { RCU_REG_VAL(periph) |= BIT(RCU_BIT_POS(periph)); }   1.1.2 GPIO配置 然后是按键GPIO的模式配置,通过调用gpio_mode_set() 函数来完成,该函数的声明为: void gpio_mode_set(uint32_t gpio_periph,uint32_t mode,uint32_t pull_up_down,uint32_t pin); 参数: gpio_periph :GPIO外设选 GPIOx (x = A,B,C,D,E,F,G,H,I) mode:GPIO 引脚模式 GPIO_MODE_INPUT: 输入模式 GPIO_MODE_OUTPUT: 输出模式 GPIO_MODE_AF: 复用功能模式 GPIO_MODE_ANALOG: 模拟信号模式 pull_up_dowm:GPIO 引脚 “上/下拉” 设置 GPIO_PUPD_NONE: 无上/下拉 GPIO_PUPD_PULLUP: 上拉模式 GPIO_PUPD_PULLDOWN: 下拉模式 pin:GPIO 引脚选择 GPIO_PIN_x (x=0..15), GPIO_PIN_ALL 另外,这里GPIO外设与引脚使用了数组与宏定义管理,如: gpio_mode_set(KEY_PORT[key_num], GPIO_MODE_INPUT, GPIO_PUPD_NONE,KEY_PIN[key_num]); 这句中的KEY_PORT[key_num] 与KEY_PIN[key_num] (函数调用时传入的key_num为KEY_TAMPER),找到其定义,在 "gd32f450i_eval.h"与"gd32f450i_eval.c"中有: #define TAMPER_KEY_PIN GPIO_PIN_13 #define TAMPER_KEY_GPIO_PORT GPIOC static uint32_t KEY_PORT[KEYn] = {WAKEUP_KEY_GPIO_PORT, TAMPER_KEY_GPIO_PORT, USER_KEY_GPIO_PORT}; static uint32_t KEY_PIN[KEYn] = {WAKEUP_KEY_PIN, TAMPER_KEY_PIN,USER_KEY_PIN}; 所以板子上Tamper这个用户按键对应的PC13脚就是通过这样的方式定义的。   1.1.3 中断配置 接着是中断的配置,包括NVIC的配置和EXTI配置: nvic_irq_enable(KEY_IRQn[key_num], 2U, 0U); syscfg_exti_line_config(KEY_PORT_SOURCE[key_num], KEY_PIN_SOURCE[key_num]); 配置完后,将设置的参数初始化进行生效: exti_init(KEY_EXTI_LINE[key_num], EXTI_INTERRUPT, EXTI_TRIG_FALLING); exti_interrupt_flag_clear(KEY_EXTI_LINE[key_num]);   1.2 LED配置 再来看一下LED的初始化部分: void gd_eval_led_init (led_typedef_enum lednum) { /* enable the led clock */ rcu_periph_clock_enable(GPIO_CLK[lednum]); /* configure led GPIO port */ gpio_mode_set(GPIO_PORT[lednum], GPIO_MODE_OUTPUT, GPIO_PUPD_NONE,GPIO_PIN[lednum]); gpio_output_options_set(GPIO_PORT[lednum], GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN[lednum]); GPIO_BC(GPIO_PORT[lednum]) = GPIO_PIN[lednum]; } 首先还是调用rcu_periph_clock_enable() 函数来使能时钟,然后调用gpio_mode_set() 函数来设置模式。 另外,LED的控制引脚作为输出模式,还要调用gpio_output_options_set() 函数来设置输出配置: void gpio_output_options_set(uint32_t gpio_periph,uint8_t otype,uint32_t speed,uint32_t pin); 参数: gpio_periph : GPIO外设选择 GPIOx (x = A,B,C,D,E,F,G,H,I) otype : GPIO 引脚输出模式 GPIO_OTYPE_PP: 推挽模式(push pull mode) GPIO_OTYPE_OD: 开漏模式(open drain mode) speed : GPIO 引脚最大输出速度(频率) GPIO_OSPEED_2MHZ: 2MHz GPIO_OSPEED_25MHZ: 25MHz GPIO_OSPEED_50MHZ: 50MHz GPIO_OSPEED_200MHZ: 200MHz pin : GPIO 引脚选择 GPIO_PIN_x (x=0..15), GPIO_PIN_ALL 程序中使用的是:推挽模式输出,速度50MHz。LED 最后,使用GPIO_BC() 将GPIO引脚清零(Bit Clear),即低电平,这样,LED默认就是熄灭状态。   1.3 中断处理函数 中断函数在“gd32f4xx_it.c”文件中: void EXTI10_15_IRQHandler(void) { if(RESET != exti_interrupt_flag_get(EXTI_13)) { gd_eval_led_toggle(LED1); } exti_interrupt_flag_clear(EXTI_13); } 当检测到按键按下时,会触发中断处理函数,这时,调用gd_eval_led_toggle() 来翻转电平,实现LED的点亮。 这个函数实际是调用库函数中的GPIO_TG() 函数: void gd_eval_led_toggle(led_typedef_enum lednum) { GPIO_TG(GPIO_PORT[lednum]) = GPIO_PIN[lednum]; } 翻转过LED后,调用exti_interrupt_flag_clear() 来清除中断标志。   2 程序编译下载 编译一下,可以看到如下编译信息: ...省略若干行 compiling gd32f450i_eval.c... assembling startup_gd32f450.s... linking... Program Size: Code=1564 RO-data=460 RW-data=100 ZI-data=1028 FromELF: creating hex file... ".\output\GD32450I_EVAL.axf" - 0 Error(s), 0 Warning(s). Build Time Elapsed: 00:00:14 这里顺便说名一下这几个Program Size: Code 是代码占用的空间。 Code=1564,即1564字节,1K多。 RO-data 是 Read Only 只读常量的大小,如const型。 RO-data=460,即460字节。 RW-data 是 Read Write 可读可写变量的大小,初始化时已经赋值的。 RW-data=100,即100字节。 ZI-data 是 Zero Initialize 没有初始化的可读可写变量的大小,没有初始化的变量都赋值一个0。 ZI-data=1028,即1028字节,1K多一点。 其中: RW + ZI就是程序总共使用的RAM字节数。 RAM就是掉电后丢失的那种(类比计算机的内存,从来存放程序运行时的数据),这个板子的RAM(内部SRAM)有256KB。 Code + RO + RW就是程序下载到Flash中的字节数。 Flash就是掉电后不会丢失的那种,这个板子的内部Flash有3MB。 注:       虽然RAM中存储了RW与ZI变量数据,但Flash中也要存RW,因为RAM掉电后数据就丢失,而Flash可以不包含ZI,因为ZI数据都是0,只要程序运行前将ZI数据所在的区域都清0即可,包含进去则浪费存储空间。   3 效果展示 按下按键,LED亮起,再次按下,LED熄灭。  

  • 2020-09-14
  • 发表了主题帖: 【GD32450I-EVAL】+ 02软件开发环境配置(KEIL 5)与流水灯测试

    本帖最后由 DDZZ669 于 2020-9-14 22:23 编辑 上篇进行了开箱测评:【GD32450I-EVAL】+ 01开箱与板载资源测评,本篇进行开发环境的配置。 单片机开发通常是使用KEIL开发,我的电脑之前安装了KEIL4和KEIL5,平时一般都用KEIL5。 KEIL5在开发某种型号的单片机时,需要先安装对应系列的pack支持包,下面介绍如何在KEIL5中安装GD32F4的pack。   1 开发环境配置 1.1 先尝试运行 在没有安装GD32F4的pack的时候,直接用KEIL5打开例子工程,看看会有什么情况发生: 查看光盘资料中的第一和LED示例程序,该工程是基于KEIL4的,我们先把它换成KEIL5格式。       方式就是将配置文件的后缀".uvopt"以及KEIL工程文件的后缀".uvproj",分别手动增加一个“x”即可, 修改后如下图所示:     我们先直接用KEIL5打开“GD32450I_EVAL.uvprojx”文件,由于此时KEIL中没有GD32F4的pack, 会先弹出一个窗口进行提示缺失pack,那我们就先安装pack吧。   1.2 pack的下载 GD32F4的pack可以去GD的官网下载,地址:http://gd32mcu.com/cn/download/7?kw=GD32F4 打开链接后如下图,选择GD32F4xxADD_ON的下载。 注意对于KEIL5,该pack仅支持v5.14以上的版本。     下载后,这里使用cmd中的tree命令来查看一下文件结构: 可以看到它有IAR和KEIL两种,并且KEIL又分为KEIL4和KEIL5两种,由于我要使用KEIL5,所以只需关注"GigaDevice.GD32F4xx_DFP.2.0.0.pack"这个pack包。 1.3 pack安装 安装方式一 直接双击该pack文件即可自动识别出已安装的KEIL5软件并自动安装,不过由于我的KEIL5也安装了v5.15和v5.30两个版本,直接双击pack文件会自动识别要安装到v5.30的KEIL,如果我想安装到KEIL5.15中,该怎么办呢?使用第二种安装方式。   安装方式二 使用需要的那个版本的KEIL打开工程文件,让它先跳出pack安装窗口,如果没有跳出,可以点击如下图方框中的图标,然后依次选择File -> Import,弹出pack文件选择窗口。       在弹出的窗口中选择pack文件:     然后就安装好了:   2 LED流水灯测试 2.1 编译 先编译一下工程,发现有报错,提示找不到头文件:     这个文件其实是在Library文件夹中:     把这个路径添加到KEIL的搜索路径,步骤如下图:     添加后的效果:     再次编译,发现就没有错误了:   2.2 下载 使用板子自带的GD-Link模块进行程序下载。 将板子通过usb先连接到电脑,usb线只需要GD-Link处的一处即可。 下载前需要先进行配置: a) 选择/确认单片机型号 魔术棒图标 -> Device :选择GD32F450IK b) 下载方式设置 魔术棒图标 -> Debug -> Use:选择CMSIS-DAP Debugger -> Settings -> Flash Download -> Add:选择GD32F4xx_3MB FMC。     点击下载图标即可下载程序到板子中:   2.3 效果展示 LED程序下载到板子后,可以看到板子上的3个LED开始闪烁起来:

  • 2020-09-12
  • 发表了主题帖: 【GD32450I-EVAL】+ 01开箱与板载资源测评

    本帖最后由 DDZZ669 于 2020-9-13 16:53 编辑  前两天收到论坛的板子了,感谢EEWorld论坛与兆易创新提供的板子,本篇先进行开箱测评。 1 开箱 白色的盒子,磁吸翻盖设计,打开很方便: 盒子里有: 网线1条 T型USB线2条 资料光盘1张 GD32450开发板(包含摄像头与液晶屏) 开发板正面: 可以看到OV2640摄像头和一块4.3寸的液晶屏。 开发板反面: 摄像头与液晶屏都是可插拔的: 再来一张板子正面图: 仔细观察板子上的走线,有一些蛇形的走线,这是为了信号抗干扰吧? 2 上电开机 用USB供电,只需要GD-Link处的一根USB供电即可,另外USB电源选择开关需要切换到USB,否则是外部5V供电。 上电后,板子默认有一个摄像头的程序,将图像显示在液晶屏上,按下用户按键可以拍照。 3 光盘资料 光盘里是GD32各种型号的资料,我们目前只关注GD32450I-EVAL Files这个文件夹: 打开GD32450I-EVAL Files文件夹,里面有库文件、项目例程以及原理图和用户手册: 打开Project文件夹,可以看到GD厂商提供了29个例子程序: 4 板载资源 参照板子原理图,对比了一下板子上的资源,对主要接口与芯片作了标注,如下图: 4.1 MCU GD32450I-EVAL搭载的微控制器具体型号为GD32F450IKH6 片上Flash:3MB 片上SRAM:256KB 主频:200MHz BGA176的封装,大小为10mm*10mm 8个16位通用定时器、2个32位通用定时器 2个先进的16位定时器,2个基本定时器 1个SysTick、2个看门狗、1个RTC 4个USART、4个UART 3个I2C 6/2个SPI/I2S 1个SDIO ... 4.2 LED 有3个LED,可以用作指示灯,LED都是高电平点亮: 4.3 按键 除了复位键,还有3个用户按键,都是上拉模式: 4.4 串口 板子上有一个9针的串口(公头),如果再有一个USB接口的串口就好了。 4.5 GD-Link 除了J-TAG接口,板子上带有一个GD-Link,这样通过USB线就可以下载调试程序了: 4.6 EEPROM EEPROM,原理图上为AT24C02,板子上实际是ATMLH009之类的字样(网上我没查到这个具体是什么型号,先不管它,等用到时再研究),IIC通信方式: 4.7 SPI-Flash 外部Flash,原理图上为GD25Q16,板子上实际是GD25Q32CS1G,SPI通信方式,使用时需要切换一下跳线帽: 4.8 外部SDRAM 型号为镁光的MT48LC16M16A2P-6AIT: 4.9 NAND Flash 原理图上给的型号为HY27UF081G2A,板子上实际为GD9FU1G8F2AMGI,这是换成GD自家的芯片了吗? 4.10 LCD显示屏 配有4.3英寸LCD显示屏与电阻触摸屏,屏幕分辨率为480*272: 4.11 摄像头 摄像头为OV2640: 4.12 网络接口 4.13 其它资源 板子上还有其它资源,以后用到时再详细介绍。 5 总结 总体上板子上的资源还是很丰富的,可以玩一阵了,哈哈。  

  • 2020-09-07
  • 回复了主题帖: 【测评入选名单公布】GD32450I-EVAL免费测评试用

    已确认个人信息,并确定可以按时完成测评

  • 2019-11-20
  • 加入了学习《Linux内核分析与应用 西安邮电大学(陈莉君)》,观看 Linux内核源码中的双链表结构

  • 加入了学习《Linux内核分析与应用 西安邮电大学(陈莉君)》,观看 Linux内核结构以及内核模块编程

  • 加入了学习《Linux内核分析与应用 西安邮电大学(陈莉君)》,观看 Linux 操作系统概述

最近访客

< 1/6 >

统计信息

已有135人来访过

  • 芯币:549
  • 好友:1
  • 主题:31
  • 回复:57
  • 课时:--
  • 资源:--

留言

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


现在还没有留言