打印
[Cortex-M0技术交流]

用C++模板计算波特率的值, PLL值

[复制链接]
11363|33
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
X-Hawk|  楼主 | 2011-5-3 18:27 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 hotpower 于 2012-9-22 13:13 编辑

(2012/07/27新增PLL值计算,请见30楼)

看到李老师拿个gcc扩展实现的SetBaudRate函数,
忍不住手痒,用C++模板也写了一个。
什么是模板呢?很精确的描述咱也不会,简单说就是带参数的C++类或函数,
这个参数可以是类型或常数。
带着这个理解,想下参数写在哪里,然后看一两个代码,任何人都能学会模板。

当然,咱这个实现,纯粹算是跑到老师的地盘班门弄斧,
老师那个实现,输入的波特率参数,语义上只要是常数,就能达到优化结果。
这个新的实现呢,有一定的限制,输入参数语法上必须为常数。

言归正传,先看例子,对于新唐M051芯片的寄存器,
计算22.1184M输入时钟的情况下,得到115200波特率。

typedef BaudRateT<22118400, 115200> baudrate_type;
outpw(&UART0->BAUD, baudrate_type::REG_VALUE);
很简单,就两句话!

第一句,用两个频率值,作为参数,定义一个类型。
该类型会自动算出寄存器的值。
不管编译器是否打开优化,以上波特率的计算过程,全部都在编译期间完成,
不占用任何的执行时间和空间。

第二句,将波特率写入寄存器。实际执行的程序里只有这句。

以下是类型里的数值,打印出来看下,便于理解,没实际意义。

printf("error = %d\n"
       "Register value of UA_BAUD = 0x%08x\n"
       "DIV_X_N = %d\n"
       "DIV_X_ONE = %d\n"
       "DIVIER_X = %d\n"
       "BRD = %d\n",
        
       baudrate_type::error,      //指示计算是否出错
       baudrate_type::REG_VALUE,  //BAUD寄存器的32位值

       baudrate_type:IV_X_EN,   //BAUD寄存器的DIV_X_EN栏位
       baudrate_type:IV_X_ONE,  //BAUD寄存器的DIV_X_ONE栏位
       baudrate_type:IVIDER_X,  //BAUD寄存器的DIVIDER_X栏位
       baudrate_type::BRD         //BAUD寄存器的BRD栏位
       );

相关帖子

来自 2楼
X-Hawk|  楼主 | 2011-5-5 23:48 | 只看该作者
本帖最后由 hotpower 于 2011-5-13 00:43 编辑

对的,查表其实迅捷而有效!



使用特权

评论回复
板凳
X-Hawk|  楼主 | 2011-5-3 18:39 | 只看该作者
本帖最后由 hotpower 于 2011-5-13 00:42 编辑

附上模板的实现代码,有点乱哈


template<uint32_t clk, uint32_t baudRate, uint32_t div,
    bool error_stop = !(div < 16)>
class BaudRateT2
{
private:
    enum
    {
        condition    = (((clk / baudRate) % (div + 1)) < 3)
    };
    typedef BaudRateT2<clk, baudRate, div + 1> next_type;
public:
    enum
    {
        error       = error_stop,               
        DIV_X_EN    = 1,
        DIV_X_ONE   = 0,
        DIVIDER_X   = (condition ? div
                    : next_type:IVIDER_X),
        BRD         = (condition? (clk / baudRate / (div + 1) - 2)
                    : next_type::BRD)
    };
};

template<uint32_t clk, uint32_t baudRate, uint32_t div>
class BaudRateT2<clk, baudRate, div, true>
{
public:
    enum
    {
        error       = true,
        DIV_X_EN    = 0,
        DIV_X_ONE   = 0,
        DIVIDER_X   = 0,
        BRD         = 0
    };
};


