REVIEW
上次学习了一下软件使用流程zynq PS点灯-CSDN博客
本次学习一下C编程基础与调试方法
1. 硬件编程原理
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 小梅哥视频链接: 07_Xilinx嵌入式裸机硬件编程原理_哔哩哔哩_bilibili 对应的课程笔记:【zynq课程笔记】【裸机】【第7课 】【硬件编程原理】 - ACZ702开发板 - 芯路恒电子技术论坛 - Powered by Discuz! (corecourse.cn) |
| ### ①典型GPIO结构 |
| |
| R1:方向/输出使能控制寄存器 R2:输出状态/数据寄存器 R3:输入状态/数据寄存器 |
| ### ②Zynq7000 GPIO结构 zynq PS端 GPIO-CSDN博客 |
| |
| ### ③GPIO控制的编程思路 |
| 初始化 根据GPIO对应位的工作场景,设置其方向、中断屏蔽位、中断检测类型。 工作 输入:若开启了中断,则编写中断处理函数; 若不开启中断,则在需要的时候直接读取输入寄存器的值。 输出:则通过写数据寄存器或输出置位/清零寄存器来修改该位的输出值。 |
2. 寄存器操作
|----------------------------------------------------------------|---------------------------------------------------------------------------------------------|
| 小梅哥视频:06_Zynq SoC嵌入式逻辑C编程基础_哔哩哔哩_bilibili 课程笔记:【zynq课程笔记】【裸机】【第6课 】【C编程基础】 - ACZ702开发板 - 芯路恒电子技术论坛 - Powered by Discuz! (corecourse.cn) ||
| cpu编程,本质上就是对指定地址进行读写操作 ||
| 视频课程用到的代码:实现让开发板上的PS_LED0亮灭 端口为MIO7 ||
| 场景分析:该场景下只需要简单的控制GPIO的对应位输出高低电平即可,用不到中断功能。 初始化 关闭中断 INT_DIS/ INT_EN: 这是一组作用于同一个功能的2个独立的寄存器,一个负责使能GPIO的每一位的中断,另一个负责禁止GPIO的每一位的中断。 本应用中,对应GPIO无需开启中断,所以设计时针对INT_DIS/ INT_EN寄存器,需要明确: 关闭某位中断该操作哪个寄存器,往该寄存器写0关闭中断,还是写1关闭中断。 根据UG585中的描述,对INT_DIS寄存器的对应位写1就可以禁止该位对应的IO产生中断。所以本例中初始化时,理论上需要对INT_DIS寄存器进行操作。设置MIO7对应的中断控制位不打开。 Data = (1<<7); Xil_Out32(XPAR_PS7_GPIO_0_BASEADDR + XGPIOPS_INTDIS_OFFSET, Data); ||
| #include "stdio.h" #include "xparameters.h" #include "xil_io.h" #include "xgpiops.h" #include "sleep.h" int main(void) { u32 reg_val = 0; u32 Data = 0; //设置方向和输出使能 //OUTEN、DIRM寄存器 //设置IO方向,bit7的方向为输出 reg_val = Xil_In32(XPAR_PS7_GPIO_0_BASEADDR + XGPIOPS_DIRM_OFFSET); Data = reg_val | (1<<7); Xil_Out32(XPAR_PS7_GPIO_0_BASEADDR + XGPIOPS_DIRM_OFFSET , Data); //设置输出使能,bit7输出使能 reg_val = Xil_In32(XPAR_PS7_GPIO_0_BASEADDR + XGPIOPS_OUTEN_OFFSET); Data = reg_val | (1<<7); Xil_Out32(XPAR_PS7_GPIO_0_BASEADDR + XGPIOPS_OUTEN_OFFSET, Data); ||
| //设置方向和输出使能 //OUTEN、DIRM寄存器 由于是点亮LED,属于输出型GPIO,因此,根据GPIO的结构图知道,需要使output enable信号为高电平,以使能IO Pin上的三态缓冲器输出。 而output enable为1的条件则是OUTEN和DIRM两个寄存器对应的位都为1。 由于这两个寄存器均是32位同时写入型,所以为了不干扰寄存器中其他位的值, 需要采用read-modify-write的操作顺序,也就是先读出,再修改,最后再写回。 ||
| while(1) { //MASK_DATA寄存器 //设置bit7输出1 Data = ((~(1<<7)) << 16) | (1<<7); Xil_Out32(XPAR_PS7_GPIO_0_BASEADDR + XGPIOPS_DATA_LSW_OFFSET, Data); usleep(1000000);//延时1000000us //设置bit7输出0 Data = ((~(1<<7)) << 16) & (~(1<<7)); Xil_Out32(XPAR_PS7_GPIO_0_BASEADDR + XGPIOPS_DATA_LSW_OFFSET, Data); sleep(1);//延时1000000us } return 0; } ||
| //MASK_DATA寄存器 写MASK_DATA寄存器时需要注意,对于每一个GPIO Bank,由MASK_DATA_LSW和MASK_DATA_MSW两个寄存器组成,其中MASK_DATA_LSW控制该组GPIO中低16位的状态,MASK_DATA_MSW控制该组GPIO中高16位的状态。 MASK_DATA的高16位中,哪些位为0,这些位对应的数据寄存器的值才允许被更新,更新的值由MASK_DATA寄存器的低16位来指定。 ||
| ### ① bsp(board support package)板级支持包 ||
| ||
| 用户编程时,可以使用该支持包中提供的驱动和函数,来避免自己编写应用函数和基于寄存器读写的驱动。 由于这些驱动程序中加了很多安全判断和兼容操作,所以,在对程序尺寸和运行效率要求不高的场合,推荐使用BSP提供的驱动和函数,而在对性能和程序尺寸有要求的场合,推荐自己编写基于寄存器读写的驱动。 //对与本摸鱼怪来说,这一点后面需要的话,再肥来恶补知识叭~ ||
| ### ② 如何实现对指定地址的读写操作 ||
| #### 使用指针 ||
| 例如对地址为0x00000020的寄存器进行读写,就可以使用下面的形式: 读寄存器:return (volatile u8) 0x00000020; 写寄存器:(volatile u8) 0x00000020 = 0x12; ||
| #### 使用IO读写函数 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); |
| 这些函数就是对指针操作的封装 ||
| ### ③ 如何知道各个外设的硬件信息(寄存器地址,位功能) ||
| #### 查看datasheet ||
| 在UG585的附录B中,有所有外设的每个寄存器的地址和功能描述。 ||
| ||
| ||
| ||
| reg_val = Xil_In32(XPAR_PS7_GPIO_0_BASEADDR + XGPIOPS_DIRM_OFFSET); XPAR_PS7_GPIO_0_BASEADDR 为基地址 XGPIOPS_DIRM_OFFSET 为偏移地址 ||
| #### 使用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。 ||
| ### ④如何实现程序中的延时 ||
| 对精度要求较高的延时,可以使用BSP中提供的基于CPU心跳定时器的定时/延时函数,例如 微秒单位延迟 :usleep(unsigned long useconds) 秒单位延迟 :sleep(unsigned int seconds) ||
| ### ⑤使用跨平台可移植的数据类型 ||
| 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; ||
3. 使用硬件库进行编程
|----------------------------|-----------------|
| 小梅哥视频链接: 08_基于SDK硬件驱动库的编程方法_哔哩哔哩_bilibili 对应的课程笔记:【zynq课程笔记】【裸机】【第8课 】【使用SDK硬件驱动库】 - ACZ702开发板 - 芯路恒电子技术论坛 - Powered by Discuz! (corecourse.cn) ||
| #include "xgpiops.h" #include "unistd.h" XGpioPs Gpio; XGpioPs_Config *ConfigPtr; int main(void) { ConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID); XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr); XGpioPs_SetDirectionPin(&Gpio, 7, 1); XGpioPs_SetOutputEnablePin(&Gpio, 7, 1); while(1) { //设置bit7输出1 XGpioPs_WritePin(&Gpio, 7, 0x1); usleep(500000); //设置bit7输出0 XGpioPs_WritePin(&Gpio, 7, 0x0); usleep(500000); } return 0; } ||
| XGpioPs_LookupConfig | 寻找指定GPIO设备的配置信息 |
| XGpioPs_CfgInitialize | 对GPIO的驱动程序进行初始化 |
| XGpioPs_SetDirectionPin | 设置指定Pin的方向 |
| XGpioPs_SetOutputEnablePin | 设置指定Pin的输出使能 |
| XGpioPs_WritePin | 写/更新指定管脚的值/状态 |
4.调试
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 小梅哥视频: 09_Zynq SoC ARM裸机程序调试方法_哔哩哔哩_bilibili 对应课程笔记:【zynq课程笔记】【裸机】【第9课 】【裸机程序调试方法】 - ACZ702开发板 - 芯路恒电子技术论坛 - Powered by Discuz! (corecourse.cn) |
| |
| |
5. 小作业
|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ### ①GPIO_MIO PS按键控制PS_LED闪烁 |
| |
| #include "COMMON.h" #include "sleep.h" int main(void) { u8 State_PS_KEY; //存放按键(MIO7)的电平状态,0 为低电平,1 为高电平 PS_GPIO_Init(); //初始化 PS 端 MIO 和 EMIO //设置 PS_LED(MIO7)为输出并且初始为低电平 PS_GPIO_SetMode(PS_LED, OUTPUT, 0); PS_GPIO_SetMode(PS_KEY, INPUT, 0); //设置 PS_KEY(MIO47)方向为输入 while(1) { //读取 PS_KEY 的电平值并存储到 State_PS_KEY 变量里 // State_PS_KEY = PS_GPIO_GetPort(PS_KEY); while(!PS_GPIO_GetPort(PS_KEY)) { //将 State 变量的值取非赋予 PS_LED 来输出 PS_GPIO_SetPort(PS_LED,1); usleep(500000); PS_GPIO_SetPort(PS_LED,0); usleep(500000); } PS_GPIO_SetPort(PS_LED,0); } return 0; } |
| while(!PS_GPIO_GetPort(PS_KEY)) 嘎嘎~这个是调试出来的小问题: while(State_PS_KEY ) { //将 State 变量的值取非赋予 PS_LED 来输出 PS_GPIO_SetPort(PS_LED,1); usleep(500000); PS_GPIO_SetPort(PS_LED,0); usleep(500000); State_PS_KEY = PS_GPIO_GetPort(PS_KEY); } 这个小问题,自己调试一下就可以发现啦~ 好久没写C,还是要好好熟练一下~ |
| 或者是: |
| #include "xgpiops.h" #include "unistd.h" XGpioPs Gpio; XGpioPs_Config *ConfigPtr; int main(void) { ConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID); XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr); //设置MIO7为输出 XGpioPs_SetDirectionPin(&Gpio, 7, 1); XGpioPs_SetOutputEnablePin(&Gpio, 7, 1); //设置MIO47为输入 XGpioPs_SetDirectionPin(&Gpio, 47, 0); XGpioPs_SetOutputEnablePin(&Gpio, 47, 0); while(1) { while(!XGpioPs_ReadPin(&Gpio, 47)) { //设置bit7输出1 XGpioPs_WritePin(&Gpio, 7, 0x1); usleep(500000); //设置bit7输出0 XGpioPs_WritePin(&Gpio, 7, 0x0); usleep(500000); } //设置bit7输出0 XGpioPs_WritePin(&Gpio, 7, 0x0); } return 0; } |
| ### ②GPIO_EMIO PL按键控制PS_LED、PL_LED交替闪烁 |
| #include "xgpiops.h" #include "unistd.h" XGpioPs Gpio; XGpioPs_Config *ConfigPtr; int main(void) { ConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID); XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr); /*设置MIO7为输出*/ XGpioPs_SetDirectionPin(&Gpio, 7, 1); XGpioPs_SetOutputEnablePin(&Gpio, 7, 1); /*设置EMIO0为输出,MIO有54个,EMIO0=54+0=54*/ XGpioPs_SetDirectionPin(&Gpio, 54, 1); XGpioPs_SetOutputEnablePin(&Gpio, 54, 1); /*设置EMIO1为输入*/ XGpioPs_SetDirectionPin(&Gpio, 55, 0); XGpioPs_SetOutputEnablePin(&Gpio, 55, 0); while(1) { while(!XGpioPs_ReadPin(&Gpio, 55)) { //设置bit54输出1 XGpioPs_WritePin(&Gpio, 54, 0x1); XGpioPs_WritePin(&Gpio, 7, 0x0); usleep(500000); //设置bit54输出0 XGpioPs_WritePin(&Gpio, 54, 0x0); XGpioPs_WritePin(&Gpio, 7, 0x1); usleep(500000); } //设置bit54输出0 XGpioPs_WritePin(&Gpio, 54, 0x0); XGpioPs_WritePin(&Gpio, 7, 0x0); } return 0; } |
| //啦啦啦,摸鱼结束~ |