这几天清明放假回家,感觉不做点事很有罪恶感,为了在V3S上实现PWM驱动,首先我要先知道PWM的寄存器使用方法。所以就写了这个测试程序。
1.思路
(1).首先映射寄存器。查看了V3S的datasheet,发现这个芯片的PWM输出不需要通过定时器就可以实现,
这个还是比较好的。所以我需要映射一个GPIO寄存器以及一个PWM寄存器。
(2).映射好了寄存器之后
/*
* PWM波配置顺序
* 1.GPIO 配置PWM输出模式
* 2.PWM 配置预分頻
* 3.PWM 配置总周期
* 4.PWM 配置活跃周期
* 5.PWM 使能
*/
2.代码
pwm_test.c
#include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <unistd.h> #include <sys/mman.h> #include <fcntl.h> #include <string.h> #define PIO_BASE_ADDR 0x01C20000 #define PIO_ADDR_OFF 0x800 #define PWM_ADDR_OFF 0x1400 #define PIO_CFG_OFF 0x24 #define PIO_DAT_OFF 0x34 #define PWM_CH0_OFF 0x04 #define Page_Size (4096 * 2) uint32_t *base_map = NULL; uint32_t *gpio_map = NULL; uint32_t *gpio_cfg = NULL; uint32_t *gpio_dat = NULL; uint32_t *pwm_base_map = NULL; uint32_t *pwm0_period = NULL; int main(void) { int mem_fd; int duty=1000; if ((mem_fd = open("/dev/mem", O_RDWR)) < 0) { printf("open error "); exit(-1); } //mmap(系统自动分配内存地址,映射区长度“内存页的整数倍”,选择可读可写,MAP_SHARED=与其他所有映射到这个对象的进程共享空间,文件句柄,被映射内容的起点) //offest 映射物理内存的话,必须页对其!!! 所以这个起始地址应该是0x1000的整数倍,那么明显0x01C20800需要减去0x800才是整数倍! if ((base_map = (uint32_t *)mmap(NULL, Page_Size, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, PIO_BASE_ADDR)) == NULL) { printf("mmap error "); close(mem_fd); exit(-1); } printf("base_map:0x%.8X ", (uint32_t)base_map); close(mem_fd); //映射好之后就可以关闭文件? //这里已经将0x1c20000的地址映射到了内存中,但是我们需要的地址是0x01C20800,所以要再加上地址偏移量~ gpio_map = (uint32_t)base_map + PIO_ADDR_OFF; //加上偏移量必选先把他转化成unsigend int才可以相加 printf("gpio_map:0x%.8X ", (uint32_t)gpio_map); gpio_cfg = (uint32_t)gpio_map + PIO_CFG_OFF; //gpioB控制寄存器地址 printf("gpio_cfg:0x%.8X ", (uint32_t)gpio_cfg); gpio_dat = (uint32_t)gpio_map + PIO_DAT_OFF; //gpioB数据寄存器地址 printf("gpio_dat:0x%.8X ", (uint32_t)gpio_dat); /*PB4点灯*/ (*gpio_cfg) &= ~((unsigned int)7 << 16); //先将对应位置0 (*gpio_cfg) |= ((unsigned int)1 << 16); //PB5 设定out (*gpio_dat) &= ~(1 << 4); //开灯 sleep(3); /** * PWM波配置顺序 * 1.GPIO 配置PWM输出模式 * 2.PWM 预分頻 * 3.PWM 总周期 * 4.PWM 活跃周期 * 5.PWM 使能 */ //PB4设定PWM输出 (*gpio_cfg) &= ~((unsigned int)7 << 16); (*gpio_cfg) |= ((unsigned int)2 << 16); //PB4 设定PWM0模式 //我们需要的地址是0x01C21400,所以要再加上地址偏移量 pwm_base_map = (uint32_t)base_map + PWM_ADDR_OFF; //pwm_base_map也是pwm控制寄存器 初始值是0x00000000 printf("pwm_base_map:0x%.8X ", (uint32_t)pwm_base_map); /*首先设置PWM0 预分頻*/ //PWM_CH0_PRESCAL (*pwm_base_map) &= ~((uint32_t)15 << 0); //先将0~3位置0 (*pwm_base_map) |= (uint32_t)8 << 0; //将0~3位设置为 1000 ---> 对应分頻12k-->2K Hz /*可能要设置SCLK_CH0_GATING为mask*/ (*pwm_base_map) &= ~((uint32_t)1 << 6); //先将第6位置0 (*pwm_base_map) |= (uint32_t)1 << 6; //将第6位置1 ---> 设置为自定义预分頻系数 /** * 具体的总周期时间的作用需要进一步测试 * 注意:要活动周期设置好之后使能PWM通道 * 这样才会有正确输出,并且之后直接修改寄存器的值就可以修改占空比。 * */ /*再设置pwm0占空比*/ pwm0_period = (uint32_t)pwm_base_map + PWM_CH0_OFF; //pwm0_period设置pwm_ch0的占空比寄存器 printf("pwm0_period:0x%.8X ", (uint32_t)pwm0_period); /*先设置总周期*/ //PWM周期的计算应该是这样 OSC 24MHz / Pre-scalar / (entire cycles + 1) (*pwm0_period) &= ~((uint32_t)65535 << 16); //将31~16位置零 //24Mhz / 12K / (1999+1) =1s (*pwm0_period) |= (uint32_t)1999 << 16; // 现在设置整个周期65535 /*再设置活跃周期 活跃周期要小于总周期*/ (*pwm0_period) &= ~((uint32_t)65535 << 0); //将15~0位置零 (*pwm0_period) |= (uint32_t)duty << 0; //将15~0位置为2500 现在设置活跃周期为35535 /*最后应该设置PWM_CH0_EN为enable*/ (*pwm_base_map) &= ~((uint32_t)1 << 4); //先将第4位置0 ---> disable (*pwm_base_map) |= (uint32_t)1 << 4; //将第4位置1 ---> enable PWM0 printf("PWM ENABLE DUTY:%d ",duty); sleep(5); duty=1800; /*再设置活跃周期 活跃周期要小于总周期*/ (*pwm0_period) &= ~((uint32_t)65535 << 0); //将15~0位置零 (*pwm0_period) |= (uint32_t)duty << 0; //将15~0位置为2500 现在设置活跃周期为10000 printf("PWM ENABLE DUTY:%d ",duty); sleep(5); munmap(base_map, Page_Size); printf("munmap success! "); return 0; }
Makefile
TARGET = pwm_test #可执行文件名称 ########################编译参数############################ CC = arm-linux-gnueabihf-gcc CXX = arm-linux-gnueabihf-g++ DEFINES = CFLAGS = -pipe -g -Wall -W -fPIE $(DEFINES) CXXFLAGS = -pipe -g -Wall -W -fPIE $(DEFINES) INCPATH = -I. PWD =$(shell pwd) #当前路径 FILE_PWD=$(strip $(PWD)) LICHEDIR := /root/ ########################编译文件############################ SOURCES = ./pwm_test.c OBJECTS = pwm_test.o $(TARGET) : $(OBJECTS) $(CC) -o $(TARGET) $(OBJECTS) pwm_test.o : pwm_test.c $(CC) $(include) $(CFLAGS) -c pwm_test.c .PHONY : clean clean : rm $(OBJECTS) $(TARGET) install: scp $(FILE_PWD)/$(TARGET) root@172.24.41.12:$(LICHEDIR)
3.实验结果与总结
编译,拷贝到板子里面运行之后感觉到闪烁频率为1s。并且占空比可控。
我上面操作寄存器的,大家有兴趣的可以看看那V3S的datasheet。
这里这个pre-scalar位 以及 period位。经过我的实践,发现这个应该和stm32差不多。
都是 总频率/预分频/(总周期+1)。我这里设置的是 24MHz÷12K÷(1999+1)= 1Hz
这个芯片应该和全志H2、H3差不多的配置。接下来可以选择写一个好一点的应用层操作库,也可以选择写一个驱动。