template<uint32_t clk, uint32_t baudRate>
class BaudRateT1
{
private:
    enum
    {
        condition   = !(clk / baudRate - 2 > 0xFFFF)
    };
    typedef BaudRateT2<clk, baudRate, 8> next_type;
public:
    enum
    {
        error       = (condition ? 0
                    : next_type::error),
        DIV_X_EN    = (condition ? 1
                    : next_type:IV_X_EN),
        DIV_X_ONE   = (condition ? 1
                    : next_type:IV_X_ONE),
        DIVIDER_X   = (condition ? 0
                    : next_type:IVIDER_X),
        BRD         = (condition ? (clk / baudRate - 2)
                    : next_type::BRD)
    };
};

template<uint32_t clk, uint32_t baudRate>
class BaudRateT0
{
private:
    enum
    {
        condition   = ((clk / baudRate) % 16 < 3)
    };
    typedef BaudRateT1<clk, baudRate> next_type;
public:
    enum
    {
        error       = (condition ? 0
                    : next_type::error),
        DIV_X_EN    = (condition ? 0
                    : next_type:IV_X_EN),
        DIV_X_ONE   = (condition ? 0
                    : next_type:IV_X_ONE),
        DIVIDER_X   = (condition ? 0
                    : next_type:IVIDER_X),
        BRD         = (condition ? (clk / baudRate/16 - 2)
                    : next_type::BRD)
    };
};

template<uint32_t clk, uint32_t baudRate>
class BaudRateT
{
private:
    typedef BaudRateT0<clk, baudRate> next_type;
public:
    enum
    {
        error       = next_type::error,
        DIV_X_EN    = next_type:IV_X_EN,
        DIV_X_ONE   = next_type:IV_X_ONE,
        DIVIDER_X   = next_type:IVIDER_X,
        BRD         = next_type::BRD,
        REG_VALUE   = ((uint32_t)BRD)
                    + ((uint32_t)DIVIDER_X << 24)
                    + ((uint32_t)DIV_X_ONE << 28)
                    + ((uint32_t)DIV_X_EN << 29)
    };
};


X-Hawk




X-Hawk

使用特权

评论回复
地板
X-Hawk|  楼主 | 2011-5-3 18:56 | 只看该作者
本帖最后由 hotpower 于 2011-5-13 00:42 编辑

模板,template, 所谓用之者生,读之者。。生不如死。
代码贴完了,有耐心看完2楼的,都非普通人。

酒鬼先吃饭去了,回来跟大家聊聊模板的几个重要的方面。
高手就不用看了,C++新手可以了解一下。

如果各位哪天写到C++,写到模板,写到串口,
突然想到用一用华邦新唐的M0芯片,
酒鬼目的就达到喽。。

使用特权

评论回复
5
hotpower| | 2011-5-3 19:00 | 只看该作者
酒鬼最近醉拳打的不错!很给力!
看来俺也要复习和学习了,否则被拉的太远了。

使用特权

评论回复
6
dong_abc| | 2011-5-3 19:38 | 只看该作者
本帖最后由 dong_abc 于 2011-5-3 19:41 编辑

//模板,template, 所谓用之者生,读之者。。生不如死。
代码贴完了,有耐心看完2楼的,都非普通人。

哈哈,普通人来观光:lol

使用特权

评论回复
7
murex| | 2011-5-3 20:50 | 只看该作者
酒鬼就是XX

使用特权

评论回复
8
X-Hawk|  楼主 | 2011-5-4 01:39 | 只看该作者
本帖最后由 hotpower 于 2011-5-4 21:11 编辑

菜农老师客气。
还是继续罗嗦模板的问题。

一、基本写法:
模板用于实现某个类型或函数,将其中用到的类型或数值量,用参数替换。
例如函数:
template<class T>  //代码1
T max(T a, T b)
{
    return (a > b ? a : b);
}
例如类型:
template<class T1, class T2>  //代码2
class some_type
{
    T1 element1;
    T2 element2;
};
嗯,地球人都能看明白,就是替换嘛。嗯,就是替换。
像typedef一样,只是简单的替换而已。当年看某人之教科书,
它就是没告诉咱这个替换的道理 ---
typedef就是将原本声明变量的位置,用类型名替换,于是就有了新的类型。
如此简单的道理不说,此书却举例许多种,不得要害。想来看书不行,还真得有点悟性。

