今天,我们选择三款产品,从开箱、电灯、测试、仿真多维度,深度评测国产自研RISC-VMCU的能力。
作者:xiashuang
初次上手
1.从官网下载的SEGGEREmbeddedStudioforRISC-V安装后Option对应设置,个人习惯把缩进和Tab都改为4,不然移植以前的文件格式很难看。
2.我以前使用KEIL时大量注释编码和汉字显示使用了GB2312,在SES中可以做如下设置,使显示正确。
3.如果使用SES内的SDK包生成工程文件如果调试器使用FTDI需要设置GDB调试,同时配置openocd比较麻烦,建议使用SDK命令行配置(generate_project-bhpm6750evkmini)。
4.关于分散加载文件可以使用generate_project-bhpm6750evkmini后增加-t选项例如:-tflash_sdram_xip也可以在Linker里面选择,选择完后重新编译即可。
分散加载文件官方SDK已经写好,因为HPM6750的内部SRAM均固定大小所以基本不需要修改配置,我主要使用两种类型的:
①flash_xip代码存储在flash中,上电后从flash取指令和数据分别进入ILM和DLM,当缓存未命中会影响执行速度,好处不用外扩SDRAM,仅仅使用低成本flash就可以,关键代码和中断也可放到SRAM提高速度。
②flash_sdram_xip代码存储在flash中,上电后先把flash数据复制到SDRAM中,程序在SDRAM中执行,好处是程序执行速度快,但SDRAM较昂贵,另外不同SDRAM上电初始化代码不太一样,需要重写代码。另外官方还提供flash_uf2和flash_sdram_uf2大概是usbapp方式。
裸机移植agile_modbus
2.增加头文件路径
3.协议栈初始化
/*initmodbusstack*/uint8_tctx_send_buf[AGILE_MODBUS_MAX_ADU_LENGTH];uint8_tctx_read_buf[AGILE_MODBUS_MAX_ADU_LENGTH];agile_modbus_rtu_tctx_rtu;agile_modbus_t*ctx=&ctx_rtu._ctx;agile_modbus_rtu_init(&ctx_rtu,ctx_send_buf,sizeof(ctx_send_buf),ctx_read_buf,sizeof(ctx_read_buf));agile_modbus_set_slave(ctx,1);4.串口使用uart13,中断代码如下
if(uart_rx_outtime==1){//printf("t");uart_rx_outtime=0;memcpy(ctx_read_buf,data_buff,buff_index);intsend_len=agile_modbus_slave_handle(ctx,buff_index,0,slave_callback,NULL);if(send_len>0){data_count=0;buff_index=send_len;memcpy(data_buff,ctx_send_buf,buff_index);uart_flush(TEST_UART1);uart_enable_irq(TEST_UART1,uart_intr_tx_slot_avail);uart_disable_irq(TEST_UART1,uart_intr_rx_data_avail_or_timeout);}else{buff_index=0;}}7.slave_callback不需要做修改,因为我使用逻辑,所以注释掉互斥访问
总结:HPM官方提供UART中断收发代码中中断状态读取在实际使用中不太合理,发送使用阻塞发送若果不使用阻塞发送将会导致程序卡死在接收中断处理里面的while(1),增加中间变量uint8_tuart_irq_state=uart_get_irq_id(TEST_UART1)使用中间变量判断状态就OK了,怀疑每次读中断状态将把中断寄存器中断清除。agile_modbus比freemodbus移植和使用点单多了,如果有需要使用modbus的建议试试看看。
软件模拟SPI采集ADS1263(32位ADC)数据
为了先验证板子的情况,先用软件模拟SPI进行实验,需要7根信号线进行连接通讯,定义的GPIO如下:
MODBUS上位机画面,使用50kgC3电阻应变桥式称重传感器采样400次精度在±1g,后来降低采样到60次/s,滑动平均5次,精度在±0.2g。
逻辑分析仪抓取波形图。采用软件模拟SPI,速度在3M左右,读取命令+状态+4字节数据+校验共7个字节数据在20.5us左右。
实验总结:
1.HPM6750在配置IO时要注意名称,因为IO引脚较多,宏定义也比较多,在初始化容易写错(这次因为IO编号写错导致两个IO口没有输出,查了1个多小时)。看来图形化代码工具还是很有必要的。
2.HPM6750的驱动强度和施密特单独出来和I.MXRT系列比较像增加了IO控制的灵活性,PCB上高速信号的抗信号反射电阻和驱动限流电阻都可以省去了,等以后试下芯片的施密特能否代替外部输入上的74HC14。
3.HPM6750运算速度很快,等以后试试高阶FIR看看
4.ADS1263的高速采样及其以来模拟电源的纹波,对内部DCDC电源还是要增加滤波器,采样60HZ对50-hz和60hz纹波抑制后精度一下就上来了。
测试CAN收发
HPM6750有4个CAN控制器,支持CANFD,目前在工业上用的还是CAN2.0比较多,本次测评使用CAN1控制器。CAN驱动芯片使用TI的VD230,电脑端使用了一个USB转CAN调试器,带有调试上位机软件。
连线如下:
1.初始化CAN引脚和时钟
HPM_IOC->PAD[IOC_PAD_PE31].FUNC_CTL=IOC_PE31_FUNC_CTL_CAN1_TXD;HPM_IOC->PAD[IOC_PAD_PE30].FUNC_CTL=IOC_PE30_FUNC_CTL_CAN1_RXD;/*SettheCAN1peripheralclockto80MHz*/clock_set_source_divider(clock_can1,clk_src_pll1_clk1,5);freq=clock_get_frequency(clock_can1);2.初始化CAN
can_config_tcan_config;can_get_default_config(&can_config);can_config.baudrate=500000;/*500kbps*/can_config.mode=can_mode_normal;board_init_can(HPM_CAN1);uint32_tcan_src_clk_freq=board_init_can_clock(BOARD_APP_CAN_BASE);hpm_stat_tstatus=can_init(HPM_CAN1,&can_config,can_src_clk_freq);if(status!=status_success){printf("CANinitializationfailed,errorcode:%d\n",status);return;}3.设置CAN中断
can_enable_tx_rx_irq(HPM_CAN1,CAN_EVENT_RECEIVE);intc_m_enable_irq_with_priority(IRQn_CAN1,1);4.CAN中断
SDK_DECLARE_EXT_ISR_M(IRQn_CAN1,test_can1_isr);voidtest_can1_isr(void){uint8_tflags=can_get_tx_rx_flags(HPM_CAN1);if((flags&CAN_EVENT_RECEIVE)!=0){can_read_received_message(HPM_CAN1,(can_receive_buf_t*)&s_can_rx_buf);has_new_rcv_msg=true;}if((flags&(CAN_EVENT_TX_PRIMARY_BUF|CAN_EVENT_TX_SECONDARY_BUF))){has_sent_out=true;}if((flags&CAN_EVENT_ERROR)!=0){has_error=true;}can_clear_tx_rx_flags(HPM_CAN1,flags);error_flags=can_get_error_interrupt_flags(HPM_CAN1);can_clear_error_interrupt_flags(HPM_CAN1,error_flags);}5.CAN发送接收到的数据
while(!has_new_rcv_msg){}has_new_rcv_msg=false;show_received_can_message((constcan_receive_buf_t*)&s_can_rx_buf);can_transmit_buf_ttx_buf;memset(&tx_buf,0,sizeof(tx_buf));tx_buf.dlc=s_can_rx_buf.dlc;tx_buf.id=0x321;uint32_tmsg_len=can_get_data_bytes_from_dlc(s_can_rx_buf.dlc);memcpy(&tx_buf.data,(uint8_t*)&s_can_rx_buf.data,msg_len);status=can_send_message_blocking(BOARD_APP_CAN_BASE,&tx_buf);if(status!=status_success){printf("CANsentmessagefailed,error_code:%d\n",status);return;}6.测试程序,设置CAN调试器为CA2.0,波特率500k,标准模式,依次发送数据,接收均正常。
测试以太网控制器ENET
HPM6750有2个千兆以太网控制器,MINI板引出了一个RMII接口以太网控制器HPM_ENET1的PIN,看SDK中有一个lwip的tcpecho例程默认使用的PHY是DP83848,翻了翻箱底找出了个DP83848模块开干。
1.连接杜邦线
2.修改例程中netconf.h的IP如下:
/*StaticIPADDRESS*/#defineIP_ADDR0192#defineIP_ADDR1168#defineIP_ADDR21#defineIP_ADDR3108/*NETMASK*/#defineNETMASK_ADDR0255#defineNETMASK_ADDR1255#defineNETMASK_ADDR2255#defineNETMASK_ADDR30/*GatewayAddress*/#defineGW_ADDR0192#defineGW_ADDR1168#defineGW_ADDR21#defineGW_ADDR313.修改tcp_echo.h中TCP端口号:
#defineTCP_ECHO_PORT(502U)4.电脑修改网口IP
5.编译下载程序后打开TCP后TCP连续发送后echo回显。
这个例程折腾了很久,最后用手按着杜邦线才收发成功,这种高速的接口用杜邦线连接就是个考验,接口连接不牢固以及长线的阻抗匹配等都是重要因素,本来想移植ModbusTCP试试,但硬件不允许等以后有机会了再弄吧!
测试I2C与ADS1115通信
HPM6750有4个I2C,HPM6750EVKMINI排针引出了2个I2C,模块侧有上拉电阻,所以使用板子上的I2C0,但比较遗憾的是HPM6750的I2C最高速度只支持1000k,不知道以后会不会支持3.4M。
ADS1115是TI家的兼容I2C的16位高精度低功耗模数转换器(ADC),I2C的最高速度是3.4M,本来想测一下3.4M的I2C,后来查看HPM6750的手册看不支持3.4M。
找了个ADS1115模块,连接杜邦线开干。
主要程序如下:
1.初始化I2C,速度为1000K
init_i2c_pins(TEST_I2C);i2c_config_tconfigi;uint32_tfreq;configi.i2c_mode=i2c_mode_fast_plus;//i2c_mode_normalconfigi.is_10bit_addressing=false;freq=clock_get_frequency(TEST_I2C_CLOCK_NAME);stat=i2c_init_master(TEST_I2C,freq,&configi);if(stat!=status_success){returnstat;}2.定义I2C地址,因为我接线将ADS1115的ADDR直接接地,所以I2C地址为0x48
#defineADS1115_WRITE_ADDRESS(0x90>>1)//0x90#defineADS1115_READ_ADDRESS(0x90>>1)//0x913.配置寄存器
voidads1115_config_register(uint8_tpointADD,uint8_tconfigH,uint8_tconfigL){uint8_treg_data[3]={pointADD,configH,configL};if(status_success!=i2c_master_write(TEST_I2C,ADS1115_WRITE_ADDRESS,reg_data,3)){printf("IICwritefailed");printf("ads1115ConfigRegistererror!!!\r\n");while(1){}}}4.读数据
int16_tads1115_read_data(void){int16_tdata;uint8_trx_data[2]={0};if(status_success!=i2c_master_write(TEST_I2C,ADS1115_WRITE_ADDRESS,0x00,1)){printf("IICwritefailed");printf("ads1115convertRegistererror!!!\r\n");while(1){}}if(status_success!=i2c_master_read(TEST_I2C,ADS1115_READ_ADDRESS,rx_data,2)){printf("IICreadfailed\n");printf("ads1115readdataerror!!!\r\n");while(1){}}data=rx_data[0]<<8|rx_data[1];returndata;}5.获取电压值
doubleads1115_get_voltage_val(uint8_tpointADD,uint8_tconfigH,uint8_tconfigL){doubleval;int16_tad_val;ads1115_config_register(pointADD,configH,configL);delay_ms(10);//等待10msad_val=ads1115_read_data();if((ad_val==0x7FFF)|(ad_val==0X8000))//是否超量程了{ad_val=0;printf("overPGA\r\n");}switch((0x0E&configH)>>1)//量程对应的分辨率{case(0x00):val=(double)ad_val*187.5/1000000.0;//break;case(0x01):val=(double)ad_val*125/1000000.0;break;case(0x02):val=(double)ad_val*62.5/1000000.0;break;case(0x03):val=(double)ad_val*31.25/1000000.0;break;case(0x04):val=(double)ad_val*15.625/1000000.0;break;case(0x05):val=(double)ad_val*7.8125/1000000.0;break;}returnval;}6.下载程序分别测试0V和3.3V
总结:1.HPM6750的I2C最高速率目前是1000K,2.HPM6750的库中I2C是给7bit地址,读写函数自动移位和增加读写位,所以只需要定义I2C从机实际地址就可以。
16位ADC测试
HPM6750含有1个16位ADC,2MSPS采样率,看起来挺诱人的,连线试试看。
使用官方SDK中ADC16例程,翻出来古老的51开发板,使用里面的电位器模拟量,连线如下:
我想直接算出电压比较,在采样结果程序中增加一行。
printf("valueis%f",result*3.3/65535);编译下载输出如下:
看起来偏差很大,于是乎看手册想把采样速率或者采样平均数增加,但手册写的很简单,寄存器的解释看起来也很仓促,那就盲改程序吧:
把配置函数voidinit_common_config(adc16_conversion_mode_tconv_mode)中ch_cfg.sample_cycle修改如下:
ch_cfg.sample_cycle=0x1ff;//200x1ff继续编译下载查看输出:
看起来好多了,再增加一点滑动平均滤波
#definenum20int32_tvalue1=0;for(inti=num;i>=0;i--){data[i+1]=data;value1+=data;}data[0]=result;value1+=result;value1/=(num+1);printf("valueis%f",value1*3.3/65535);编译下载输出如下:
旋转电位器测试了下响应很快。
总结:
1.本次只是简单测试了下ADC,使用的是电位器分压,没有模拟前端缓冲和滤波,会影响精度和稳定;
2.使用的参考电压均为电脑USB供电,纹波会比较大也会影响精度,但通过滤波还是能很好的反应芯片本身的模拟性能;
3.吐槽一下手册确实太精简了,希望再详尽一点。
作者:qinyunti
CDK仿真体验
仿真在嵌入式开发中非常重要,一款芯片和IDE的仿真能力及其使用友好性上非常重要。
很多问题必须要仿真才能高效解决。仿真的断点,变量查看,寄存器查看,栈回溯,条件断点,数据断点,存储查看等等都非常重要。
首先我们就体验下CDK的仿真环境。
点击红色的d下载并进入仿真环境:
如果代码没有修改可以直接点击蓝色d进入仿真环境。
调试窗口:菜单栏Debug->DebugViews可以显示各窗口。
代码断点:c代码断点,在代码前点击右键->ToggleBreakpoints,打代码断点,然后点击运行即可运行到断点处。
汇编级断点:有时c代码断点并不精确,需要定位到具体那一条指令就打开汇编窗口在汇编代码前单击打断点。
单步运行:如下三个按钮分别是单步进入函数,单步不进入函数,跳出函数。
内核寄存器:内核寄存器在判断处理器运行状态异常原因时很重要。
C和汇编对照:C有时并不能看到具体的行为,只有和汇编对照才知道到底执行的是什么指令,在分析编译器优化到这问题时就需要一条条看汇编代码。
函数内局部变量:自动显示函数内局部变量还是比较方便的。
函数调用栈:可以知道函数的调用路径,分析在什么执行路径下出现问题。
内存查看:在协议分析,数据处理等时往往需要查看缓冲区的内容导致是什么,或者查看寄存器的只。
Watch全局变量:选中全局变量右键点击addwatch'xxx’,就可以实时查看变量的值。
总结:总的来说基本的仿真功能都有,但是没有外设寄存器查看,内核外设查看等。仿真操作也不是很顺畅经常卡死,用户体验有待增强。
RGB点灯
拿到开发板之后马上点个灯是对开发板最起码的尊重,所以我们这一篇就来点板载的RGB灯。
下面开始创建工程。菜单栏View->ShowWelcomePage,新建工程。
搜索BL606创建工程。
输入工程名LED,下载方案。
指定工程名和路径。
左侧工程红色的表示没有下载成功。
右键点击从Web下载。
修改脚本:LED\__workspace_pack__\bl606p_evb_e907\v7.6.2\script\aft_build.sh的如下位置加-kp参数,否则重启后不能运行。
修改源码:
LED\app\app_main.c中#include
下载:将如此说四个跳线帽跳线到JTAG这一边(靠近RGBLED这一边)。
点击下载:
运行:将上述跳线帽JP678跳到另外一边,上电可以看到RGB灯三色依次亮灭。
总结
RGB点灯之后JTAG不能下载问题解决
下载RGB点灯程序之后发现无法JTAG下载了。
由于RGB的GPIO171819和JTAG复用了,RGB点灯之后,用户代码将引脚配置为了IO,所以就用不了JTAG了。所以需要要停止用户代码运行,然后连接JTAG。
可以断开USB电源,按住BOOT按键,然后接上USB电源上电,此时不会运行用户程序,就可以JTAG下载了,下载之后就可以松开BOOT按键了。
ADC测试-虚拟示波器Demo
我们测试了IO,继续ADC接口测试。我们可以采集ADC值从串口打印,然后PC端接收打印的的值进行解析,曲线显示,这样就实现了虚拟示波器的功能。
引脚:使用GPIO16对应CH8GPIO16_ADC_CH8。
代码:
包含头文件
#include
csi_pin_set_mux(GPIO_PIN_16,GPIO16_ADC_CH8);配置ADC
adc_dev_tadc;adc.port=8;adc.config.sampling_cycle=100;hal_adc_init(&adc);采集值并打印
while(1){uint32_tvalue=0;hal_adc_value_get(&adc,&value,HAL_WAIT_FOREVER);LOGD("INFO","/*%d*/\r\n",value);}测试:使用旋转变压器分压,输入模拟电压,到引脚GPIO16。
点击JSON编辑器按如下设置:
点击添加组别:
添加数据集:
按如下设置点击申请:
连接串口控制台可以看到打印:
仪表盘可以看到曲线:
以上完成了ADC的测试,实现了虚拟示波器的功能。说实话官方的文档写的太随意了,参数都不知道怎么写,很多时候靠猜,Demo也不完整,希望文档方面能加强。另外官方的SDK也是一言难尽,文档又不规范,所以只能说测试一下还可以,要用到产品还有很长的路要走,至少文档这是一个态度问题,无关技术。好用的东西才会有人用,所以文档用户体验很重要。
PWM测试-呼吸灯效果实现
继续接口测试,测试PWM的输出,测试其频率和占空比的设置,以及使用PWM控制LED实现呼吸灯的效果。
引脚:使用GPIO16:
对应PWM0的输出GPIO16_PWM0_CH0P
#include
csi_pin_set_mux(GPIO_PIN_16,GPIO16_PWM0_CH0P);PWM配置
pwm_dev_tpwm;pwm.port=0;pwm.config.freq=5000;pwm.config.duty_cycle=0.2f;hal_pwm_init(&pwm);启动
hal_pwm_start(&pwm);测试
输出不同频率不同占空比的值,使用逻辑分析仪测试。
呼吸灯代码
while(1){for(inti=0;i<=100;i++){pwm_cfg.duty_cycle=i/100.0;hal_pwm_para_chg(&pwm,pwm_cfg);aos_msleep(20);}for(inti=100;i>=0;i--){pwm_cfg.duty_cycle=i/100.0;hal_pwm_para_chg(&pwm,pwm_cfg);aos_msleep(20);}}可以看到占空比从0~100变化。下图是99~100~99的过程。
将GPIO16接到JP8的GPIO19_LED_B,用GPIO16的占空比控制蓝色LED的亮灭。
可以看到呼吸灯的效果。实际灯的亮度并不是跟电压呈线性关系的,所以占空比和亮度也不是线性的,所以要按照实际LED的特性进行优化,按照查表得到不同亮度和占空比的对应关系,这样输出才会是均匀变化。
以上完成了PWM的测试,使用逻辑分析仪测试了占空比和频率的设置的正确性。同时用PWM控制LED实现了呼吸灯的效果。
coremark基准性能测试
按照测评计划,我们这一篇进行性能测试。CPU基准性能测试有很多种,比较常见的是coremark,我们就以coremark进行CPU的基准性能测试。
准备代码,下载代码。只保留如下内容,并添加到工程app目录下:
修改代码
core_portme.h中
#defineHAS_PRINTF0改为#defineHAS_PRINTF1
#defineee_printfprintf
改为
#defineee_printfaos_cli_printf
添加#include"aos/cli.h"
添加#defineMAIN_HAS_NOARGC1
FLAGS_STR按照实际改为”-O0”或者”-O3”等。
typedefsize_tee_size_t;改为
typedefunsignedintee_size_t;
添加#defineITERATIONS10000
core_portme.c中
barebones_clock(){#error\"Youmustimplementamethodtomeasuretimeinbarebones_clock()!Thisfunctionshouldreturncurrenttime.\n"}改为
barebones_clock(){//#error\//"Youmustimplementamethodtomeasuretimeinbarebones_clock()!Thisfunctionshouldreturncurrenttime.\n"returnaos_sys_tick_get();}注释掉
#defineTIMER_RES_DIVIDER1#defineCLOCKS_PER_SEC(100)core_main.c中main函数改为coremark_main
core_util.c/coremark.h还有其他文件中的crc16改为core_crc16
main函数中调用
voidcoremark_main(void);coremark_main();测试:设置优化等级。
2Kperformancerunparametersforcoremark.CoreMarkSize:666Totalticks:10261Totaltime(secs):102.610000Iterations/Sec:97.456388Iterations:10000Compilerversion:GCC10.2.0Compilerflags:-O3Memorylocation:STACKseedcrc:0xe9f5[0]crclist:0xe714[0]crcmatrix:0x1fd7[0]crcstate:0x8e3a[0]crcfinal:0x988cCorrectoperationvalidated.SeeREADME.mdforrunandreportingrules.CoreMark1.0:97.456388/GCC10.2.0-O3/STACK蓝牙播放音频测试
安装喇叭如下:
解压到非中文路径,双击打开longyuan_v2.1.1\solutions\bt_audio_demo\project.cdkproj
编译:
下载,注意JTAG跳线。
复位可以看到串口打印:
用手机扫描蓝牙,找到"BTAudioDemo"设备点击,配对。
配对成功后串口打印如下:
串口输入以下指令播放:
btavrcppt680btavrcppt681串口打印如下:
手机中看到播放内容:
输入
btavrcppt690btavrcppt691暂停总结
通过蓝牙进行WIFI配网测试
WiFi作为一种常见的无线接入手段,在使用前需要配置SSID和Password。通过wifi_provisioning组件,开发者可以方便的实现多种配网方式。
常用的配网方式有:
SoftAP配网,通过设备上WiFi,生成一个临时网络,手机接入临时网络,完成配置。BLE蓝牙配网,通过BLE配置WiFi网络。其中BLE蓝牙配网方案,无论是成功率还是用户体验均优于SoftAP配网方案。
本篇测试使用BLE蓝牙进行WIFI配网。
过程:安装喇叭如下。
解压到非中文路径,双击打开longyuan_v2.1.1\solutions\wifi_ble_provisioning_demo\project.cdkws
下载,注意JTAG跳线
重启,串口打印如下:
使用手机,安装NRFConnectApp,点击右上角的SCAN,扫描设备名称为"WBP_DEMO"。
点击CONNECT:
点击如下位置:
分别点击如下向上箭头:
选择TEXT输入WIFI用户名,SEND:
选择TEXT,密码,SEND:
串口可以看到连接了WIFI:
以上体验了通过蓝牙快速进行WIFI配网,这在物联网场景中是非常有用的,比通过WIFI设置热点再配置路由器的用户名和密码更快捷。
作者:常见泽1
流水灯跑跑跑
一、开盒
经历了一些波折,终于拿到了GD32VF103CSTART开发板,外壳依旧是GD的一贯风格,白色外壳,但是纸壳厚度明显比之前Cortex内核的开发板要厚重,盒子开关卡扣也变成了磁铁吸合的,看起来比之前GD的开发板高端。
打开盒子,映入眼帘的是乳白色的开发板,这个白色比STNUCLEO板要更白一点,没有NUCLEO板看起来厚重,另外GD的两边排针都是空的没有焊接,ST的一般都是焊接好的。
吐槽一下,GD的RISC-V板子过孔也太多了而且白色板子就特别特别明显,我个人看起来不是特别的舒服。(看图片还可以,实物是有点不太舒服的)
二、软件安装
Windows环境下搭建基于Eclipse+RISC-Vgcc编译器的RISC-V开发环境,配合openocd调试软件,可以实现RISC-V内核程序的编译、下载和调试。下载直接去的芯来科技官网下载的开发环境。
安装过程中碰到个小问题:
Eclipse启动提示javaw.exeinyourcurrentPATH
可修改eclipse.ini文件,在最前面加上下面两行内容:
-vm
D:/jva/bin/javaw.exe
三、工程建起来
点击FINISH即可。
产生的例程是EVAL板的,而我用的是START板,GPIO略有区别,需要修改修改。
查看原理图,修改部分代码
编译真的比较慢,下载仿真调试更更更慢了!可以喝一杯茶再来了。看现象LED开始闪烁,OK第一个例程成功。
和ST官方例程简单比较
粗略看了下GD的官方例程,感觉还是保持了ST官方例程的大体结构,基本做过ST开发的很容易能明白。基本差不多,除了库的位置放置略不同。
下面这是ST的官方例程的目录:
我们打开GD的官方例程:
A.GD32VF103_standard_peripheral子文件夹
1.固件库头文件
2.固件库源文件
3.GD32VF103全局头文件和系统配置文件
B.GD32VF103_standard_peripheral子文件夹
C.RSIC-V文件夹
1.drivers子文件夹包含RISC-V内核的支持文件
2.env_Eclipse子文件夹包含了基于RISC-V内核处理器的启动代码、异常服务程序及链接脚
本文件
3.stubs子文件夹包含了_write、_read等桩函数的定义
工程放置位置:
打开key中断例程工程文件夹:
1.gd32vf103_it.c/.h
中断处理程序及头文件,和ST的类似
2.gd32vf103_libopt.h
设置例程所使用的外设,包含了什么adc.hgpio.h头文件,感觉有点像ST的conf.h那个头文件(个人感觉,可能并不是这个意思)
3.main.c
用户的例程
4.systick.c
使用systick的精准延时程序
代码对比
简单拿一个GPIO例程来,GDRISC-V的和STM3的很相似,只是库函数的名字改了,基本参数名字改了,时钟和IO口需要配置的东西基本类似。
GD的GPIO函数库如下:
所以我们编程只要抓住GD的库函数手册就可以啦,就是GD32VF103固件库用户指南.pdf。
RISC-V初步了解
RISC-V(发音同“risk-five”)是一种免费开源指令集架构(ISA),通过开放标准协作开创处理器创新的崭新纪元。RISC-V基金会创立于2015年,由超过235家成员组织组成,建立了首个开放、协作的软硬件创新者社区,开创了处理器创新的新时代。RISC-VISA发端于深厚的学术研究,将免费且可扩展的软硬件架构自由度提升至新的水平,为未来50年的计算设计与创新铺平了道路,国产CPU逆袭之路。
RISC-V是由U.C.Berkeley开发的自由和模块化的RISC指令集,“V”包含两层意思,一是这是Berkeley从RISCI开始设计的第五代指令集架构,二是它代表了变化(variation)和向量(vectors)。
不同于x86、Arm架构高昂的IP费用,RISC-V架构使用BSD开源协议给予使用者很大自由,允许使用者修改和重新发布开源代码,也允许基于开源代码开发商业软件发布和销售。
GD32VF103采用RISC-V的Bumblebee处理器内核。Bumblebee内核设计了二级变长流水线微架构,配备了精简的指令预取单元和动态分支预测器,并融入多种低功耗设计方法,能够以二级流水线的代价,达到传统架构三级流水线的性能和频率,实现了优秀的能效比与成本优势。所以GD32VF103在最高主频下的工作性能可达153DMIPS,CoreMark测试也取得了360分的优异表现,相比GD32Cortex-M3内核产品性能提升15%的同时,动态功耗还降低了50%,待机功耗更是降低了25%。
指令集就是一个处理器的硬件可以支持的基本操作(符号化的抽象描述)的集合。(wiki:“Aninstructionset,withitsinstructionsetarchitecture(ISA),istheinterfacebetweenacomputer'ssoftwareanditshardware,andtherebyenablestheindependentdevelopmentofthesetwocomputingrealms;itdefinesthevalidinstructionsthatamachinemayexecute.”)。
通常,处理器的指令集架构(ISA:InstructionSetArchitecture)确定它的功能。最著名的x86就是intelCPU的指令集。一个通用处理器,为了适应所有的应用,其指令集必须考虑最大的灵活性。这种灵活性主要表现在指令功能是不是完备和粒度是不是够细。
架构师希望保持ISA的简洁性,从而缩小实现ISA的处理器的尺寸。我们将在随后的章节看到,RISC-VISA比ARM-32ISA简洁得多。就简洁性造成的影响举例,我们把使用相同大小缓存(16KiB)的RISC-VRocket处理器和采用相同技术(TSMC40GPLUS)的ARM-32CortexA5处理器进行比较。RISC-V晶粒的大小是0.27mm2,而ARM-32晶粒的大小是0.53mm2。由于面积大一倍,ARM-32CortexA5的晶粒成本是RISC-VRocket的约4(22)倍。即使晶粒的大小只缩小10%,成本也将以1.2(1.12)倍的比例缩小。
ARM-32的架构师后来试图通过向以前统一的32位ISA中添加16位指令来缩减代码长度,但根本就没有空间了。因此,唯一的解决方案是先用16位指令来创建一个新的ISA(Thumb),然后同时用16位指令和32位指令来组成另外一个ISA(Thumb-2),并用一个模式位在两种长度的指令间切换。为了切换模式,程序员或编译器会跳转到一个最低有效位为1的字节地址。这种方法有效的前提是,在正常的16位和32位指令中,该位应该是0
RISC-V和Corex-M3指令测试
加减乘除运算应该是每个人编程都会用得到,本篇就想看看RSIC-V和M3的加减乘除取余等等运算有什么区别。
乘法运算:
M3
RSIC-V
简单的例子反汇编:
RSIC-V反汇编
除法运算:
长期以来,ARM-32只有乘法而无除法指令。直到第一台ARM处理器诞生的大约20年后(2005年),除法指令才成为ARM的必要组成部分
Cortex-M3包含两个硬件除法指令,SDIV和UDIV(Thumb-2指令)
RSIC-V带除法指令
M3整型除法汇编
RSIC-V浮点数除法汇编运算
取余运算:
RV32M具有有符号和无符号整数的除法指令:divide(div)和divideunsigned(divu),它们将商放入目标寄存器。在少数情况下,程序员需要余数而不是商,因此RV32M提供remainder(rem)和remainderunsigned(remu),它们在目标寄存器写入余数,而不是商。
而M3是没有取余的,需要通过乘法和加减一起来实现。
试验跑起来
由于我逻辑分析仪是20M采样率,我把RSIC-V和M3的主频都设置在了24MHZ,否则测的不会很准,毕竟乘除法可能只是几个指令周期的活。
(1)num=i*j+i*k乘法Ij整型数据
(2)num=i*j乘法Ijfloat型数据
(3)num=i/j+i/k除法Ij是整型数据
M3是硬件除法器,在整型除法里耗时比RISC-V要短。
(4)num=i/j+I/K除法Ij是float型数据
在浮点数除法运算力,RISC-V的表现是快了一半。
(5)i%j+i%k整数取余
虽然RISC-V的取余是直接得到,和M3不一样,但是耗时却更加长了,不知道改的意义在哪里???
总体来说,RISC-V和Cortex-M3在同等频率、同等运算下进行测试,很多运算指令执行速度要相对快一些。
STARTPWM
先来看下RISC-VGD的103的定时器的总览:
这次主要参考官方例程,使用PWM模式:
在PWM输出模式下(PWM模式0是配置CHxCOMCTL为3’b110,PWM模式1是配置
CHxCOMCTL为3’b111),通道根据TIMERx_CAR寄存器和TIMERx_CHxCV寄存器的值,
输出PWM波形。
根据计数模式,可以分为两种PWM波:EAPWM(边沿对齐PWM)和CAPWM(中央对齐PWM)
参考M3的PMW描述,两者是差不多的:
这里我们选用PWM模式0.
下面代码开始配置PWM的时钟、GPIO、模式及参数:
这里分频系数119周期500主频是108M算下来要你管管是1.8khz左右,占空比50%
其实和M3真的很像,见下图。
基本一样,主要就是在通道参数配置那里略有不同,貌似多了两个函数
timer_channel_output_pulse_value_config(TIMER0,TIMER_CH_0,250); timer_channel_output_mode_config(TIMER0,TIMER_CH_0,TIMER_OC_MODE_PWM0);而M3是由结构体完成初始化
我们看下试验现象
STARTEXTI中断
RISC-V:EXTI(中断/事件控制器)包括19个相互独立的边沿检测电路并且能够向处理器内核产生中断请求或唤醒事件。EXTI有三种触发类型:上升沿触发、下降沿触发和任意沿触发。EXTI中的每一个边沿检测电路都可以独立配置和屏蔽
STM32:EXTI(Externalinterrupt/eventcontroller)—外部中断/事件控制器,管理了控制器的20个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。
看一下RISC-VEXTI框图
CortexEXTI图
上图就可以看出不一样了,STM32是通过NVIC有个,而RISC采用了ECLIC
RISC-V集成了改进型内核中断控制器(ECLIC)来实现高效的异常和中断处理。ECLIC旨在为RISC-V系统提供低延迟、矢量化、抢占式的中断。当ECLIC被激活后,它将包含并替换现有的RISC-V处理器局部中断控制器。CLIC设计有一个基本设计,需要最少的硬件,但它支持额外的扩展来提供硬件加速。ECLIC设计的目标是为各种软件ABI和中断模型提供支持,而不需要复杂的硬件影响高性能处理器的实现。它与处理器核心紧密耦合。
Cortex图
Cortex配置的步骤:
通过下面的过程来配置20个线路做为中断源:
●配置20个中断线的屏蔽位(EXTI_IMR)
●配置所选中断线的触发选择位(EXTI_RTSR和EXTI_FTSR);
●配置对应到外部中断控制器(EXTI)的NVIC中断通道的使能和屏蔽位,使得20个中断线中的请求可以被正确地响应
RISC-V中断配置
Cortex中断配置
下面就和RISC不一样了,RISC中断等级等等都在ECLIC,而CORTEX还得配置下NVIC
中断函数
RISC-V
Cortex-M3
这个没啥说的,基本都一样句柄。
希望通过三款产品的深度评测,帮助工程师快速了解三款产品。未来,我们还会继续深度评测更多国产RISC-VMCU,敬请期待。