opencv学习小结(2)-filter2D函数

函数原型:均

dst=cv.filter2D(src, ddepth, kernel[, dst[, anchor[, delta[, borderType]]]])

 

参数 描述
src 原图像
dst 目标图像,与原图像尺寸和通过数相同
ddepth 目标图像的所需深度
kernel 卷积核(或相当于相关核),单通道浮点矩阵;如果要将不同的内核应用于不同的通道,请使用拆分将图像拆分为单独的颜色平面,然后单独处理它们。
anchor 内核的锚点,指示内核中过滤点的相对位置;锚应位于内核中;默认值(-1,-1)表示锚位于内核中心。
detal 在将它们存储在dst中之前,将可选值添加到已过滤的像素中。类似于偏置。
borderType 像素外推法,参见BorderTypes

改函数实际计算的是相关性,而不是卷积

在内核足够大(~11×11或者更大)的时候,该函数使用DFT算法,对于小内核则直接计算。

也可见,anchor相当于坐标轴平移。

其中ddepth表示目标图像的所需深度,它包含有关图像中存储的数据类型的信息,可以是unsigned char(CV_8U),signed char(CV_8S),unsigned short(CV_16U)等等…

Input depth (src.depth()) Output depth (ddepth)
CV_8U -1/CV_16S/CV_32F/CV_64F
CV_16U/CV_16S -1/CV_32F/CV_64F
CV_32F -1/CV_32F/CV_64F
CV_64F -1/CV_64F

Note:当ddepth=-1时,表示输出图像与原图像有相同的深度。

opencv学习小结(1)–高斯模糊与卷积核

因为做的项目需要用到opencv的工具包,所以本人就开始了对opencv的各类函数进行原理上的学习,希望能够尽快的完成对opencv系统的学习。

1:什么是高斯模糊:

模糊是一种利用图像算法对源图像素色值进行重新合成的一种处理结果(当然也是近视眼就会导致模糊..xixi),而高斯模糊则是运用高斯函数(遵循数据正态分布)求出对应图像的卷积核在进行一定换算最后合成图像的一种特殊处理结果,当然其中有很多过程及概念,先简单了解一下高斯模糊成像的过程是怎么样的:

1:卷积核:

一看非常高大上的名字,它其实就是通过某种算法得出的一个结果矩阵,我们知道图像其实就是一个二维的矩阵排列,通过高斯函数求出来的卷积核(矩阵)在与源矩阵进行一定的换算就得出结果矩阵就是我们最后模糊的图像结果,效果如图所示:

示例1

假设我们先不关心卷积核的生成,那么他是如何通过卷积核生成结果矩阵的呢:如图中所示:如果要求出结果矩阵的第2排第2列中的元素值需要将卷积核矩阵对准源矩阵中对应的像素元素也就是source pixel中的6(3*3的矩阵),然后通过求出加权的和也就是(-1*3)+(0*0)+(1*1)+(-2*2)+(0*6)+(2*2)+(-1*2)+(0*4)+(1*1)结果为-3,那么结果矩阵里的第2排第2列的元素值为-3;是不是感觉就是那么回事,其实的确就是那么回事,通过高斯函数求出的卷积核(也称滤波器)在进行加权求和就得出运算之后的图像了,那么现在知道模糊成像的这么一个过程,我们应该继续探索高斯求出的卷积核是怎么求出来的!

2:模糊的原理

我们知道了模糊的一个过程那么我们应该知道模糊的简单原理是什么样的还是画图理解更好解释:

示例2

如上图所示,如果我们想对中心元素点2进行一个模糊处理那么应该怎么做,既然是模糊那么就是通过周边元素的值来确定自己应该是多少,显然照上图来看的话我们模糊很简单就是求出周边的加权平均,结果就是1了(就像扫雷一样)

即(1+1+1+1+2+1+1+1+1)/9=1,如下:

示例3

这样得出的结果就已经是经过简单模糊的图像的了,当然实际操作不可能通过这么简单的运算来得出模糊图像,因为不管从效率还是实际的模糊效果来说都太差了,那么我们应该去学习更高级的权重分配模式了。在统计学里有一个影响深远的权重分配模式叫“正态分布”,在高斯模糊中也正是遵循正态分布来求出适应的模糊值,那么我们就了解一下正态分布是什么东西吧

