NanoPi PWM驱动(Nanopi-S2)

2023-10-11 06:58
文章标签 驱动 pwm s2 nanopi

本文主要是介绍NanoPi PWM驱动(Nanopi-S2),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  • pwm相关手册说明

These timers can be used to generate internal interrupts to the ARM subsystem. In addition, Timers 0, 1, 2, and 3include a PWM function (Pulse Width Modulation) which can drive an external I/O signal. The PWM for timer 0 has a optional dead-zone generator capability, which can be utilized to support a large current device. Timer 4 is only an internal timer with no output pins.

这些定时器在ARM子系统中产生一个内部中断。另外,定时器0、1、2、3包含一个PWM功能,来驱动一个外部I/O信号。PWM定时器0有一个支持死区发生器能力,支持大电流设备,定时器4是一个内部定时器,没有引脚输出。

The Timers are normally clocked off of a divided version of the APB-PCLK. Timers 0 and 1 share a programmable 8-bit prescaler that provides the first level of division for the PCLK. Timer 2, 3, and 4 share a different 8-bit prescaler. Each timer has its own, private clock-divider that provides a second level of clock division (prescaler divided by 2, 4, 8, or 16). Alternatively, the Timers can also select a clock source from an external pin, except Timer 4. Timers 0 and 1 can select the external clock TCLK0. Timers 2 and 3 can select the external clock TCLK1. Timer 4 operates with APB-PCLK only.

定时器通常由APB-PCLK的分频提供时钟。定时器0和1共享一个可编程的8位预分频器,为PCLK提供第一级分频。 定时器2、3和4共享一个不同的8位预分频器。每个定时器都有自己的专用时钟分频器,提供第二级时钟分频(预分频器除以2,4,8或16)。除定时器4外,定时器还可从外部引脚选择一个时钟源。定时器0和1可选择外部时钟TCLK0。 定时器2和3可以选择外部时钟TCLK1。 定时器4仅使用APB-PCLK进行操作。

Each timer has its own 32-bit down-counter which is driven by the timer clock. The down-counter is initially loadedfrom the Timer Count Buffer register (TCNTBn). When the down-counter reaches zero, the timer interrupt requestis generated to inform the CPU that the timer operation is completed. When the timer down-counter reaches zero,the value of corresponding TCNTBn can be automatically reloaded into the down-counter to start the next cycle.However, if the timer stops, for example, by clearing the timer enable bit of TCONn during the timer running mode, the value of TCNTBn is not reloaded into the counter.

每个定时器都有自己的32位递减计数器,由定时器时钟驱动。 递减计数器最初从定时器计数缓冲寄存器(TCNTBn)加载。 当减计数器达到零时,产生定时器中断请求以通知CPU定时器操作完成。 当定时器减计数器达到零时,对应的TCNTBn的值可以自动重新加载到减计数器中以开始下一个循环。然而,如果定时器停止,例如:在定时器运行模式下,清除定时器的使能位TCONn,TCNTBn的值是不能重加载到计数器的。

The Pulse Width Modulation function (PWM) uses the value of the TCMPBn register. The timer control logic changes the output level when the down-counter value matches the value of the compare register in the timer control logic. Therefore, the compare register determines the turn-on time (or turn-off time) of a PWM output.

脉宽调制功能(PWM)使用TCMPBn寄存器的值。 当递减计数器的值与定时器控制逻辑中的比较寄存器的值相匹配时,定时器控制逻辑改变输出电平。 因此,比较寄存器决定PWM输出的导通时间(或关断时间)。

The TCNTBn and TCMPBn registers are double buffered to allow the timer parameters to be updated in the middle of a cycle. The new values will not take effect until the current timer cycle completes.

