[Cortex-M0技术交流] 用C++模板计算波特率的值, PLL值

[复制链接]
 楼主| 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波特率。

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

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

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

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

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

  10.        baudrate_type:IV_X_EN,   //BAUD寄存器的DIV_X_EN栏位
  11.        baudrate_type:IV_X_ONE,  //BAUD寄存器的DIV_X_ONE栏位
  12.        baudrate_type:IVIDER_X,  //BAUD寄存器的DIVIDER_X栏位
  13.        baudrate_type::BRD         //BAUD寄存器的BRD栏位
  14.        );
 楼主| 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 编辑

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


  1. template<uint32_t clk, uint32_t baudRate, uint32_t div,
  2.     bool error_stop = !(div < 16)>
  3. class BaudRateT2
  4. {
  5. private:
  6.     enum
  7.     {
  8.         condition    = (((clk / baudRate) % (div + 1)) < 3)
  9.     };
  10.     typedef BaudRateT2<clk, baudRate, div + 1> next_type;
  11. public:
  12.     enum
  13.     {
  14.         error       = error_stop,               
  15.         DIV_X_EN    = 1,
  16.         DIV_X_ONE   = 0,
  17.         DIVIDER_X   = (condition ? div
  18.                     : next_type:IVIDER_X),
  19.         BRD         = (condition? (clk / baudRate / (div + 1) - 2)
  20.                     : next_type::BRD)
  21.     };
  22. };

  23. template<uint32_t clk, uint32_t baudRate, uint32_t div>
  24. class BaudRateT2<clk, baudRate, div, true>
  25. {
  26. public:
  27.     enum
  28.     {
  29.         error       = true,
  30.         DIV_X_EN    = 0,
  31.         DIV_X_ONE   = 0,
  32.         DIVIDER_X   = 0,
  33.         BRD         = 0
  34.     };
  35. };


  36. template<uint32_t clk, uint32_t baudRate>
  37. class BaudRateT1
  38. {
  39. private:
  40.     enum
  41.     {
  42.         condition   = !(clk / baudRate - 2 > 0xFFFF)
  43.     };
  44.     typedef BaudRateT2<clk, baudRate, 8> next_type;
  45. public:
  46.     enum
  47.     {
  48.         error       = (condition ? 0
  49.                     : next_type::error),
  50.         DIV_X_EN    = (condition ? 1
  51.                     : next_type:IV_X_EN),
  52.         DIV_X_ONE   = (condition ? 1
  53.                     : next_type:IV_X_ONE),
  54.         DIVIDER_X   = (condition ? 0
  55.                     : next_type:IVIDER_X),
  56.         BRD         = (condition ? (clk / baudRate - 2)
  57.                     : next_type::BRD)
  58.     };
  59. };

  60. template<uint32_t clk, uint32_t baudRate>
  61. class BaudRateT0
  62. {
  63. private:
  64.     enum
  65.     {
  66.         condition   = ((clk / baudRate) % 16 < 3)
  67.     };
  68.     typedef BaudRateT1<clk, baudRate> next_type;
  69. public:
  70.     enum
  71.     {
  72.         error       = (condition ? 0
  73.                     : next_type::error),
  74.         DIV_X_EN    = (condition ? 0
  75.                     : next_type:IV_X_EN),
  76.         DIV_X_ONE   = (condition ? 0
  77.                     : next_type:IV_X_ONE),
  78.         DIVIDER_X   = (condition ? 0
  79.                     : next_type:IVIDER_X),
  80.         BRD         = (condition ? (clk / baudRate/16 - 2)
  81.                     : next_type::BRD)
  82.     };
  83. };

  84. template<uint32_t clk, uint32_t baudRate>
  85. class BaudRateT
  86. {
  87. private:
  88.     typedef BaudRateT0<clk, baudRate> next_type;
  89. public:
  90.     enum
  91.     {
  92.         error       = next_type::error,
  93.         DIV_X_EN    = next_type:IV_X_EN,
  94.         DIV_X_ONE   = next_type:IV_X_ONE,
  95.         DIVIDER_X   = next_type:IVIDER_X,
  96.         BRD         = next_type::BRD,
  97.         REG_VALUE   = ((uint32_t)BRD)
  98.                     + ((uint32_t)DIVIDER_X << 24)
  99.                     + ((uint32_t)DIV_X_ONE << 28)
  100.                     + ((uint32_t)DIV_X_EN << 29)
  101.     };
  102. };


  103. X-Hawk