正态分布的权重

一维的正态分布图
二维的正态分布图(图像都是二维)

首先我们来看一下正态分布是个什么样子,在统计学中正态分布应用在很多地方如医学,建筑等领域,从图中我们可以看出距离中心0zero的位置越小取值越大,曲度越大,处于中心点则是最高权重,位于周边则取值递减,也更加符合只有距离模糊像素越近的像素才有更高的权重思路,这种做法就避免了如果模糊像素与周边像素颜色差值过大造成的模糊效果不尽人意的问题;

权重矩阵(可以理解为卷积核)

如果理解了正态分布的权重分配我们可以继续往下来讲,如果我们在实际对图像做处理的时候其实就是对图像矩阵做处理,图像都是由很多像素排列的矩阵形成的图像,那么权重矩阵该怎么分配呢,很简单就是利用像素点的坐标值通过高斯函数对每个像素点进行运算得出每个像素点的权重值

二维的高斯函数公式

其中x,y代表当前模糊像素点的坐标值,σ代表是模糊半径(指的就是你中心点与周围像素的距离取值),有了这个公式那么算出权重矩阵那就是没那么复杂了,举个栗子,如图:

5*5的卷积核也就是权重矩阵

如果要构建一个模糊半径为2的权重矩阵那么他的矩阵应该是(2*2+1)*(2*2+1),为什么要加1呢是因为作为权重矩阵往往都是奇数矩阵,通常来讲这是为了能有一个中心点,如果不是奇数那么中心点的半径就不是统一的,例如如果是2*2的矩阵那么就无法确定准确的中心点位置,奇数刚好可以解决这个问题;有了这个矩阵我们就可以开始利用高斯函数来进行计算了,将x,y带入函数中,利用java的数学操作可以求出中心点(0,0)及周边的像素点的权重值: sigma(模糊半径)

 
函数在代码中的体现

最后通过每个对矩阵里的每个像素点进行运算求出的矩阵:

权重矩阵

这样我们的权重矩阵也就是卷积核就大功告成了,当然还没有完,假设我们有一个图像矩阵也就是未经过处理的图像矩阵,他们的色值如下图所示:

这个色值值得是图像的RGB色值,那么我们将权重矩阵对准源图像的色值矩阵将权重矩阵和色值矩阵相乘得出个结果全部加起来就可以求出中心的模糊值

浅谈tcp/ip协议

什么是 TCP/IP?

TCP/IP 是供已连接因特网的计算机进行通信的通信协议。

TCP/IP 指传输控制协议/网际协议 (Transmission Control Protocol / Internet Protocol)。

TCP/IP 定义了电子设备(比如计算机)如何连入因特网,以及数据如何在它们之间传输的标准。

这张表就是tcp/ip模型和OSI模型的模型图。我们可以看到tcp/ip模型比osi模型更加精简一些。

tcp/ip协议中各层的作用

在我们刚刚贴出的表中可以看到,tcp/ip协议分为四层,分别是:应用层,传输层,网络层和数据链路层。

应用层

应用层是TCP/IP协议的第一层,是直接为应用进程提供服务的。对不同种类的应用程序它们会根据自己的需要来使用应用层的不同协议。应用层还能加密、解密、格式化数据。应用层可以建立或解除与其他节点的联系。常见的应用层协议有:HTTP,MQTT,SMTP等等。

大家平时在登陆网站的时候经常会以https作为开头,https是http协议的变种,区别在于http使用明文传输容易被破解,安全性不高,而https协议会对传输的内容进行加密,使得器安全性大大增加,所以https协议已经几乎取代了http协议(当然有些网站用的还是http……)。

运输层

作为TCP/IP协议的第二层,运输层在整个TCP/IP协议中起到了中流砥柱的作用。

它为两台主机上的应用程序提供端到端的通信。常见的传输层协议有:tcp,udp协议。

网络层