还是说模板,下面用自问自答的方式逐步深入。
(注:酒鬼不是语言学家,以下可能有讲的不精确/完备的地方,欢迎来拍砖。)

1. 什么可以当模板参数?
    类型,例如上面的例子
    数值常量,例如2楼计算M051波特率的例子
    字符串可以当参数?不行
    字符可以当参数?可以
   
2. 如何使用已经写好的模板?
    将模板参数放到尖括号即可。模板名<参数> 的形式,可以看作实际的函数名或类型名。
       int a = max<int>(3, 4);    //调用max模板函数
       some_type<int, short>  o;  //使用some_type模板产生一个类型,使用该类型定义一个变量o

3. 尖括号中的参数可以省略吗?
    可以,如果在某种情况下,确信编译器可以推断出模板参数,你就可以将它省略。
    例如:  int a = max(3, 4); //没问题,max能通过3和4知道类型是int
    例如:类似于some_type,再写一个obj模板
template<class T1, class T2 = long>  //代码3
class obj
{
    T1 element1;
    T2 element2;
};
然后写obj<int> o; //没问题,obj模板第三个参数默认为long,
     和obj<int, long> o; 是一样的。

4. obj<int, short> 和 obj<int, long> 有什么区别?
    两个都是C++的类型(就和 char, bool一样,都是类型)。
    但他们是不同的类型,穿着同样的外衣,
    两个类型内部的实现,可以非常的类似,也可以完全的不同。
    本质上,它们是两个东西。
   
5. 如何让obj<int, long> 有不同的实现?
    为obj<int, long>写一个特殊的类即可。
template<>  //代码4
class obj<int, long>
{
    char c;
};
加上这部分实现的后,对于<int, long>作为参数的obj类型,将优先使用 "代码4" 的类型实现。
   
   obj<int, long> i; //定义一个变量i,i的类型为“代码4”的obj类型
   obj<int, short> j; //定义一个变量j,j的类型为“代码3”的obj类型
   obj<int> k; //第二个参数没写,根据 "代码3" 的template一行,默认用long做第二个参数,
                      //于是obj<int> 相当于obj<int, long>, 于是k的类型为“代码4”的obj类型!!

6. 对于任意的类型T, 如何让obj<T, long> 有不同的实现
    为任意T写一个obj<T, long>的特殊的类即可。
template<class T>  //代码5
class obj<T, long>
{
    short *ptr;
};
加上这部分实现的后,对于<某类型, long>作为参数的obj类型,将优先使用 "代码5" 的类型实现。

   obj<bool, long> a; //定义一个变量a,a的类型为“代码5”的obj类型
   obj<bool, short> b; //定义一个变量b,b的类型为“代码3”的obj类型
   obj<bool> c; //第二个参数没写,根据 "代码3" 的template一行,默认用long做第二个参数,
                      //于是obj<bool> 相当于obj<bool, long>, 于是c的类型为“代码5”的obj类型!!
   
7. 新的问题,obj<int, long>既符合 "代码4",又符合 "代码5"。
    对于obj<int, long> v; 变量v, 到底是哪个类型?
    要看哪一个的匹配程度最具体化。
    其实他还符合 "代码3"。匹配的具体程序,"代码4" > "代码5" > "代码3".
    "代码4" 所有模板参数都匹配到了,是最终的优胜者。
    所以变量v的类型,是 "代码4" 定义的类。

8. 有点迷糊?
    请先仔细比对下代码3、代码4、代码5的实现,
    特别是其中尖括号的不同。
    总结一下类的模板的写法:
    a. 需要至少写一个“通用实现”,比如代码3。
    b. 可以对具体参数特化(或部分特化),比如代码4,代码5.
        写特化类的时候,将没有特化的参数,放在最前面template后的尖括号里;
        并在类名后,需要加上所有的参数列表。