TCNTBn和TCMPBn寄存器是双缓冲寄存器,允许在一个周期中更新定时器参数。 新值在当前计时器周期完成之后才会生效。

  • 示例
    pwm示例

    1. 初始化 TCNTBn = 160 ,TCMPBn = 110
    2. 通过设置定时器开始位开启定时器,160被装载到TCNTBn中,输出低电平
    3. 当减计数器计数向下计数的值从TCNTBn减到TCMPBn寄存器值为110时,输出变化从低到高
    4. 当减计数器计数向下计数的值为0时,产生中断请求
    5. 同时,减计数器自动重加载TCNTBn,重新开始该循环
  • mmap函数

    作用:将物理内存映射到虚拟内存上,通过对虚拟内存的读写,实现对物理地址的读写

    头文件:<sys/mman.h> <unistd.h>
    函数原型:void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offsize);
    函数返回:成功则返回映射区起始地址, 失败则返回MAP_FAILED(-1)

    参数说明:

    • addr:指定映射的起始地址,通常设为NULL,由系统指定
    • length:将文件的多大长度映射到内存
    • prot:映射区的保护方式,可以是:PROT_EXEC PROT_READ PROT_WRITE PROT_NONE
    • flags:映射区的特性
    • MAP_SHARED:对映射区域的写入数据会复制回文件,且允许其他映射该文件的进程共享
    • MAP_PRIVATE:对映射区域的写入操作会产生一个映射的复制(copy-on-write),对此区域所做的修改不会写回原文件
    • 其他
    • fd:由open返回的文件描述符,代表要映射的文件
    • offset:以文件开始处的偏移量,必须是分页大小的整数倍,通常为0,表示从文件头开始映射
  • 实际操作

    以GPIOD1为例,根据手册GPIOD1为PWM0,需要使能GPIO的 Alternate Function 1功能

  • 示例代码

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>#define GPIOD_BASE_ADDRESS  (0xC001D000)#define GPIODOUT            (*(volatile unsigned int *)GPIOD_BASE_ADDRESS)
#define GPIODOUTENB         (*(volatile unsigned int *)(GPIOD_BASE_ADDRESS + 0x04))
#define GPIODALTFN0         (*(volatile unsigned int *)(GPIOD_BASE_ADDRESS + 0x20))
#define GPIODALTFN1         (*(volatile unsigned int *)(GPIOD_BASE_ADDRESS + 0x24))#define PWM_MAP_SIZE        (0x44 + 0x04)
#define GPIO_MAP_SIZE       (0x24 + 0x04)//PWM与TIMER的基址是不一样的
#define TIMER_BASE_ADDRESS  (0xC0017000)
#define PWM_BASE_ADDRESS    (0xC0018000)#define TIMER_TCFG0         (*(volatile unsigned int *)(PWM_BASE_ADDRESS + 0x00))
#define TIMER_TCFG1         (*(volatile unsigned int *)(PWM_BASE_ADDRESS + 0x04))
#define TIMER_TCON          (*(volatile unsigned int *)(PWM_BASE_ADDRESS + 0x08))
/*Timer0*/
#define TIMER_TCNTB0        (*(volatile unsigned int *)(PWM_BASE_ADDRESS + 0x0C))
#define TIMER_TCMPB0        (*(volatile unsigned int *)(PWM_BASE_ADDRESS + 0x10))
#define TIMER_TCNTO0        (*(volatile unsigned int *)(PWM_BASE_ADDRESS + 0x14))
/*Timer1*/
#define TIMER_TCNTB1        (*(volatile unsigned int *)(PWM_BASE_ADDRESS + 0x18))
#define TIMER_TCMPB1        (*(volatile unsigned int *)(PWM_BASE_ADDRESS + 0x1C))
#define TIMER_TCNTO1        (*(volatile unsigned int *)(PWM_BASE_ADDRESS + 0x20))
/*Timer2*/
#define TIMER_TCNTB2        (*(volatile unsigned int *)(PWM_BASE_ADDRESS + 0x24))
#define TIMER_TCMPB2        (*(volatile unsigned int *)(PWM_BASE_ADDRESS + 0x28))
#define TIMER_TCNTO2        (*(volatile unsigned int *)(PWM_BASE_ADDRESS + 0x2C))
/*Timer3*/
#define TIMER_TCNTB3        (*(volatile unsigned int *)(PWM_BASE_ADDRESS + 0x30))
#define TIMER_TCMPB3        (*(volatile unsigned int *)(PWM_BASE_ADDRESS + 0x34))
#define TIMER_TCNTO3        (*(volatile unsigned int *)(PWM_BASE_ADDRESS + 0x38))
/*Timer4*/
#define TIMER_TCNTB4        (*(volatile unsigned int *)(PWM_BASE_ADDRESS + 0x3C))
#define TIMER_TCNTO4        (*(volatile unsigned int *)(PWM_BASE_ADDRESS + 0x40))
/*Timer interrupt and status*/
#define TIMER_TINT_CSTAT    (*(volatile unsigned int *)(PWM_BASE_ADDRESS + 0x44))#define PWMTIMERCLKENB      (*(volatile unsigned int *)(0xC00C0000 + 0xD000))
#define PWMTIMERCLKGEN0L    (*(volatile unsigned int *)(0xC00C0000 + 0xD004))static int dev_fd = -1;//GPIOD1 PWM0 Alternate Function 1int main(int argc, char **argv)
{dev_fd = open("/dev/mem", O_RDWR | O_NDELAY);if (dev_fd < 0){printf("open(/dev/mem) failed.");return 0;}unsigned int PWMBaseAddr = (unsigned int)mmap(NULL, PWM_MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dev_fd, PWM_BASE_ADDRESS );unsigned int GPIOBaseAddr = (unsigned int)mmap(NULL, GPIO_MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dev_fd, GPIOD_BASE_ADDRESS );PWMTIMERCLKENB |= 1<<2;/*设置定时器时钟*/TIMER_TCFG0  &= ~(0xff<<8);TIMER_TCFG0  |= (20<<8);//变成10MHz的时钟输入Timer234TIMER_TCFG1  &= ~(0xf<<16);TIMER_TCFG1  |= (0x4<<16);//16分频为625KHz,0.0000016s每次TIMER_TCNTB4 = 625;//装填值625,为1ms中断一次TIMER_TINT_CSTAT |= (1<<4);//开timer4中断TIMER_TCON   &= ~(0x7<<20);//清位timer4TIMER_TCON   |= (0x7<<20);//开启timer4,自动重装模式//page 591GPIODOUT |= 1<<1;GPIODOUTENB |= 1<<1;GPIODALTFN0 |= 1<<2;GPIODALTFN0 &= ~(1<<3);if(dev_fd)close(dev_fd);munmap((unsigned int *)PWMBaseAddr,PWM_MAP_SIZE);munmap((unsigned int *)GPIOBaseAddr,GPIO_MAP_SIZE);return 0;
}