网络层是OSI参考模型中的第三层,介于传输层和数据链路层之间,它在数据链路层提供的两个相邻端点之间的数据帧的传送功能上,进一步管理网络中的数据通信,将数据设法从源端经过若干个中间节点传送到目的端,从而向运输层提供最基本的端到端的数据传送服务。

主要的协议有ip,ipx等协议。

数据链路层

数据链路层最基本的服务是将源计算机网络层来的数据可靠的传输到相邻节点的目标计算机的网络层。

主要协议有点对点协议(Point-to-Point Protocol);以太网(Ethernet);高级数据链路协议(High-Level Data Link Protocol);帧中继(Frame Relay);异步传输模式(Asynchronous Transfer Mode)。

数据通过一层层的流动最后会发送出去,接受的服务端也会按照tcp/ip协议来进行读取信息,从数据链路层开始直到应用层最后到我们手上的就是经过处理好后的信息了。

易班PHP-认证sdk解析

先上sdk的核心代码,代码是使用php写的,非常容易理解,我甚至想起了我亲戚家小孩看的《宝宝的PHP》。

 

<?php
  /**
   * @package YBAPI
   *
   * 授权认证接口
   *
   * 使用授权认证协议认证方式的接口
   * 授权接口中的接口对于appid或appsecret还有access_token有不同的需求
   * 调用不同方法时需要开发人员保证已经把对应的配置值传入
   */
  class YBAPI_Authorize{
    
    const API_OAUTH_CODE	= "oauth/authorize";
    const API_OAUTH_TOKEN	= "oauth/access_token";
    const API_TOKEN_QUERY	= "oauth/token_info";
    const API_TOKEN_REVOKE	= "oauth/revoke_token";
    
    
    /**
     * 构造函数
     *
     * 使用YBOpenApi里的config数组初始化
     *
     * @param Array 配置(对应YBOpenApi里的config数组)
     */
    public function __construct($config) {
      foreach ($config as $key => $val) {
        $this->$key	= $val;
      }
    }
    
    /**
     * 设置访问令牌
     *
     * @param String 访问令牌
     * @return YBAPI_Authorize 本身实例
     */
    public function bind($token) {
      $this->token = $token;
      
      return $this;
    }
    
    /**
     * 生成授权认证地址
     *
     * 客户端重定向到授权地址
     * 获取授权认证的CODE用于取得访问令牌
     *
     * @param	String 防跨站伪造参数
     * @return	String 授权认证页面地址
     */
    public function forwardurl($state = 'QUERY') {
      assert(!empty($this->appid),   YBLANG::E_NO_APPID);
      assert(!empty($this->backurl), YBLANG::E_NO_CALLBACKURL);
      
      $query = http_build_query(array(
        'client_id'		=> $this->appid,
        'redirect_uri'	=> $this->backurl,
        'state'			=> $state,
      ));
      
      return YBOpenApi::YIBAN_OPEN_URL.self::API_OAUTH_CODE.'?'.$query;
    }
    
    /**
     * 通过授权的CODE获取访问令牌
     *
     * 应用服务器只需要请用此接口
     * 自动处理重定向
     *
     * @param	String 授权CODE
     * @param	String 应用回调地址
     * @return	Array  访问令牌哈希数组
     */
    public function querytoken($code, $redirect_uri = '') {
      assert(!empty($this->appid),   YBLANG::E_NO_APPID);
      assert(!empty($this->seckey),  YBLANG::E_NO_APPSECRET);
      
      if(empty($redirect_uri)) {
        $redirect_uri = $this->backurl;
      }
      $param = array(
        'client_id'		=> $this->appid,
        'client_secret'	=> $this->seckey,
        'code'			=> $code,
        'redirect_uri'	=> $redirect_uri
      );
      
      $info = YBOpenApi::getInstance()->request(self::API_OAUTH_TOKEN, $param, true, false);
      if(isset($info['access_token'])) {
          $this->bind($info['access_token']);
      }
      return $info;
    }
    
    
    /**
     * 获取用户token
     */
    public function getToken(){
        if(isset($_GET['code']) && !empty($_GET['code'])) {
            /**
             * 使用授权码(code)获取访问令牌
             * 若获取成功,返回 $info['access_token']
             * 否则查看对应的 msgCN 查看错误信息
             */
            $info = $this->querytoken($_GET['code']);
            if(isset($info['access_token'])) {
                return array('status'=>true, 'token'=>$info['access_token']);
            }else {
                return array('status'=>false, 'msg'=>$info['msgCN']);
            }
        }else {	// 重定向到授权服务器(这里使用header()重定向,可用使用其它方法)
            header('location: '.$this->forwardurl());
            return array('status'=>false, 'msg'=>'');;
        }
    }
  }
  
