v3s PWM 使用mmap方式操作PWM

  这几天清明放假回家,感觉不做点事很有罪恶感,为了在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差不多的配置。接下来可以选择写一个好一点的应用层操作库,也可以选择写一个驱动。

原文地址:https://www.cnblogs.com/ZQQH/p/8733510.html