X-Hawk
 楼主| X-Hawk 发表于 2011-5-3 18:56 | 显示全部楼层
本帖最后由 hotpower 于 2011-5-13 00:42 编辑

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

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

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

hotpower 发表于 2011-5-3 19:00 | 显示全部楼层
酒鬼最近醉拳打的不错!很给力!
看来俺也要复习和学习了,否则被拉的太远了。
dong_abc 发表于 2011-5-3 19:38 | 显示全部楼层
本帖最后由 dong_abc 于 2011-5-3 19:41 编辑

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

哈哈,普通人来观光:lol
murex 发表于 2011-5-3 20:50 | 显示全部楼层
酒鬼就是XX
 楼主| X-Hawk 发表于 2011-5-4 01:39 | 显示全部楼层
本帖最后由 hotpower 于 2011-5-4 21:11 编辑

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

一、基本写法:
模板用于实现某个类型或函数,将其中用到的类型或数值量,用参数替换。
例如函数:
  1. template<class T>  //代码1
  2. T max(T a, T b)
  3. {
  4.     return (a > b ? a : b);
  5. }
例如类型:
  1. template<class T1, class T2>  //代码2
  2. class some_type
  3. {
  4.     T1 element1;
  5.     T2 element2;
  6. };
嗯,地球人都能看明白,就是替换嘛。嗯,就是替换。
像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模板
  1. template<class T1, class T2 = long>  //代码3
  2. class obj
  3. {
  4.     T1 element1;
  5.     T2 element2;
  6. };
然后写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>写一个特殊的类即可。
  1. template<>  //代码4
  2. class obj<int, long>
  3. {
  4.     char c;
  5. };
加上这部分实现的后,对于<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>的特殊的类即可。
  1. template<class T>  //代码5
  2. class obj<T, long>
  3. {
  4.     short *ptr;
  5. };
加上这部分实现的后,对于<某类型, 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哦。


tlb 发表于 2011-5-4 05:34 | 显示全部楼层
没用过C++
标记下
hotpower 发表于 2011-5-4 10:40 | 显示全部楼层
讲的很明了。谢谢了
dong_abc 发表于 2011-5-4 12:13 | 显示全部楼层
都是强人啊,仰望一下,帽子掉了。:hug:
hotpower 发表于 2011-5-5 14:45 | 显示全部楼层
有表格吗?查表也不错。
 楼主| 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就对了,

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

  2. //第一步,对于任何的n的实现
  3. template<unsigned int n>
  4. struct fac
  5. {
  6.     static const unsigned int result =
  7.         n * fac<n - 1>::result;
  8. };
  9. //第二步,fac<0>的特别实现
  10. template< >
  11. struct fac<0>
  12. {
  13.     static const unsigned int result = 1;
  14. };

  15. int main()
  16. {
  17.     unsigned int result = fac<5>::result;
  18.    
  19.     printf("result = %d\n", result);
  20.     return 0;
  21. }


hotpower 发表于 2011-5-6 02:23 | 显示全部楼层
酒鬼的模板应该称为魔鬼了!
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};
};
 楼主| 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个常量都会编进程序里,占空间。


weshiluwei6 发表于 2011-5-6 22:53 | 显示全部楼层
牛啊
hotpower 发表于 2011-5-6 23:06 | 显示全部楼层
晕,是牛魔王!!!
cecwxf 发表于 2011-5-13 12:10 | 显示全部楼层
hotpower 发表于 2011-5-18 23:42 | 显示全部楼层
顶起来
您需要登录后才可以回帖 登录 | 注册

本版积分规则

3

主题

380

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部