?>

 

对于定义部分不作过多解释,首先我们可以看到,首先创建了几个函数用于提取config和token中的信息。

之后使用函数使用提取的内容创建一个可访问验证url,供用户调用

之后的便是处理服务器返回的json文件,来作为用户的令牌;显而易见,这是一个非常简单的,简单到完全不像sdk的sdk,以至于别的接口全要用户自己写,易班真的NB!!!

 

易班PHP-SDK开发

一、文档目录结构

-- /
|
|-- classes/ 开放平台SDK(网站接入)
|
|-- demo/ 测试实例
|
|-- config.php 配置文件,您需要修改这个文件写入对应的 AppID 等信息
|
|-- index.php DEMO入口
|
|-- authorize.php 网站接入授权流程(使用DEMO时请将管理中心->网站详细中授权回调地址指向此文件所在的URL)
|
|-- apitest.php 功能接口测试(需要完成授权流程获取到access_token才能进行接口测试)
|
|-- revoke.php 撤销授权功能调用测试
|
|-- README.txt 本文档

 

二、SDK简要使用说明

1、将 classes/ 目录下所有文件放到您的项目中,保持里面的目录结构
2、引用classes/下yb-globals.inc.php文件
3、通过$api = YBOpenApi::getInstance()->init() 实例化对象并配置应用信息
4、(网站接入授权)通过 $api->->getAuthorize()->getToken() 完成用户授权,获得access_token(可参考demo中authorize.php文件)
5、通过$api->bind()绑定access_token

三、SDK接口调用简要说明

完成初始化后,通过YBOpenApi::getInstance()->request($url, $param, $isPOST, $applyToken)来调用易班api
参数说明:
$url String 具体调用的接口名称,例如user/me
$param Array 接口请求参数数组
$isPOST Boolean 是否使用POST方式请求,默认使用GET方式
$applyToken Boolean 请求参数中是否需要添加access_token,设置为true时自动添加之前绑定的token到参数数组中(如果为true请先通过bind()将token绑定至实例中)

