芯路恒电子技术论坛

 找回密码
 立即注册
热搜: 合集
查看: 8228|回复: 5

【zynq课程笔记】【裸机】【第6课 】【C编程基础】

[复制链接]
  • TA的每日心情
    慵懒
    2021-2-24 10:16
  • 428

    主题

    811

    帖子

    1万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    16109
    QQ
    发表于 2022-1-13 17:05:13 | 显示全部楼层 |阅读模式

    更多课程笔记请查看:【zynq裸机编程课程笔记合集】 http://www.corecourse.cn/forum.php?mod=viewthread&tid=29095

    本节视频课程学习地址(需复制链接到浏览器才能打开):www.bilibili.com/video/BV1Ra411q7ww?p=6

    课程大纲

    1、什么是bsp(board support package)

    BSP的全称为Board Support Package,中文意思是板级支持包,事实上对于Zynq来说,bsp的确切含义应该是硬件系统支持包,我们创建的BD文件添加的PS、以及添加的各种扩展IP,共同组成了基于PS的硬件系统,该硬件系统中包含的各个PS和PL的外设功能,Xilinx原厂都为用户提供了预先编写并验证好的驱动程序,以及实现各种常用功能时需要用到的函数,这些驱动程序以及常用函数组合在一起,共同实现了对该特定硬件系统的编程的支持,用户编程时,可以使用该支持包中提供的驱动和函数,来避免自己编写应用函数和基于寄存器读写的驱动。由于这些驱动程序中加了很多安全判断和兼容操作,所以,在对程序尺寸和运行效率要求不高的场合,推荐使用BSP提供的驱动和函数,而在对性能和程序尺寸有要求的场合,推荐自己编写基于寄存器读写的驱动。

    2、如何实现对指定地址的读写操作

    使用指针

    如何实现对指定地址的读写呢,根据CPU的工作原理,每一项操作的本质,都是对指定地址进行数据的读写操作。所以从CPU编程角度来说,我们只需要知道每个寄存器的绝对地址,就可以使用指针的方式读取或写入该存储器/寄存器。例如对地址为0x00000020的寄存器进行读写,就可以使用下面的形式:

    • 读寄存器:return (volatile u8 ) 0x00000020;
    • 写寄存器:(volatile u8 ) 0x00000020 = 0x12;

    使用IO读写函数

    xilinx提供的基本的地址读写函数,这些函数位于xil_io.h文件中,如下所示:

    Xil_In8(addr); Xil_In16(addr); Xil_In32(addr); Xil_In64(addr); Xil_Out8 (addr, data); Xil_Out16(addr, data); Xil_Out32(addr, data); Xil_Out64(addr, data);

    如果打开任意一个函数查看,会发现这些函数就是对指针操作的封装而已。如下所示:

    static INLINE u32 Xil_In32(UINTPTR Addr) { return (volatile u32 ) Addr; }

    3、如何知道各个外设的硬件信息(寄存器地址,位功能)

    经过上面的分析,我们了解到,CPU的编程就是对各个地址的存储器/寄存器进行读写操作 ,那么这些地址分别是多少呢?换句话说,如何知道哪个地址的数据能够对应代表什么功能,或者说,指定功能由哪个地址的存储器中的值控制呢?方法很多,最基础,最原始,也是最可靠的方法就是查看芯片的数据手册(datasheet)。

    查看datasheet

    这个方法对于任何的CPU架构的芯片都是通用的。不管是8051、还是Cortex-M3、以及高端的Cortex-A9、A53系列。Zynq 7000系列芯片,对应的手册叫做UG585,安装了Xilinx的文档向导(DocNav)后,就能在文档向导中找到这个文档。文档向导只要安装了vivado,就会自动被安装 ,不用再重新单独安装。在UG585的附录B中,有所有外设的每个寄存器的地址和功能描述。

    使用BSP提供的驱动和硬件信息文件

    在bsp工程中,Xilinx为每一个硬件功能都提供了描述其寄存器地址和位功能的.h文件,这类文件字母x开头,然后紧跟外设功能名,最后以_hw结尾。例如,对于GPIO,提供的该文件名为xgpiops_hw.h,对于串口(uart),提供的该文件名为xuartps_hw.h,对于SD/MMC外设控制器,提供的该文件名为xsdps_hw.h。

    需要注意的是,SDK在生成BSP时,会仅针对系统中配置使能了的硬件生成硬件信息文件,对于没有配置使能的硬件,则可能不会生成硬件信息文件,例如我们开发流程课程中,因为没有使能SD/MMC外设和UART外设,所以在SDK中生成的LED_bsp下就找不到刚刚说的xsdps_hw.h和xuartps_hw.h。

    4、如何实现程序中的延时

    在程序中,少不了要进行延时处理,比如最简单的LED灯闪烁控制,就可以通过延时加翻转LED控制IO的方式来实现LED指定时长的亮灭。对精度要求很低的延时,可以使用死循环的方式实现,例如使用while、for循环等等。对精度要求较高的延时,可以使用BSP中提供的基于CPU心跳定时器的定时/延时函数,例如

    微秒单位延迟:usleep(unsigned long useconds)

    秒单位延迟:sleep(unsigned int seconds)

    这些函数都包含在名为unistd.h的文件中。

    这些延时函数在Xilinx的BSP中,都是根据CPU架构平台,使用CPU系统标配的滴答定时器实现的,所以延时精度相对与程序中写while死循环的方式延迟,要高得多。

    另外,这种延时方式是跨平台通用的,在很多操作系统或者提供裸机bsp的平台上,都支持使用这些函数进行延时,所以,推荐大家在编写c程序时,需要用到延时的地方,优先使用这两个延时函数,而不是使用自己写的while死循环的方式,或者封装的其它延时函数名。

    想要更高精度的延时方法,或者希望在延时期间还能让cpu做点其他事情,就可以使用定时器中断的方式了。使用这种方式,可以让指定的事件在指定的定时时间到来后去执行一次,而在没有到来的情况下,CPU可以处理别的事情。这种方式,后续我们讲解定时器的使用时候会讲到。

    总结下来就是,对于精度要求不高,和对CPU利用率要求不高的场合,使用usleep、sleep函数是非常方便的,而且还能在大多数情况下跨平台移植,非常方便。

    5、使用跨平台可移植的数据类型

    什么是跨平台的数据类型,为什么要使用跨平台的数据类型?先给大家举个简单的例子。

    8位数量类型,Xilinx的SDK提供的例子,使用的都是u8,对应的数据类型实际为unsigned char。使用u8定义一个变量i。就是 u8 i;

    在Xilinx的SDK中这么定义,没问题,只要你的程序中任何一个头文件中包含了"xil_types.h"就能正常使用,比如在LED这个程序中,我们为了使用GPIO的寄存器地址信息,包含了xgpiops_hw.h这个头文件,而在xgpiops_hw.h这个文件中,又包含了"xil_types.h"这个头文件,所以我们在main函数中使用u8\u16\u32\u64\s8\s16\s32\s64来定义变量的类型,才会没有任何问题。但是当我们将这句简单的话放到其他的编译器中,就不行了,例如,我们放到intel的硬核或软核的软件开发环境,也就是Nios II 13.0 Software Build Tools for Eclipse中,就会报错。

    会不会是nios ii eds比较垃圾,没有做这个定义呢,那我们再打开Cypress的FX3(USB3.0芯片)的开发环境,在这个里面我们同样来写这句话,也同样会报错。

    也就是说,除了在Xilinx的SDK中,这个类型在其他很多开发环境中都没有定义。(确实是没有定义,不是我没有找到定义的头文件的位置,是确实没有定义,感兴趣的可以自己下来研究核实)所以当我们在Xilinx的SDK中写的程序移植到其他的开发环境中时,所有的变量定义都会失效,而让我们不得不自己手写该平台的这些变量类型的定义语句(使用typedef)。

    那么有没有一种变量类型是在绝大多数开发环境中通用的呢?确实有,这个就是C99标准新增的标准头文件stdint.h中定义的各个类型。在stdint.h中,定义了各种常见类型的缩写关键词。

    那么,这些类型定义,真的能够做到跨平台通用吗?这个确认是的,不信的话大家可以做个试验,在自己期望测试的开发环境中,写上下面这段代码。

    include “stdint.h”,

    uint8_t us8_type;

    uint16_t us16_type;

    uint32_t us32_type;

    uint64_t us64_type;

    int8_t s8_type;

    int16_t s16_type;

    int32_t s32_type;

    int64_t s64_type;

    然后看看能不能在你的编译环境中找到这些定义。或者修改你可以成功编译的程序里某个变量的类型声明为stdint.h中定义的类型,然后编译看看,看会不会报错。

    基本上可以绝大多数开发环境都能很好的支持这些定义,所以使用stdint.h文件中定义的数据类型来定义变量,就能够实现跨平台便捷移植。推荐大家使用。

    总结

    好了,这节课程讲到这里,还没有讲解如何去点亮一个LED灯,只是介绍了C一些在Xilinx的SDK中编程的常见思路和规律。掌握这些规律,能够让你往具备独立开发能力的开发者的路上迈进,而不是去做一个简单的模仿者。万事俱备,下一节课,我们就来以GPIO为例,仔细看看我们该用怎么的思路和方法对各种硬件外设进行编程控制。

    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2021-2-24 10:16
  • 428

    主题

    811

    帖子

    1万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    16109
    QQ
     楼主| 发表于 2022-1-18 17:56:27 | 显示全部楼层
    本节课程没有对应的附件,所以本楼层仅做占位
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    71

    主题

    100

    帖子

    1525

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    1525
    发表于 2022-1-18 17:59:02 | 显示全部楼层
    c5bf6a99e94489bdb736ae8eb42ea5e5.gif l
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    慵懒
    2021-2-24 10:16
  • 428

    主题

    811

    帖子

    1万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    16109
    QQ
     楼主| 发表于 2022-1-18 17:59:27 | 显示全部楼层

    本楼层作者占位,主要用来统一解答大家学习本节课程过程中遇到的常见问题,后续会再次编辑完善。学习者有疑问的,请在本楼层之后开始提问和讨论。
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    擦汗
    2024-3-26 13:58
  • 3

    主题

    30

    帖子

    319

    积分

    中级会员

    Rank: 4

    积分
    319
    发表于 2022-8-3 17:23:35 | 显示全部楼层
    小梅哥我有一个疑问,学ARM裸机开发 到底是跟着Xilinx提供的例程给它抄一遍理解了全部函数或者一部分,学了寄存器又感觉没用上,不知道这块的学习重点到底是啥 很迷惑
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    慵懒
    2021-2-24 10:16
  • 428

    主题

    811

    帖子

    1万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    16109
    QQ
     楼主| 发表于 2022-8-4 10:15:21 | 显示全部楼层
    CJC 发表于 2022-8-3 17:23
    小梅哥我有一个疑问,学ARM裸机开发 到底是跟着Xilinx提供的例程给它抄一遍理解了全部函数或者一部分,学了 ...

    肯定用的上,库函数太臃肿了,效率不高,在一些对时间要求很高的场合,比如中断服务函数中的内容,你用库函数会让你的中断服务函数响应时间扩大到至少3倍。
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|小黑屋|Archiver|芯路恒电子技术论坛 |鄂ICP备2021003648号

    GMT+8, 2024-11-23 18:09 , Processed in 0.121179 second(s), 37 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc. Template By 【未来科技】【 www.wekei.cn 】

    快速回复 返回顶部 返回列表