通过对/dev/mem操作寄存器点灯程序
点灯程序如下
soc: zynq7010
该方式通过linux下对 /dev/mem
的操作实现点等程序
有一点是自己之前一直理解错的,就是这些寄存器,是CPU架构内部有程序定义的,CPU的架构也是通过软件实现的,语言用的硬件逻辑语言, 而寄存器也是CPU内部定义的, 然后每个寄存器有对应的数据位宽, 每个位都代表一个功能,向指定位写入0或者1, 使能对应的功能。至于是啥功能,这个就得看寄存器手册了。
这次代码虽然简单,但是让我从CPU架构到FPGA,在到硬件,软件linux架构有了更深层次的认知。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
// 查看寄存器手册 ug585-Zynq-7000-TRM.pdf
#define GPIO_ADDR_BASE 0xE000A000
#define GPIO_DATA1 0xE000A044 //第几个引脚
#define GPIO_DIRECTION 0xE000A204 //控制IO脚是输出还是输入, 0 input 1 output
#define GPIO_CTRLDIREC 0xE000A208 //当引脚用于输出时候, 该寄存器是否启用输出 0 disable,1 enabled
#define GPIO_INTERGPIO 0xE000A214 //用于取消或者屏蔽GPIO中断输入, 0 nochange 1 clear interrupt mask
#define APER_CLK_CTRL 0xF8000000 //时钟控制的起始地址,如果确定地址去偏移不行, 只能偏移相对起始地址,需要看手册
#define APER_CLK_CTRL1 0xF800012c //控制GPIO时钟地址, 如果该时钟没有控制上,相关GPIO值只能读到0
void led_gpio() {
int fd = open("/dev/mem", O_RDWR | O_NOCTTY);
if(fd < 0) {
printf("error open /dev/mem; fd: %d \n", fd);
return;
}
int gpio = 7;
unsigned int * addr_aper = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, APER_CLK_CTRL);
unsigned int * addr_aper1 = addr_aper + 75;
int aper1_val = addr_aper1[0];
addr_aper1[0] = aper1_val | (0x1U << 22); //控制GPIO时钟使能的,只有给上这个信号,才能读出默认数据,不然一直读到的是0
aper1_val = addr_aper1[0];
printf("aper1_val %p, addr_aper: %p \n", aper1_val, addr_aper);
unsigned int * addr_base = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO_ADDR_BASE);
// 0x40 data0。 需要对长度 * 4,就是地址 0xE000A044, 以下同样,
// mmap是不能够对地址 0xE000A044 直接映射,只能对起始地址进行映射 0xE000A000
unsigned int * addr_data = addr_base + 16;
unsigned int * addr_dire = addr_base + 129; //0x204
unsigned int * addr_ctrl = addr_base + 130; //0x208
unsigned int * addr_inte = addr_base + 133; //0x214
printf("addr_base %p \n", addr_base);
int val1 = addr_data[0];
int val2 = addr_dire[0];
int val3 = addr_ctrl[0];
int val4 = addr_inte[0];
// addr_data[0] = val1 & ~(0x1U << 7);
addr_inte[0] = val4 | (0x1U << 7); //清除GPIO中断功能
addr_dire[0] = val2 | (0x1U << 7); //设置IO输出
addr_ctrl[0] = val3 | (0x1U << 7); //启用输出
addr_data[0] = val1 | (0x1U << 7); //引脚高电平, 默认将灯点亮
// 控制灯闪烁
int times = 0;
while(1) {
if(times++%2 == 0) {
addr_data[0] = val1 | (0x1U << 7); //引脚高电平
}else {
addr_data[0] = val1 & ~(0x1U << 7); //引脚高电平
}
printf("=> addr_data: %0x, times %d \n", addr_data[0], times);
sleep(1);
}
// int cnt = 0;
// for(cnt = 0; cnt < 150; cnt++) {
// if(cnt % 10 == 0) {
// printf("\n");
// }
// printf("%08x ", addr_base[cnt]);
// }
// printf("\n");
}
int main(int argc, char const *argv[])
{
led_gpio();
return 0;
}
总结
- 对/dev/mem 可以操作到所有存在的外设, 只要是通过寄存器能控制的都能通过该方式操作,包括串口,SPI等。和裸机开发一样操作寄存器即可
- 该方式不需要设备树的支持,设备树主要是为驱动提供的一种初始参数供设备树例化程序,操作寄存器
本文来自博客园踩坑狭,作者:韩若明瞳,转载请注明原文链接:https://www.cnblogs.com/han-guang-xue/p/17207494.html