以 获取当前用户信息 为例:(接口说明 https://o.yiban.cn/wiki/index.php?– /

$url 		= 'user/me';
$param 		= array();
$isPOST		= false;
$applyToken = true;
$result = YBOpenApi::getInstance()->request($url, $param, $isPOST, $applyToken);//获取接口返回信息

 

最后的祝福–致电子工业学校(上海电子信息职业学院中专部)

自从来到电子工业以来,我们共度了3年时光,其中,有快乐、有悲伤,酸甜苦辣,五味杂陈。在大电子,认识了不少好老师,作为祖国未来的花花草草,这些好老师教会了我们许多道理,比如德高望重的沈老师,教导我们千万不能在上课时使用手机做任何事情,不然就是违反了国务院颁发的法律。双元部的老师教会我们一定要妥善保管物品,不然会造成大麻烦。除此之外,我们应该感谢吴敏老师、曹华老师、张芸老师、朱鸿婷和尹春丽老师、朱星宇老师、黄颖老师、宋显龙老师、余诞夏老师、龚飞老师、彭水林老师、国文霞老师、孙慧敏和陆凯老师等等所有担任过我班教师的老师,感谢老师们对我们的教导,感谢老师们在无数个夜晚熬夜备课、阅卷、出卷的辛勤劳动,虽然我们不是最优秀的学生,但是进了电机学院后,会努力,会拼搏!会去图书馆里安静地学习,而不是大吵大闹打游戏以至于几百双眼睛齐刷刷地盯着自己而无地自容!最后,再次向各位老师致谢,感谢您们的付出,感谢您们的操劳,感谢您们的努力!

 

但是!我还有一段话想对戴建雄老师和学生科讲!

各位同学老师们,你们好:
今天,是我毕业的日子,也是全体16届毕业的日子,有很多话想对在群的各位说。
令我印象最深刻的,是学生科每个老师对工作的认真负责和一丝不苟。特别是戴老师,和已经不在本校的孙皓源老师。
第一,是关于团员的事情。我校老团委孙老师对学生都热情负责,即便是没有入团的同学,也会积极鼓励他们,给他们颁发优秀团员奖状,祖国的园丁,你们辛苦了!
第二,用餐问题,在此,我更加要好好感谢学生科一些老师的努力工作,虽然经常人不在岗位,但我知道,这是他们繁忙的工作所迫!虽然个别同学肠胃不好,一吃学校的饭就会拉肚子,但是你们为了学生安全考虑,仍旧坚持不让学生点外卖或泡泡面,导致他们一天上几次厕所,这是何等智高无上的行为啊!
由于字数限制,就不把小事一一叙述了。再次感谢学生科的老师们,你们,辛苦了!
       第三,让电子中本喜大普奔的事,本次毕业班级只有中高职和中专拿到了2张毕业照,虽然收费是一样的,由此可以得出,中本的照片质量最好,一个顶俩,具有新时代社会特色主义,完美诠释了什么叫做工匠精神,精益求精!
再次感谢戴老师的大恩大德以及对中本三年的呕心沥血的教育!
        @戴建雄 感激不尽,这辈子都要谢谢您了!不会忘记您的善良和负责!
 

蛇(Python)型填数

参考答案:

#include <cstdio>
#include <cstring>
#include<stdlib.h>
int main()
{
  int n;
  int a[101][101];
  int sum;   //计数
  int i, j;
  scanf_s("%d", &n);
    sum = 0;
  int x = 0, y = n - 1;   // 代表 行列
  memset(a, 0, sizeof(a)); //将数组清零
  a[x][y] = ++sum;  //填入第一个数 :1
  while (sum < n * n)
    {
      while (x + 1 < n && !a[x + 1][y])    // !a[x + 1][y] 代表这个区域未有数填入,区域依然为0
      {
        a[++x][y] = ++sum;
      }
      while (y - 1 >= 0 && !a[x][y - 1])
      {
        a[x][--y] = ++sum;
      }
      while (x - 1 >= 0 && !a[x - 1][y])
      {
        a[--x][y] = ++sum;
      }
      while (y + 1 < n && !a[x][y + 1])
      {
        a[x][++y] = ++sum;
      }
    }
  for (i = 0; i < n; i++)
    {
      for (j = 0; j < n; j++)
      {
        printf("%5d", a[i][j]);
      }
      printf("\n");
    }


  //system("pause");
  return 0;
}

 

百事世界杯之旅

解题思路:

//示例代码 C++

#include <cstdio>
#include <cmath>

long long n;

long long gcd(long long a,long long b)
//求最大公约数(用于约分和通分)
{
    if(!b)
    {
        return a;
    }
    return gcd(b,a%b);
}

int getlen(long long a)
//求一个数的位数(用于输出)
{
    return (int)(log(a)/log(10))+1;
}

struct frac
{
    long long z,m;

    int join(long long a,long long b)
    //构造一个分数,值为a/b
    {
        z=a;
        m=b;
        int y=gcd(z,m);
        z/=y;
        m/=y;
        //上面三步是约分
        return 0;
    }

    frac operator +=(const frac &other)
    //加法
    {
        long long pu=m*other.m/gcd(m,other.m);
        //pu是最简公分母
        z*=pu/m;
        long long otherz=other.z;
        otherz*=pu/other.m;
        m=pu;
        //上面4步是通分
        z+=otherz;
        //上面这一步是加法
        long long y=gcd(m,z);
        m/=y;
        z/=y;
        //上面三步是约分
        return *this;
    }

    int print()
    {
        long long pzh=z/m;
        long long pz=z-(z/m)*m;
        //pzh是带分数形式的整数部分,pz是带分数形式的分子
        if(pz)
        //如果有分子的话,输出分子
        {
            for(int i=1; i<=getlen(pzh); i++)
            {
                putchar(' ');
            }
            printf("%lld\n",pz);
        }
        printf("%lld",pzh);
        //输出整数部分
        if(pz)
        //如果有分数部分的话,输出分数线和分母
        {
            for(int i=1; i<=getlen(m); i++)
            {
                putchar('-');
            }
            putchar('\n');
            for(int i=1; i<=getlen(pzh); i++)
            {
                putchar(' ');
            }
            printf("%lld",m);
        }
        return 0;
    }
};

frac ans;

int main()
{
    scanf("%lld",&n);
    ans.join(0,1);
    //求sum(n/i)
    for(long long i=1; i<=n; i++)
    {
        frac x;
        x.join(n,i);
        ans+=x;
    }
    ans.print();
    putchar('\n');
    return 0;
}

 

 

 

参考答案:

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
long long a, b, p;
long long gcd(long long a, long long b)      //求最大公约数,参与分数计算,使用long long型函数
{                                            //此GCD函数建议直接背下来,所有输出分数的题全部都要用到
  if (b == 0) return a;
  return gcd(b, a%b);
}

int main() {                                //题意为:从n种数中选出n个不同数的期望次数。
  int n, i; cin >> n; a = 1; b = 1;       //所以第一个球星抽到的概率是1,第n个是1/n。那么我们要从k个到k+1个球星的概率为(n-k)/n,所以期望为n/(n-k)。
  for (i = 2; i <= n; i++) {              //答案即为ANS=n(1/1+1/2+1/3+....1/n)
    a = a*i + b; b *= i;
    p = a; a /= gcd(a, b); b /= gcd(p, b);//计算 
  }
  a *= n;
  if (a%b == 0)cout << a / b;
  else {
    cout << a / b; a -= b*(a / b);
    cout << '(' << a / gcd(a, b) << '/' << b / gcd(a, b) << ')';//输出 
  }
  system("pause");
  return 0;
}

 

基于rt_thread的pwm设备应用实例

PWM概述

我们即将创建一个会变换颜色的呼吸灯的程序,在编写程序之前我觉得有必要先稍微讲解一下什么是pwm。

pwm的全称是Pulse Width Modulation,翻译过来就是脉冲宽度调制。

以上就是pwm的原理示意图。在T1这个时间段中属于高电平输出,在T2这个时间段中属于底电平输出。高电平在整个周期(T1+T2)中所占时间的比例就是占空比,通过对占空比的控制我们可以输出一段幅值相等的脉冲。

PWM控制亮度的原理

我们平时调节手机屏幕和灯的亮暗就是靠pwm来进行控制的,通过对占空比的控制我们可以让led灯进行快速的闪烁,这个闪烁因为过快导致我们的肉眼根本无法识别出来,在不停的闪烁中如果灭的持续时间长那么我们就会觉得屏幕变暗了,如果亮的时间长我们就会觉得屏幕变亮了。

编程思路

首先贴上本次要使用的函数:

rt_device_find()

rt_pwm_set()

rt_pwm_enable()

rt_pwm_disable()

函数 描述
rt_device_find() 根据 PWM 设备名称查找设备获取设备句柄
rt_pwm_set() 设置 PWM 周期和脉冲宽度
rt_pwm_enable() 使能 PWM 设备
rt_pwm_disable() 关闭 PWM 设备

通过不停对占空比进行有序的改变可以让灯的亮度由暗变亮再让灯由亮变暗,有两种方法可以完成这种操作,一个是使用for或者while循环,一个就是使用数组,在接下来的实例中我们是使用数组进行有序控制的。

代码

首先在这儿声明一下,这里使用的开发板以低电平输出亮,高电平输出暗,开发板中关于led的原理图如下:

#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>

#define pwm        "pwm3"
int jun[300];//声明数组
int a=0;
/* 将占空比的值赋给数组 */
void pwm_sat8(){
   for(int i=100000;i>=0;i=i-1000){
  jun[a]=i;
   a++;
}
  
   for(int i=0;i<=100000;i=i+1000){
  jun[a]=i;
   a++;
}

  a=0;
  struct rt_device_pwm *pwm_dev;//设备句柄      
  pwm_dev = (struct rt_device_pwm *)rt_device_find(pwm);//查找设备
/* 完成红、绿、蓝亮度的依次变化 */
  while(1){
    
  for(a=0;a<=201;a++){
   rt_pwm_set(pwm_dev,2,100000,jun[a]);
   rt_pwm_enable(pwm_dev,2);
   rt_thread_mdelay(20);
  }
  
  for(a=0;a<=201;a++){
   rt_pwm_set(pwm_dev,3,100000,jun[a]);
   rt_pwm_enable(pwm_dev,3);
   rt_thread_mdelay(20);
  }
  
  for(a=0;a<=201;a++){
   rt_pwm_set(pwm_dev,4,100000,jun[a]);
   rt_pwm_enable(pwm_dev,4);
   rt_thread_mdelay(20);		
  }
  
    }
  
      }

MSH_CMD_EXPORT(pwm_sat8, pwm sample)

首先,我们定义了一个名字为jun,大小为300的数组。

我们在这里设置的周期是100000纳秒(也就是0.1ms),因为亮度的大小范围是0%——100%,所以我们将占空比从100000,每次以减1000的方式赋值给数组,再从0,以每次加1000的方式赋值给数组,这样子我们就得到了{100000,99000,98000…98000,99000,100000}的数组。

接着使用rt_device_find()获取设备句柄,在获取设备句柄之前我们可以看到有这么一行代码:

struct rt_device_pwm *pwm_dev;

这行代码我们先创建了一个结构体,并给这个结构体设置了一个指针。

因为rt_device_find()返回的是一个句柄所以我们要先强制类型转换,然后才能把返回的句柄赋给指针。

while(1){
  
for(a=0;a<=201;a++){
 rt_pwm_set(pwm_dev,2,100000,jun[a]);
 rt_pwm_enable(pwm_dev,2);
 rt_thread_mdelay(20);
}

for(a=0;a<=201;a++){
 rt_pwm_set(pwm_dev,3,100000,jun[a]);
 rt_pwm_enable(pwm_dev,3);
 rt_thread_mdelay(20);
}

for(a=0;a<=201;a++){
 rt_pwm_set(pwm_dev,4,100000,jun[a]);
 rt_pwm_enable(pwm_dev,4);
 rt_thread_mdelay(20);		
}

  }

以上代码就是实现交替变换颜色并且安照顺序调节亮度的代码。我们抽取第一段分析一下:

for(a=0;a<=201;a++){
   rt_pwm_set(pwm_dev,2,100000,jun[a]);
   rt_pwm_enable(pwm_dev,2);
   rt_thread_mdelay(20);
  }

通过rt_pwm_set()可以设置我们需要使用的pwm设备和需要使用的通道(不理解什么是通道?看看我刚发的led原理图),然后设置周期为100000,最后设置占空比,在上文中我们已经讲过我们是使用数组进行占空比设置的,所以设置为jun[a],通过for循环中的判断和更新变量来完成占空比从100000到0再从0到100000的变化。

在设置pwm设备后我们要使能pwm设备,最后加上延时函数rt_thread_mdelay(),这里我们设置延时为0.02毫秒。

*注意

因为本次使用的开发板是以低电平为输出led亮的,所以当你使用env工具配置好pwm设备后它会默认亮起来,为了解决这个问题我们可以在mian()函数中编写如下代码:

int main(){
struct rt_device_pwm *pwm_dev;      
  pwm_dev = (struct rt_device_pwm *)rt_device_find("pwm3");
  rt_pwm_set(pwm_dev,2,100000,100000);
  rt_pwm_enable(pwm_dev,2);
  rt_pwm_set(pwm_dev,3,100000,100000);
  rt_pwm_enable(pwm_dev,3);
  rt_pwm_set(pwm_dev,4,100000,100000);
  rt_pwm_enable(pwm_dev,4);
}

这样子我们就能在开始的时候把它给关闭了。

学习辛苦了,看点色图放松一下吧(/龇牙)