9. 函数模板的模板参数可以特化吗?
    不可以,没需要。用C++的函数重载,能达到同样的目的。


后续
二、经典例子1, 用模板实现一个编译期的阶乘运算
三、经典例子2,boost::any,可以当作任意类型用的类型
。。。
这些很多地方有提过,感兴趣也可自行google哦。


使用特权

评论回复
9
tlb| | 2011-5-4 05:34 | 只看该作者
没用过C++
标记下

使用特权

评论回复
10
hotpower| | 2011-5-4 10:40 | 只看该作者
讲的很明了。谢谢了

使用特权

评论回复
11
dong_abc| | 2011-5-4 12:13 | 只看该作者
都是强人啊,仰望一下,帽子掉了。:hug:

使用特权

评论回复
12
hotpower| | 2011-5-5 14:45 | 只看该作者
有表格吗?查表也不错。

使用特权

评论回复
13
X-Hawk|  楼主 | 2011-5-6 00:26 | 只看该作者
本帖最后由 hotpower 于 2011-5-13 00:43 编辑

二、经典例子:用模板实现一个编译期的阶乘运算

这个程序非常简单,用模板计算阶乘。
计算阶乘的递归公式
    fac(n) = n * fac(n-1)
    fac(0) = 1
现在,我们用模板来改写以上公式 ---
第一步,
    写一个通用fac实现,其中用static const定义一个常量result,
    阶乘的计算结果,自然就是fac<n>::result了。
    这个模板的意思是 fac<n>::result = n * fac<n-1>::result
    相当于第一个递归公式。
第二步,
    我们必须让第一步的递归,递归到0结束。
    于是写一个fac<0>的特别实现

最后,正如下面main函数中的代码所示,
比如要得到5的阶乘,只要写fac<5>::result就对了,

完整的代码如下,这个代码相对简单易懂,
用来勾引人开始玩模板编程,非常合适。。
#include <stdio.h>

//第一步,对于任何的n的实现
template<unsigned int n>
struct fac
{
    static const unsigned int result =
        n * fac<n - 1>::result;
};
//第二步,fac<0>的特别实现
template< >
struct fac<0>
{
    static const unsigned int result = 1;
};

int main()
{
    unsigned int result = fac<5>::result;
   
    printf("result = %d\n", result);
    return 0;
}


使用特权

评论回复
14
hotpower| | 2011-5-6 02:23 | 只看该作者
酒鬼的模板应该称为魔鬼了!

使用特权

评论回复
15
lxyppc| | 2011-5-6 09:05 | 只看该作者
我也插句嘴
用enum代替static const int的类型可以少写点代码

template<unsigned int n>
struct fac
{
    enum { result = n * fac<n - 1>::result };
};
//第二步,fac<0>的特别实现
template< >
struct fac<0>
{
   enum { result = 1};
};

使用特权

评论回复
16
X-Hawk|  楼主 | 2011-5-6 09:37 | 只看该作者
本帖最后由 hotpower 于 2011-5-13 00:43 编辑

15# lxyppc

说的相当贴切!
static const int 需要占用常量的储存空间的,
比如fac<3>, 它不但产生了 fac<3>::result这个常量,
还产生了其他3个常量
fac<2>::result
fac<1>::result
fac<0>::result

在优化做的不好的编译器里,后面3个常量都会编进程序里,占空间。


使用特权

评论回复
17
weshiluwei6| | 2011-5-6 22:53 | 只看该作者
牛啊

使用特权

评论回复
18
hotpower| | 2011-5-6 23:06 | 只看该作者
晕,是牛魔王!!!

使用特权

评论回复
19
cecwxf| | 2011-5-13 12:10 | 只看该作者
mark

使用特权

评论回复
20
hotpower| | 2011-5-18 23:42 | 只看该作者
顶起来

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

3

主题

380

帖子

3

粉丝