• 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差不多的配置。接下来可以选择写一个好一点的应用层操作库,也可以选择写一个驱动。

  • 相关阅读:
    SQL经典语句和要点整理
    XMLHTTPRequest状态status完整列表
    console和windows子系统
    QT的文件查找
    QT的编译原理
    AES加密算法
    多线程基础
    0210. Course Schedule II (M)
    ip段/数字,如192.168.0.1/24的意思是什么?
    Excel如何让日期单元格随着某个单元格的修改而自动更新
  • 原文地址:https://www.cnblogs.com/ZQQH/p/8733510.html
Copyright © 2020-2023  润新知