Linux实现方波输出和PWM技术 (linux 方波输出pmw)

随着人们对数字化和自动化需求的增加,嵌入式系统在市场上的应用越来越广泛,而Linux系统作为软件开发非常成熟的一个操作系统,被广泛应用于嵌入式系统中。在嵌入式系统开发中,有许多应用需要使用到PWM技术或者方波输出,本文将会详细讲解如何在Linux系统中实现方波输出和PWM技术。

一、方波输出

方波信号是一种具有特定周期和占空比的信号,一般用于嵌入式系统中的定时器、DAC转换、蜂鸣器等场合,现在我们将借助Linux系统实现方波输出。

1.1 硬件搭建

方波输出的硬件搭建如图1所示,需要一个单片机作为信号发生器和一个示波器进行观察。

![图1](https://img-blog.csdn.net/20230331210056508?watermark/2/text/aHR0cDovL2J2cuY3Nkbi5uZXQvbG9naW5fZ3VpZGUx/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/q/75)

1.2 打开XT2时钟

在CCS(Code Composer Studio)中,多数情况需要用到内部低频晶振(LFXT1),然而现在我们没用到它,而使用外部高频晶振(XT2)。需要进行如下配置:

(1)flash主程序分配区域

在 startup_ccs/hw_memmap.h 文件中添加如下宏定义:

#define HW_NMI (0xFFFEu)

将NMI vector定义设置在微控制器的外部RAM中。

(2)时钟配置

在CPU主频为16MHz(CLK)和数量级于4~20MHz的外部时钟的设定下,格式为:

#include

UCSCTL3 |= SELREF_2; // Set DCO FLL reference = XT2 = 16MHz

UCSCTL4 |= SELA_2; // Set ACLK = XT2 = 16MHz

UCSCTL0 |= UCSON; // Enable UCS subsystem

UCSCTL4 |= SELS_4 | SELM_4; // CLK=MCLK=XT2

此处,UCSCTL3是内部系统时钟,表示将此时钟配置为使用XT2作为DCO FLL参考时钟,UCSCTL4是时钟门控寄存器,SELREF_2表示使用XT2作为DCO FLL参考时钟,SELA_2表示设置ACLK时钟源为XT2,SELS_4和SELM_4表示将时钟源分别设置为CLK和MCLK。

1.3 实现方波读写操作

(1)打开输出口

P8SEL |= BIT0;

这里P8SEL和SEL和dir是三个位于io.h头文件中的宏定义。P8SEL代表P8口,SEL口和DIR口分别用于配置端口是输入还是输出,这里我们将P8口的P8.0位于SEL高阻态。

(2)关闭滤波器

/*

* Regarding Digital IOs’ filtering, if a I/O line,

* primary or secondary function, is expected to experience

* a sharp rising or falling edge, and that we want to

* capture that signal, one would have to disable the

* digital filter associated to the I/O line using the

* digital IO disable register DIO#_FSEL.

*

* DIO#_FSEL &= ~bitfield;

*

* DIO# is the name of the Digital IO and bitfield is the

* bitfield associated to that Digital IO.

* __even_in_range is a macro that dynamically compares the

* user defined number with the base of the register.

*

* We’ll disable all digital filters for this example.

*/

P8DIR |= BIT0;

P8DS |= BIT0;

P8OUT &= ~BIT0;

P8SEL &= ~BIT0;

P8REN &= ~BIT0;

P8SEL |= BIT0;

这里需要将滤波器概念介绍一下。数字IO端口在信号输入时,当解析器的输出跳变时,端口会使用一个低频率振荡电路(低通滤波器)将输入信号进行滤波。滤波器按照设备的预定阈值设定为一个可滤波的更大上升沿时延(通常在几微秒到几百微秒之间),过滤掉了较慢的信号干扰。当端口输出时,需要将端口滤波器关掉,否则会影响输出。通过配置P8口的P8.0相应的FSEL控制寄存器,可以实现关闭数字滤波器。

(3)实现方波输出

while (1) { // Loop forever

period = 20; // 20ms period

pulsewidth = period / 2; // Time the signal stays high

TA0CCR0 = TA1CCR0 = period * 1000 / 25; // Counter for up/down mode

TA0CCR1 = TA1CCR1 = pulsewidth * 1000 / 25; // Time when output is high

TA0CCTL1 = TA1CCTL1 = OUTMOD_7; // Set output mode to toggle

TA0CTL = TA1CTL = TASSEL__XT2 | MC__UPDOWN | TACLR; // Set upcounter & clear timer

while (1){}; // Let period edge interrupt handle next pulse

}

在这段代码中,我们通过配置TA0和TA1两个定时器,实现了单片机生成一段特定占空比的方波信号。这里的定时器是通用定时器(Timer_A),是单片机中常用的高级定时器。

TA0CCR0是计数器阈值,TA0CCR1是比较器阈值,TA0CCTL1是比较器控制器,OUTMOD_7表示设置输出模式为“比较输出模式7”(即:除计数器为0时置位外,其他情况下,比较器寄存器与计数器寄存器相等则翻转信号,否则不翻转)。TASSEL_2是选择TA的时钟源为XT2,MC_UPDOWN是计数模式设为向上向下计数模式,TACLR是允许清除TA计时器计数器。

1.4 完整代码

下面是生成方波信号的完整代码:

#include

int mn (void) {

WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer

P8SEL |= BIT0; // Set P8.0 for secondary peripheral module function (GPIO output)

P8DIR |= BIT0; // Configure P8.0 as output

P8DS |= BIT0; // Connect P8.0 to I/O pad

P8OUT &= ~BIT0; // Set initial output to 0

P8SEL &= ~BIT0; // Disconnect P8.0 from NMI/nRST

P8REN &= ~BIT0; // Disable internal pullup resistor

P8SEL |= BIT0; // Select peripheral module function (in this case, TACLK)

while (1) { // Loop forever

period = 20; // 20ms period

pulsewidth = period / 2; // Time the signal stays high

TA0CCR0 = TA1CCR0 = period * 1000 / 25; // Counter for up/down mode

TA0CCR1 = TA1CCR1 = pulsewidth * 1000 / 25; // Time when output is high

TA0CCTL1 = TA1CCTL1 = OUTMOD_7; // Set output mode to toggle

TA0CTL = TA1CTL = TASSEL__XT2 | MC__UPDOWN | TACLR; // Set upcounter & clear timer

while (1){}; // Let period edge interrupt handle next pulse

}

二、PWM技术

PWM技术(Pulse-width modulation)又称脉宽调制技术,是一种通过调节周期相等的脉冲宽度来控制输出电压或电流的技术,一般应用于马达控制、LED的亮度控制等场合。下面我们将介绍在Linux系统中如何实现PWM技术。

2.1 硬件搭建

PWM技术的硬件搭建如图2所示,需要一个单片机作为信号发生器和一个示波器进行观察。

![图2](https://img-blog.csdn.net/20230331211053518?watermark/2/text/aHR0cDovL2J2cuY3Nkbi5uZXQvbG9naW5fZ3VpZGUx/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/q/75)

2.2 驱动器

PWM技术的es0风驱动器通过将电源转换为PWM信号来控制电机的转速。驱动器是由一个控制器、一个驱动芯片和一个三相桥组成的。控制器负责控制电机的电流,驱动芯片ON/OFF是在这个信号的控制下进行的。使用PWM技术,我们可以调整PWM信号的峰值来控制电机的输出功率。

2.3 PWM方式实现

在Linux系统中,实现PWM方式有两种:软件PWM和硬件PWM。软件PWM的优点是实现简单,但精度有限,很难实现更高分辨率的PWM波形。硬件PWM技术通过直接控制MCU的输出端口来生成高分辨率的PWM波形,但由于接口的限制,硬件PWM技术的灵活性比软件PWM要低。

下面我们将介绍如何在Linux系统中通过硬件PWM技术来实现控制一个3相电机的转速。

(1)配置PWM输出输出引脚

我们需要向设备树引擎添加定时器、PWM驱动程序和节点,以实现PWM控制。

给xilinx,dma节点添加定时器属性,将PWM信号输出到zynq (ps)端口。定时器只需要设定时钟源和计数器周期即可。

pwm-leds {

compatible = “pwm-leds”;

led0 {

pwms = ;

pwn-period = ;

line-names = “pwm0”;

default-brightness-level = ;

};

};

&pwm0 {

status = “okay”;

pwn-period = ; /* 100Hz with internal clock */

ti,shoot-yes;

};

配置完毕后,我们可以读取PWM周期和占空比并将其写入设备文件,以控制电机的速度。

(2)控制电机的速度

控制电机的转速是通过调整PWM信号的频率和占空比来实现的。具体来说,我们可以通过增加PWM信号的频率来加快电机的转速,同时通过增加PWM信号的占空比来提高其输出功率。

下面是代码,用于通过PWM技术驱动一个3相电机:

/* pwm_leds.c */

#include

#include

#include

#include

#include

#include

#include

#include

#define DEVICE_NAME “pwm-leds”

static struct pwm_device *pwm_dev;

static struct device *dev;

static void pwm_leds_set_brightness_level(unsigned int level)

{

printk(KERN_INFO “pwm_leds: Set brightness level to %u\n”, level);

/* Level should be in the range of [0, pwn-period] */

level = min_t(unsigned int, pwm_get_period(pwm_dev), level);

/* Set PWM duty cycle */

pwm_config(pwm_dev, level, pwm_get_period(pwm_dev));

/* Enable the PWM signal */

pwm_enable(pwm_dev);

/* Wt a short while for the new settings to take effect */

msleep(50);

}

/* Device attribute callbacks */

static ssize_t brightness_level_show(struct device *dev,

struct device_attribute *attr, char *buf)

{

return scnprintf(buf, PAGE_SIZE, “%u\n”,

pwm_get_duty_cycle(pwm_dev));

}

static ssize_t brightness_level_store(struct device *dev,

struct device_attribute *attr, const char *buf,

size_t count)

{

int value;

int ret;

ret = kstrtoint(buf, 10, &value);

if (ret

return ret;

if ((value pwm_get_period(pwm_dev)))

return -EINVAL;

pwm_leds_set_brightness_level(value);

return count;

}

/* Device attributes */

static DEVICE_ATTR(brightness_level, S_IWUSR|S_IRUGO,

brightness_level_show, brightness_level_store);

/* Platform device driver */

static int pwm_leds_probe(struct platform_device *pdev)

{

struct device_node *np = pdev->dev.of_node;

int err;

if (!np)

return -ENODEV;

pwm_dev = devm_pwm_get(&pdev->dev, NULL);

if (IS_ERR(pwm_dev))

return PTR_ERR(pwm_dev);

/* Initialize device attributes */

err = device_create_file(&pdev->dev, &dev_attr_brightness_level);

if (err)

goto fl;

/* Create a new device node */

dev = device_create(pc_class, NULL, 0, NULL, DEVICE_NAME);

if (IS_ERR(dev)) {

err = PTR_ERR(dev);

goto fl;

}

dev_set_drvdata(dev, pwm_dev);

return 0;

fl:

if (&dev_attr_brightness_level.attr)

device_remove_file(&pdev->dev, &dev_attr_brightness_level);

return err;

}

static int pwm_leds_remove(struct platform_device *pdev)

{

device_remove_file(&pdev->dev, &dev_attr_brightness_level);

device_unregister(dev);

return 0;

}

static struct platform_driver pwm_leds_driver = {

.probe = pwm_leds_probe,

.remove = pwm_leds_remove,

.driver = {

.name = DEVICE_NAME,

.owner = THIS_MODULE,

},

};

static int __init pwm_leds_init(void)

{

return platform_driver_register(&pwm_leds_driver);

}

static void __exit pwm_leds_exit(void)

{

platform_driver_unregister(&pwm_leds_driver);

}

MODULE_LICENSE(“GPL”);

module_init(pwm_leds_init);

module_exit(pwm_leds_exit);

相关问题拓展阅读:

小车的电机驱动要不要L298N?我是想用PMW方式调速,是单片机直接输出高低电平到L298N吗?

我驱动电机的时候用了光耦,不用的话电机停不下来,电路图百度文库有,找不到的话我给你发一个。

不需要用L298N,将上图的DC MTR1接到你单片机的PWM1口,DC MTR2接到单族链片机的PWM2口,你需要程序做的是:清DC MTR2为低电平,并输出PWM信号到PWM1口;反之亦然。

注意:该电路不带过流检测,单片机尘穗卖复位的时候一定要清PWM1、PWM2两个端口,否则程序如一直不启动,两个口均为高电平状态,此时Q1、Q2、Q4、Q6四个三极派逗管为导通状态!

达林顿管有压降,不可避免的对电机速度有限制,可以试着用场春棚做效应管,场效应管没有压降,但是驱动电压高,可以先升压再和告用单片机控制三极扒衡管,进而控制场效应管,这样就把速度提上去了!

L298N的内部结构就是你图里的桥接。可以不用锋薯L298N。

可以不用光耦。

PWM(不是PMW)可以直接接298上的两个使能扰和端。

我用298做了个,也是PWM调银李者速,控制两个电机。没有用光耦。

基于单片机信号发生器设计重点研究问题是什么

利用单片机做信号发生器,其重点就是单片机的主频啦

  因为主频代表着程序运行的时间,这个时间是完成一次程序的从头到尾单片机内部所需的时间,而运行一次只能输出一种端口状态,那么需要方波输出,则需要单片机运行两次才能真正输出一个方波信号,所以主频才是升毁重中之重。

  另外还有程序的整体步数,就是程序的长度或多少,程序语句越多,运行速度也越慢,输出的信号频率也越低

  例神宴如想做一个1MHz的方波发生器,那么51单片机的更高主频是12MHz,然而真正输出的更高只能达到12分之一,那就是1MHz,勉勉强强算是可以

  如果超过1MHz的波形,51类单片机是达不到效果了,只能选择其它单片机

下面是本人曾经利用单吵瞎备片机做的PMW信号发生器程序,仅供参考

/***************************************************************************/

#include//频率约为 2.37 KHz

//根据按键来控制输出波形

it D=P2^0 ; //端口定义

int h,m,s,f;

/***************************************************************************/

void main(void)

{

TMOD=0x22; EA=1; ET0=1; ET1=1; TR0=1;//定时器初始化

while(1)

{

switch(P0)

{

case 0xfe : h=1; break;

case 0xfd : h=2; break;

case 0xfb : h=3; break;

case 0xf7 : h=4; break;

case 0xef : h=5; break;

case 0xdf : h=6; break;

case 0xbf : h=7; break;

case 0x7f : h=8; break;

default : h=9; break;

}

m=10-h;

}

}

/***************************************************************************/

void int0() interrupt 1 //定时器 0 中断

{

TH0=0xff; s++;

if(s>=h){ TR0=0; TR1=1; D=0; s=0; }//开始时间

}

/***************************************************************************

/void int1() interrupt 3 //定时器 1 中断

{

TH1=0xff; s++;

if(s>=m){ TR1=0; TR0=1; D=1; s=0; }//休止时间

}

/***************************************************************************/

基于单片机信号发生器设计

让我来帮你 .

关于linux 方波输出pmw的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。


数据运维技术 » Linux实现方波输出和PWM技术 (linux 方波输出pmw)