备注:时间关系以上代码未测试,可能时钟树的配置有问题

  • 其他方案

    • 通过高精度定时器实现pwm,参考文章,实测发现interval小于5k后,内核会重启,精度不太适合产生pwm

    • sp4418实现使用c++内存映射实现GPIO、Timer、PWM访问控制

这篇关于NanoPi PWM驱动(Nanopi-S2)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/186344

相关文章

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

驱动(RK3588S)第七课时:单节点设备树

目录 需求一、设备树的概念1、设备树的后缀名:2、设备树的语法格式3、设备树的属性(重要)4、设备树格式举例 二、设备树所用函数1、如何在内核层种获取设备树节点:2、从设备树上获取 gpio 口的属性3、获取节点上的属性只针对于字符串属性的4、函数读取 np 结点中的 propname 属性的值,并将读取到的 u32 类型的值保存在 out_value 指向的内存中,函数的返回值表示读取到的

驱动安装注册表指令

HKCR: HKEY_CLASSES_ROOT HKCU: HKEY_CURRENT_USER HKLM: HKEY_LOCAL_MACHINE HKU: HEKY_USER HER: 相对根键

UMDF驱动安装

VS2013 + WDF8.1,UMDF驱动选择User Mode Driver,不要选User Mode Driver 2.0,否则Win7安装有问题,如图 另外,在驱动安装时不要忘记WUDFUpdate_<主版本号><次版本号>.dll文件,具体文件名在INF中查找。此文件可在WDF的安装目录中找到。注意:在WDF的安装目录中会有3个WUDFUpdate_xxx.dll文件,x86,x6

电脑驱动分类

电脑驱动程序(驱动程序)是操作系统与硬件设备之间的桥梁,用于使操作系统能够识别并与硬件设备进行通信。以下是常见的驱动分类: 1. 设备驱动程序 显示驱动程序:控制显卡和显示器的显示功能,负责图形渲染和屏幕显示。 示例:NVIDIA、AMD 显示驱动程序。打印机驱动程序:允许操作系统与打印机通信,控制打印任务。 示例:HP、Canon 打印机驱动程序。声卡驱动程序:管理音频输入和输出,与声卡硬件

麒麟系统安装GPU驱动

1.nvidia 1.1显卡驱动 本机显卡型号:nvidia rtx 3090 1.1.1下载驱动 打开 https://www.nvidia.cn/geforce/drivers/ 也可以直接使用下面这个地址下载 https://www.nvidia.com/download/driverResults.aspx/205464/en-us/ 1.1.3安装驱动 右击,

windows10 卸载网络驱动以及重新安装

右键桌面此电脑的图标,点击管理,设备管理器—网络适配器,找到下图中的驱动(不同的系统或者显卡会导致网卡驱动名称与下图不一样,多为Realtek开头),右键选择卸载设备,然后重启电脑,系统会自动重新安装驱动 新电脑首次安装驱动: 根据主板厂家,比如华硕,进入华硕官网,点击服务支持,点击下载中心,选择型号,点击右侧驱动程序和工具软件,选择windows版本,下载相应的驱动,下载完之后在对应文件中找

笔记整理—内核!启动!—kernel部分(1)驱动与内核的关系

首先,恭喜完成了uboot部分的内容整理,其次补充一点,uboot第一部分和第二部分的工作不是一定的,在不同的版本中,可能这个初始化早一点,那个的又放在了第二部分,版本不同,造成的工作顺序不同,但终归是要完成基本内容初始化并传参给kernel的。         那么至于驱动与内核的关系,用一张图来说明最适合不过:         驱动位于OS层的中下层与硬件相接。驱动是内

读源码笔记--文件过滤驱动FileSpy第1篇 -- DriverEntry

今天只读FileSpy的DriverEntry,位于源文件:filespy.c。 // // 全局变量. // ULONG gFileSpyDebugLevel = DEFAULT_FILESPY_DEBUG_LEVEL; #if WINVER >= 0x0501 ULONG gFileSpyAttachMode = FILESPY_ATTACH_ALL_VOLUMES; #else ULON

STM32CubeMX 3 解锁PWM模块

今天在底层高手的指导下又学习了PWM模块的配置!汪! 首先打开上次配置好的文件,如下: 然后,我们要用TIM1来进行PWM 的设置: 然后,clock configuration 不用动 然后 双击自动弹出以下,窗口: