赞
踩
使用GD32F450,芯片手册有明确的说明复位之后内部的初始化时间有至少140ms(应该是bss和data段等的初始化),无法满足测试需求,故选用其他芯片。
RISC-V由于是开源架构,在价格和开发支持上有一定的优势,这里选用先辑HMP6320型号,使用原生jlink调试时候固件至少是V10以上,软件驱动版本7.2以上,识别型号选择HPM6340xPAx,JTAG模式,暂不支持SWD。
以下按开发时间顺序记录踩过的坑及心得体会
一、外设配置
1.GPIO
(1)主要配置HPM_IOC->PAD[x].FUNC_CTL 和HPM_IOC->PAD[x].PAD_CTL这两个寄存器,但是GPIOY和GPIOZ还需要配置HPM_PIOC和HPM_BIOC。
(2)配置高速IO需单独配置FGPIO。
2.SPI
(1)引脚配置时SCLK必须要加上IOC_PAD_FUNC_CTL_LOOP_BACK_SET,同理当用作普通输出且需要读取输出状态时,也需要给引脚配置这一位。
(2)HMP6320的SPI资源较少,所以液晶和FLASH共用SPI,片选使用GPIO模拟,在写驱动时注意互斥的使用。
(3)调试FLASH时数据有错误,用逻辑分析仪抓取的时序发现两个问题:一是片选没有实际拉低时数据已经送出,所以在片选操作时候加一定的延时(这里应该还有一个解决办法就是把这个引脚配置成高速模式);二是片选在中途有被拉高的情况出现,明显是驱动能力不足,引脚配置IOC_PAD_PAD_CTL_DS_SET(7)增强驱动能力解决问题。液晶并没有发现类似的问题,应该是与SPI从设备的电器特性有关。
(4)做从机时候需要配置为纯数据模式:control_config.slave_config.slave_data_only = true。
3.DMA
使用HDMA进行外设与内存的数据传送:
(1)数据源(外设)必须配置成硬件握手机制——DMA_HANDSHAKE_MODE_HANDSHAKE。
(2)每次传送的字节数以及DMA使能位是在传送完成之后自动清除的,所以在计量芯片的高速口设计时需要在DMA传送完成中断里重新触发DMA。当然这里的前提条件是一次DMA的传送及重新触发需要的时间小于一包数据的时间间隔,否则就会丢数据。
(3)定义在AXI_SRAM这个区域的数据是不能使用DMA的,这个就跟GD32不太一样——ILD和DLM不能使用DMA,普通SRAM是可以的。
SRAM是可以用DMA的,原因是DMA传输的数据被cache缓存了,所以不变化。解决办法是把这块的数据放进nocache区或者重新放进DLM区里面,cache和DLM,ILM是一起的,不存在缓存的问题。
4.I2C
(1)引脚SDA和SCL均需要配置IOC_PAD_FUNC_CTL_LOOP_BACK_MASK。
(2)设备地址需要用数据手册的地址右移1位。
(3)对于读取从I2C设备指定地址值的时序需要用i2c_master_address_read接口,但是这个API有一个bug,就是在发送完设备地址和寄存器地址之后紧跟着发送了一个STOP信号,导致时序提前结束。这个问题是在调试RTC的时候发现的,但是奇怪的是E2PROM就没有问题。虽然时序是一样,但是两者现象不一样,只能解释为E2PROM对时序中的STOP信号并不“敏感”,时序要求并不是那么严苛。但是总归加STOP信号是不对的,因此改了SDK的i2c_master_address_read,运行正常。
5.ADC
配置好引脚之后,直接读取ADC的数据寄存器就会触发转换。这个比较方便,但是转换时间及转换完成标志并没有开放,按手册的理解应该是内部控制的,用户不需要特殊处理。本来还担心这样的模式直接在中断里读取会不会造成异常,目前看没有什么问题。后续会使用DMA传输,就不会有这样的担心。
6.USART
配置方面没有特殊的,需要注意的是使用自定义流控时,发送的时候需要保证最后一个字节发出去之后再控制发送方向——多判断一次发送完成标志。
二、关于FreeRTOS与RISC-V内核
1.第一版FreeRTOS是不支持中断嵌套的,后来改了一版但是出现死机——现象是在中断里发送完消息或者信号量之后立即进行调度时断taskSELECT_HIGHEST_PRIORITY_TASK失败,调试发现是pxReadyTasksLists内存溢出。最终查找原因是如下:
#define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31UL - __builtin_clz( uxReadyPriorities ) )
其中的__builtin_clz在RISC-V中,当传入参数是0的时候返回值是-1,导致pxReadyTasksLists数组下标溢出跑死了。
2.解决完上面BUG之后又发现程序跑一到两小时后就死机,异常地址,跑飞,完全没有前后调试信息。费好大功夫模拟现象最终确定是在串口中断里频繁发送消息时候死机,本身645规约有很多的BUG,例如结构体数据的大小不注意字节对齐的问题,用眼睛数出来的大小就是错误的(请使用sizeof保准不会错),所以干脆修改了645规约的处理机制——使用环形缓冲区用读写指针控制。
虽然问题再没有复现,但是这个BUG也不能完全确定就是645规约造成的,也有可能是RTOS的版本太新,是否在消息机制上存在BUG。
三、SDK
官方提供的API中涉及到寄存器标志判断的基本上都使用了超时模式,而不是死等,这样的好处是BSP及驱动可以在裸机和RTOS之间平移。
四、EEPROM
之前的平台有关I2C的标志位判断都采用了死等模式,这样在写EEPROM时等到ACK信号后就能保证写操作完成,这个时间根据EEPROM手册最小是5ms,这也就能解释为什么实际操作EEPROM的时间并不符合按照400KHz计算出来的。在HMP平台上,由于API都是超时判断模式,所以写操作之后需要添加大于5ms的延时来保证数据成功写入EEPROM。
五、BSS段初始化
BSS段是把未初始化的全局变量等初始化为0。
程序全部跑起来之后发现初始化时间并不是最开始测试的25ms,而是有100ms左右,时间全部耗费在bss段的初始化这里,因为HMP平台是把这块的工作放在了用户代码里。项目程序的bss段有将近130k,再加上FLASH是XIP模式,耗费时间长是符合实际情况的。解决办法就是把大数组溢出链接脚本的bss段,重新定义一块内存区。但是需要注意的是这些数据初值肯定不为0了,要确保运行之后进行赋值再使用!!!
六、eclipse环境配置注意事项
1.内核指令集按HMP数据手册配置。
2.Linker 设置里面最后一栏的other linker flag 默认有--specs=nosys.specs删除。
七、FreeRTOS新版本关于中断堆栈大小的配置
1.configISR_STACK_SIZE_WORDS:如果没有定义那中断堆栈就是用系统堆栈,可以自定义在ram中的位置
2.如果定义,一定注意这个值的大小。调试过程中发现有些内存数据会被莫名其妙的改为任务句柄,其实就是中断嵌套很多,导致从中断出来之后执行任务调度出错,改大这个值之后问题解决。
八、FreeRTOS下有关动态内存分配死机问题
1.增加外部引脚中断之后,进入中断就死机。最终定位到问题在任务中使用了malloc,改成系统内存分配函数vPortMalloc解决问题。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。