OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 55884|回复: 143

开始做实验了,C++版本的STM32测试代码会逐步上传

[复制链接]

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
发表于 2012-3-13 16:12:36 | 显示全部楼层 |阅读模式

代码会在以后做完实验逐步上传,功能会和不完全手册上的实验的一致,
对有兴趣的新手也是另一种选择,欢迎交流讨论~

虽然C++的工程模板不算完全做好,不过剩下的也只是拓展性的工作和一些小的改动了...
工程里面用的是3.5版本的库的头文件(所以就不要使用mdk默认给的那个<stm32f10x_lib.h>了...),
但是3.5版的头文件的一些内容被我修改了...(PS:昨天被头文件的各种包含关系弄得晕死了...)
从stm32f10x.h里面分离出了寄存器结构体的定义,放到mylib文件夹下的reg_Typedef.h里去了.

直接操作寄存器对我来说比较困难,我的习惯是在学习的过程中积累自己的封装库,
主要以原子哥的不完全手册为参考,不过代码和原来的C语言的例程会有些区别

第一个是LED跑马灯实验。主函数的代码比较简单,我就直接贴上来了,当然还需要其它的代码来支持的,不直接贴了,在附件里

(原始版本的代码不贴在这里了。。。大家有兴趣的看附件。。。)

-----------------------------------------------------------------------------------------------------------------------------------------------------

2012-4-19 日更新代码。。。(  stm32_cppTest-01跑马灯 (2) )    
(PS:后面发的代码有些不能和以前的兼容啊。。。不好意思。。。以后完善了我会整理出完整的版本。。。)


#include "sys.h"

 

using namespace periph;

 

int main(void) 

{

  sys.Init();

rcc.PeriphControl(gpioa, ENABLE); // 使能 GPIOA

rcc.PeriphControl(gpiod, ENABLE); // 使能 GPIOD

pa8.Config(OUTPUT, PUSH_PULL);    // 等价 gpioa.Config(Pin8, OUTPUT, PUSH_PULL)

pd2.Config(OUTPUT, PUSH_PULL);

pa8 = 1;

while(1){

Delay_ms(100);

pa8 = !pa8;

pd2 = !pd2;

}

}


stm32_cppTest - 跑马灯.zip

448.79 KB, 下载次数: 3111

stm32_cppTest-01跑马灯 (2).zip

257.56 KB, 下载次数: 2259

https://github.com/roxma
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

0

主题

4

帖子

0

精华

新手入门

积分
24
金钱
24
注册时间
2012-3-13
在线时间
0 小时
发表于 2012-3-13 16:33:46 | 显示全部楼层
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165516
金钱
165516
注册时间
2010-12-1
在线时间
2116 小时
发表于 2012-3-13 16:35:52 | 显示全部楼层
呵呵,继续加油。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-3-13 23:11:28 | 显示全部楼层

按键输入实验,
程序的比较不同的地方,是把按键扫描程序分离出读按键和消抖部分。
在学习51的时候只看过有人用定时器去做按键扫描,用一种叫状态机的方式(可参考马潮的AVR资料),
自己一直没实验过,所以就写一次,不过程序里没有用到定时器,只是思想是类似的,要移植到定时器上也比较简单。

其实感觉这个还是属于C语言的方式,程序比较简单,要用面向对象反而会比较麻烦,
只是想在学习这些东西的时候,慢慢把寄存器的封装给完善起来...

主要代码如下:
#include "sys.h"
#include "led.h"
#include "key.h"

void KeyDown(const u16& key); //按键按下的处理函数

int main(void)
{
 Stm32_Clock_Init(9);        //系统时钟设置,外部晶振为8M,9倍频后为8*9=72M主频
 Delay_Init(72);    //延时初始化

 using namespace led;
 using namespace key;

 //如果不加前缀的话会报错的哦,
 //因为led和key命名空间里也有这个函数,编译器不知道你想调用的是哪个
 led::Init();
 key::Init(); 
 Switch_1(false);
 Switch_0(false);

 u8 stat = 0;
 u8 key = 0; 
 u8 read = 0;
 u16 cnt = 0;
 while(1)
 {
  read = GetKey();
  switch(stat)
  {
   case 0:  //初始状态,判断有没有按键被按下
   {
    if(read != 0)
    {
     key = read;
     stat++;
    }
   }break;
   case 1: //20ms后第二次检验
   {
    if(read == key){
     stat++; cnt = 0;
    }
    else if(++cnt == 3){ //一段时间后检测不对, 
     stat = 0; cnt = 0; //重新检测
    } 
   }break;
   case 2: //执行动作
   {
    KeyDown(key);
    stat++;
   }break;
   case 3: //等待松手
   {
    if(read == 0) //因为这个程序没必要支持组合按键和按键保持计数,所以就简单处理了
     stat = 0;
   }break;
   default:break;
  }
  Delay_ms(20);
 } 
}

void KeyDown(const u16& key)
{
 using namespace led;
 if(checkb(key, Bit(0)))
 {
  Toggle_0();
 }
 else if(checkb(key, Bit(1)))
 {
  Toggle_1();
 }
 else if(checkb(key, Bit(2)))
 {
  Toggle_1();Toggle_0();
 }
}

stm32_cppTest - 按键输入.zip

482.09 KB, 下载次数: 2144

https://github.com/roxma
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-3-13 23:13:17 | 显示全部楼层
回复【3楼】正点原子:
---------------------------------
嗯,我在努力啊~


最近的学习任务...亚力山大啊。。。
https://github.com/roxma
回复 支持 反对

使用道具 举报

39

主题

597

帖子

2

精华

金牌会员

Rank: 6Rank: 6

积分
2115
金钱
2115
注册时间
2011-9-3
在线时间
121 小时
发表于 2012-3-13 23:14:39 | 显示全部楼层
话说我毕业设计也用到C++,当时用VC6.0写上位机,用好VC还是挺有挑战性的。
回复 支持 反对

使用道具 举报

39

主题

597

帖子

2

精华

金牌会员

Rank: 6Rank: 6

积分
2115
金钱
2115
注册时间
2011-9-3
在线时间
121 小时
发表于 2012-3-13 23:15:40 | 显示全部楼层
不过在STM32上用C++,好像不太发挥C++的功效,个人感觉!
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-3-13 23:23:41 | 显示全部楼层
回复【7楼】螃蟹爱虫:
---------------------------------
嗯,确实是
不过对于以后拓展文件系统会有比较大的帮助
不知你有没有看过这个连接里下载的代码,确实是很强大的,用C++写的
http://andybrown.me.uk/ws/2011/12/28/stm32plus-a-c-library-for-stm32-development/  

MFC在大一的C++课的最后一次实验的时候写了个比较大的程序当普通C++作业交了。。。(当时别人的程序都要用手来抄到纸上再上交啊,我的代码太长就免了,爽啊~)
后来写过一点小程序,之后就比较少用到了,MFC很强大,不过想学精的话真的不容易啊,
对于PC上的大开型软件,估计Java会合适点吧,还有现在的.net。。。
不过我们搞嵌入式的就比较少去碰这些了...用MFC已经够了
https://github.com/roxma
回复 支持 反对

使用道具 举报

12

主题

216

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
313
金钱
313
注册时间
2011-4-7
在线时间
3 小时
发表于 2012-3-14 00:56:52 | 显示全部楼层
mark
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-3-14 13:26:38 | 显示全部楼层
 
先不急着做下面的实验了,我之前说C++的好,这里可能比较难看得出C++会好在哪里
昨晚睡觉前想了一下,怎么才能利用C++的语法为程序提供便利,
就拿LED来说,其实LED都有个共性,就是通过控制某个引脚的电平,来控制LED的亮和灭,

面象对象的一个很重要的优点就是代码的复用,可是,我这里要怎么才能利用这些优点呢?
一开始我是想到这个方案,
做一个LED类,初始化的时候给LED对象初始化它的对应的IO端口的指针和对应的引脚号,
这样的话,似乎是比较好的方案,因为它可以在任何一个STM32板上轻松的移植,
不过问题就出在,LED的引脚号会被系统认为是变量啊。。。
而我的配置口的函数要求参数不可以是变量,否则,生成的代码量将会不堪设想啊。。。
(只可惜这里的C++好像没有限制某些参数必须是编译期常量的方法,现在PC机上的C++好像有这个了),

以前写的函数已经不想去改动了,于是我就想了另一种方案,用模板,类模板,


我做过简单的实验后发现,这样的两个类是允许存在的,
class A
{public:enum {HAHA}};
class B
{public:enum {HAHA} };
访问虽然名字是相同的,但是访问这两个值的时候分别要用A::HAHA 和 B::HAHA来写,
然后我就乐了,因为,我打算使用类模板的时候就需要这样的常量,
为什么不用const 或static const ? 印象中好像是要消耗空间的(未验证过),
但是enum不同,它不是一个变量,是一个数据类型。

好,那么现在可以对LED的共性进行抽取,对于两个LED,有哪些可能是不同的呢?
有端口号,有引脚号,好像就没其它的了,但是为了通用起见,还有一个就是点亮LED的电平,有的可能是高,有的可能是低。

那么就开始写模板了,为简单起见,我只实现LED开关的控制的共性抽取,
初始化函数还是使用之前在按键实验的时候写的LED函数,具体的实现如下:
#include "sys.h"
#include "led.h"

//LED0 的个性信息

class CLED0_INFO
{
public:
 enum
 { 
  ORT = (uint32_t)GPIOA,  //端口
  IN = io:in8,    //引脚
  LIGHT_LEVEL = 0    //发光的电平
 };
};

//LED1 的个性信息

class CLED1_INFO
{
public:
 enum
 { 
  ORT = (uint32_t)GPIOD,  //端口
  IN = io:in2,    //引脚
  LIGHT_LEVEL = 0    //发光的电平
 };
};

//LED模板

template <class T>
class CLED
{
 public:
 static finline void Switch(bool x)
 {
  ((GPIO_TypeDef*)(T:ORT))->Write((io:inSource)(T:IN),x?(bool)(T:IGHT_LEVEL):!(T:IGHT_LEVEL));
 }
};

extern CLED<CLED0_INFO> led0;
extern CLED<CLED1_INFO> led1;

int main(void)
{
 Stm32_Clock_Init(9);        //系统时钟设置,外部晶振为8M,9倍频后为8*9=72M主频
 Delay_Init(72);    //延时初始化

 using namespace led;

 //如果不加前缀的话会报错的哦,
 //因为led和key命名空间里也有这个函数,编译器不知道你想调用的是哪个
 led::Init(); 
 
 while(1)
 {
  led1.Switch(false);
  led0.Switch(true);
  Delay_ms(100);
  led0.Switch(false);
  led1.Switch(true);
  Delay_ms(100);
 }
}

这样一来,仔细看看代码,以前,
我需要写两个点亮LED的函数,Switch_0 Switch_1
现在,我只需要分别提供两个LED个性相关的类,而其它的共性的内容,全部由模板来帮我完成了,
这样做,不就比copy代码修改代码省事儿多了吗?


stm32_cppTest_LED类模板.zip

481.2 KB, 下载次数: 1998

https://github.com/roxma
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-3-14 13:28:42 | 显示全部楼层
回复【10楼】Pony279:
---------------------------------
另外,Switch函数经过测试,生成的代码量和原来的是没什么两样的
也就是说,我既保证了效率,又保证了通用性。

而且,同样的思想,一样可以应用到按键中去
我比较懒,加上最近功课的学习有点吃力,就不写了,网友有兴趣的话帮我补充完整哈
https://github.com/roxma
回复 支持 反对

使用道具 举报

39

主题

597

帖子

2

精华

金牌会员

Rank: 6Rank: 6

积分
2115
金钱
2115
注册时间
2011-9-3
在线时间
121 小时
发表于 2012-3-14 13:44:41 | 显示全部楼层
回复【8楼】Pony279:
---------------------------------
是的,当时是用MFC。如果能力强的,用好MFC真的很有赚钱来头。。
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-3-14 17:34:47 | 显示全部楼层

上传经过9楼的方案完全模板化后的LED例程

至于模板的优点,我打个比方,
现在实验板的引脚PA2引出来驱动面包板上的LED,我现在要做3个LED的实验,
那么,我只需要再在led.h头文件里加上以下声明,(直接从已有的例子中复制过来,稍作修改即可)
 class CLED2_INFO
 {
 public:
  enum{ 
   ORT = (uint32_t)GPIOA,   //端口
   IN = io:in2,     //引脚
   LIGHT_LEVEL = 0,    //使LED发光的电平为低电平
   OUTPUT_STYLE = io::OPEN_DRAIN //引脚开漏输出
  };
  static void finline Port(FunctionalState e) {RCC->ortA(e);} //对应的端口使能
 };

extern CLED<CLED2_INFO> led2;

这样,我已经不需要再去纠结LED的驱动代码怎么写,就可以在主函数代码里直接写

led2.Init();    //初始化2号LED
led2.Switch(true)    //点亮2号LED
led2.Toggle()    // 2号LED取反
就是这么简单

stm32_cppTest-LED完全模板.zip

482.18 KB, 下载次数: 2015

https://github.com/roxma
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165516
金钱
165516
注册时间
2010-12-1
在线时间
2116 小时
发表于 2012-3-14 22:22:43 | 显示全部楼层
回复【13楼】Pony279:
---------------------------------
你搞这么多函数,别到时候自己都不记得自己的函数怎么用了....
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-3-14 23:05:46 | 显示全部楼层
回复【14楼】正点原子:
---------------------------------

对于LED其实只有上面写的Init , Switch 和 Toggle 三个是常用的

至于怎么添加声明,如果是写给别人使用的,确实是应该有说明文档的,

但是LED是一个比较简单的东西,就没必要那么规范了。
只是作为模板的一个示例罢了,多少还是有一点实际意义的,
如果板上有四个LED而且又不是在连续的引脚上,
那么要写四个点亮LED的函数和四个LED取反的函数的话确实是比较枯燥的

不过其实至于液晶啊SD啊一般一个板上也就只有一个,用这种方法就意义不大了,
硬要说好处的话只能说是把使用需要提供的信息明确地集中到一个类里去了,只要用户提供不了,就不能编译成功。

C++的模板可以专门用一本书来讲的,只是一直没时间去看这一类书籍...
https://github.com/roxma
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-3-15 21:32:39 | 显示全部楼层
串口实验,为简单起见,先只做串口发送的 hello world程序了
不完全手册上的串口实验是有串口接收的功能的,
要做串口接收的话我还得搞下NVIC_TypeDef 的封装,以后的事了。

对之前的命名有一点改动,删了几个GPIO的没必要的函数,不过应该是兼容之前的程序的

比较少独立写驱动,所以这次就独立看参考手册来写驱动了,
其实还是比较简单的,因为参考手册上面已经把操作的流程全部都写在那里了

有个地方我比较郁闷啊。。。
想不明白波特率发生器分什么mantissa和fraction...按照给的公式


16*USARTDIV 不就是把 BRR ( baud rate register ) 寄存器的值当作整数看的结果吗。。。

难道这么区分只是为了说明这个容忍偏差吗???。。。。实在是有点无语啊。。。



串口发送的程序的主函数比较简单。。。就直接贴上来了,
#include "sys.h"

int main(void)
{
 Stm32_Clock_Init(9);        //系统时钟设置,外部晶振为8M,9倍频后为8*9=72M主频
 Delay_Init(72);    //延时初始化

 using namespace periph;

 RCC->ortA(ENABLE);        // PA使能
 GPIOA->Config(Pin9, AF_OUTPUT, PUSH_PULL);  // 串口输出脚配置
 RCC->Usart1(ENABLE);       // 使能串口
 USART1->Config(9600);       // 配置串口

 while(1)
 {

  USART1->Write("Hello world!\n");    //串口发送字符串
  Delay_ms(1000);
 }
}

Config函数里面有比较多的默认参数,比如默认时钟就是72M的,默认开发送器...
因为波特率的设置太过自由,以后可能要加上返回和实际误差值的功能,或者如果误差大于某个值,就返回失败也行。。。

stm32_cppTest-串口发送实验.zip

486.39 KB, 下载次数: 1853

https://github.com/roxma
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-3-15 22:00:20 | 显示全部楼层

突然想起C++的cout,呵呵,
就做个类似的,重载 << 操作符

主函数和原来差不多
#include "sys.h"

int main(void)
{
 Stm32_Clock_Init(9);        //系统时钟设置,外部晶振为8M,9倍频后为8*9=72M主频
 Delay_Init(72);    //延时初始化

 using namespace periph;

 RCC->ortA(ENABLE);        // PA使能
 GPIOA->Config(Pin9, AF_OUTPUT, PUSH_PULL);  // 串口输出脚配置
 RCC->Usart1(ENABLE);       // 使能串口
 USART1->Config(9600);       // 配置串口

 while(1)
 {
  usart1 <<"Welcome to Openedv!\r\n"
    <<"欢迎来到开源电子网!\r\n"
    <<"www.openedv.com\r\n"; 
  Delay_ms(1000);
 }
}

具体实现看MYLIB文件夹下的USART_TypeDef.cpp文件里的内容

貌似没啥好写的,顺手上个图好了。。。



 

stm32_cppTest-串口发送-运算符重载.zip

486.82 KB, 下载次数: 1889

https://github.com/roxma
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-3-15 22:11:30 | 显示全部楼层
另外,关于9楼,10楼,12楼的LED类模板,
其实感觉需要提供的声明还是挺长的,每次都写这么长,实在有点麻烦,其实有些一般是可以默认的
比如LED发光的电平,配置IO口的模式啊。。。

所以可以利用继承的方式,在15楼16楼的代码里已经有做做这个改变了,具体的可以自己参考。

还有一点要注意的就是,如果编译器的优化等级设置到最低,那么led的对象是不能直接使用的,

虽然声明了extern,而且只用静态函数。。。但是优化等级低了编译器就比较傻B了。。。

如果非要这么设置,可以在任意一个cpp文件里定义一个led0, led1 ... 的全局变量就行了。
https://github.com/roxma
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165516
金钱
165516
注册时间
2010-12-1
在线时间
2116 小时
发表于 2012-3-15 23:35:31 | 显示全部楼层
虽然另类,但是楼主的坚持和分享,值得称赞.本帖加精!继续努力.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-3-17 02:37:28 | 显示全部楼层

回复【19楼】 正点原子 :
---------------------------------
谢谢原子哥的鼓励!

----------------------------------------------------------------------------

这个实验是串口接收中断实验,
为了简单起见,我就没有做缓存了,只是接收到一个字符,就回应电脑接收到了什么字符。。。

调程序的时候遇到了点小问题,在配置 SCB_AIRCR寄存器的时候一定不要忘了写Register Key...
Bits 31:16 VECTKEYSTAT[15:0]/ VECTKEY[15:0]  Register key
Reads as 0xFA05
On writes, write 0x5FA to VECTKEY, otherwise the write is ignored.

有原子哥的例程调起程序来也方便啊,那个问题后直接就对照例程看了,发现错误,立刻就改过来了。

对原先的代码做了一些改动,比如说macro_functions.h里的一些已经没有用的宏(之前已经改成内联+模板实现了)被我删了,本来不想改的,不过感觉真的有点碍眼,
就删了。。。
对命名空间也做了一点调整,但不是什么大问题,
串口的config函数调整了,参数列表里取消了打开串口发送器的选项,需要使用另外的函数,这一点和上一个串口发送实验稍微有点不同了。

本来早应该调了的程序了,电脑莫明其妙就蓝屏了,,,然后好长一段时间都不能正常启动。。。搞得我还以为要重装系统了。。。

还是只贴上主函数代码,底层的代码有兴趣的就自己看了。
顺手写了个endl函数,呵呵。
标准C++的iostream实现起来比较复杂,一大堆模板,,,水平有限,我搞不定,,,直接用函数重载做个简单的能用的就行了。。。

#include "sys.h"

int main(void)
{
 Stm32_Clock_Init(9);        //系统时钟设置,外部晶振为8M,9倍频后为8*9=72M主频
 Delay_Init(72);    //延时初始化
 
 using namespace periph;

 scb.SetPriorityGrouping(p4Grp4Sub);            // 中断分组方式,4个组优先级,4个子优先级
 nvic.Config(USART1_IRQn, ENABLE, 0, 0);    // 内核中使能串口中断。
 
 rcc.Enable(IOPAEN);        // PA使能
 gpioa.Config(Pin9, AF_OUTPUT, PUSH_PULL);  // 串口输出脚配置
 gpioa.Config(Pin10, INPUT);      // 串口接收脚
 rcc.Enable(USART1EN);        // 使能串口
 usart1.Config(9600);         // 配置串口的参数
 usart1.Transmitter(ENABLE);           // 使能串口发送
 usart1.Receiver(ENABLE);        // 使能串口接收
 usart1.Interrupt(ReceivedInt_OverRunErrInt, ENABLE); //使能接收中断

 while(1)
 {
  usart1 <<"Welcome to Openedv!"<<endl
    <<"欢迎来到开源电子网!"<<endl
    <<"www.openedv.com"<<endl
    <<"^_^"<<endl<<endl;
  Delay_ms(1000);
 }
}

ARMAPI void USART1_IRQHandler(void)
{
 using namespace periph;
 char res;    
 if(usart1.Received())//接收到数据
 { 
  res= usart1.Read();
  usart1 << "The character you send is "
    << res<<endl;                  
 }             
}

stm32_cppTest-07串口发送-接收实验.zip

433.51 KB, 下载次数: 1721

https://github.com/roxma
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-3-19 14:21:18 | 显示全部楼层
这几天比较忙啊,
现在还是功课比较重要,
所以以后会放缓做实验和上传代码的速度,争取一周一次吧。。。
昨晩赶作业到2点多啊,还发现好多不会做,都惭愧死了。。。

下一次上传代码可能不是新实验,
打算再定义一些东西,方便使用,如
pa0, pa1, pa2, ..., pb0, pb1, pb2, ...,
然后重载 >> 和 << 操作符
比如
bool a;
pa0 >> a;
把引脚pa0的值写到变量a
pa1<<a;
把a的值写到 pa1

...
周末再来吧,貌似不多人对这个感兴趣啊。。。>
https://github.com/roxma
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-3-24 23:58:41 | 显示全部楼层
今天比较悲剧,所以比较有空,呵呵,我就来更新程序了,这个实验还是关于串口的,

之前那个串口接收的没有做缓冲区,用起来不方便,
但是做缓冲区又想考虑通用性,然后我就搞了个简陋的类似 cin, cout 的输入输出流,专门用来做文本的输入输出的,
不过我发现通用性强了,效率就会降低了,
一切皆有代价嘛,就看值不值了,如果只是输出做为文本显示的话,损失点效率确实没什么,
如果是像文件系统那般带有大量数据传输的,就比较悲剧了。

代码的还有一个改变就是定义了pa0, pa1, ..., pa15, pb0, pb1, ...
然后如果要定义pa0为输出的话,就可以直接写
pa0.Config(OUTPUT);    //呵呵,用起来还是比较方便的。

然后就是主函数代码了,底层代码我依然是不想贴~我在这里把不同的地方加粗了。

#include "sys.h"
#include "IOStream.h"

int main(void) 
{
sys.Init();
using namespace periph;

scb.SetPriorityGrouping(GRP4_SUB4);

nvic.Config(USART1_IRQn, ENABLE, 0, 0);
rcc.Enable(IOPAEN); // PA使能

pa9.Config(AF_OUTPUT, PUSH_PULL); // 串口输出脚配置
pa10.Config(INPUT); // 串口输入

rcc.Enable(USART1EN); // 使能串口

usart1.Config(9600); // 配置串口的参数
usart1.Transmitter(ENABLE);   // 使能串口发送

usart1.Receiver(ENABLE); // 使能串口接收
usart1.Interrupt(ReceivedInt_OverRunErrInt, ENABLE); // 使能接收中断
char str[20];
while(1)
{
output <<"\n请输入一个单词吧 :";
input >>str;
output <<"\n你输入的单词是:"<<str<<'\n';

}
}

然后是软件仿真的结果,实际测试也是通过的:



另外一点,题外话,MDK的configuration wizard 好有爱啊,我在我的 iostream.cpp 里有这一段代码:


如果我点了上面的 configuration wizard,就可以图形化地进行配置了,好好玩啊~


原理是什么?呵呵,请点击上面的help键,
咱们搞技术的,一定要会看文档。

stm32_cppTest-08简单的输入输出流.zip

440.34 KB, 下载次数: 1721

https://github.com/roxma
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-3-29 13:39:10 | 显示全部楼层
这个实验是外部中断实验,比较简单,看原子哥的不完全手册就懂了,我也不需要解释了,

直接贴代码,初始化流程应该是一目了然的

#include "sys.h"
#include "led.h"

using namespace periph;
using namespace led;

// 外部中断0 服务程序 
ARMAPI void EXTI0_IRQHandler(void) 
{
Delay_ms(10); // 消抖
if(pa0 == 1)
{
led0.Toggle();
led1.Toggle();
exti.ClearPending(Line0); 

ARMAPI void EXTI15_10_IRQHandler(void)
{
Delay_ms(10); // 消抖
if(pa13 == 0)
{
led0.Toggle();
}
else if( pa15 == 0)
{
led1.Toggle();
}
exti.ClearPending(Line13|Line15); 
}

int main(void) 
{
sys.Init(); // 系统初始化
using namespace periph;

// scb.SetPriorityGrouping(GRP4_SUB4); // 这个在sys.Init()里已经做了初始化了

nvic.Config(EXTI0_IRQn, ENABLE);
nvic.Config(EXTI15_10_IRQn, ENABLE);

rcc.Enable(IOPAEN); // 开PA时钟
rcc.Enable(AFIOEN); // 开AFIO时钟

afio.Map(EXTI_LINE0, PORTA);
afio.Map(EXTI_LINE13, PORTA);
afio.Map(EXTI_LINE15, PORTA);

pa0.Config(INPUT, PULL, DOWN); // PA0下拉输入
pa13.Config(INPUT, PULL, UP); // PA13上拉输入
pa15.Config(INPUT, PULL, UP);

exti.FallingTrigger(Line13 | Line15, ENABLE); //下降沿中断
exti.RisingTrigger(Line0, ENABLE);//上升沿中断
exti.Interrupt(Line0 | Line13| Line15, ENABLE); //开启EXTI0, EXTI13, EXTI15的中断

led0.Init(); // LED初始化
led1.Init(); // LED初始化
while(1)
{

}
}


stm32_cppTest-09外部中断实验.zip

470.28 KB, 下载次数: 1827

https://github.com/roxma
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-4-2 13:55:30 | 显示全部楼层
这个实验是独立看门狗实验,功能和原子哥的《STM32不完全手册》中的独立看门狗实验描述的差不多。

复位后延时1s,然后点亮LED,启动看门狗,如果0.8s内没有按KEY2(外部中断0,PA0),看门狗就会把片子复位。
所以如果不停的按KEY2(外部中断0,PA0),在外部中断0的中断函数里会不停的喂狗,所以灯会一直亮。否则,灯会闪烁


因为上一个实验做了外部中断实验,所以就直接用外部中断来读按键了。
贴上代码:

#include "sys.h"
#include "led.h"

using namespace periph;
using namespace led;

// 外部中断0 服务程序, KEY2 
ARMAPI void EXTI0_IRQHandler(void) 
{
Delay_ms(10); // 消抖
if(pa0 == 1)
{
iwdg.Feed();
exti.ClearPending(Line0); 

int main(void) 
{
sys.Init(); // 系统初始化
using namespace periph;

///////////外部中断的初始化///////////

nvic.Config(EXTI0_IRQn, ENABLE);

rcc.Enable(IOPAEN); // 开PA时钟
rcc.Enable(AFIOEN); // 开AFIO时钟

afio.Map(EXTI_LINE0, PORTA);

pa0.Config(INPUT, PULL, DOWN); // PA0下拉输入

exti.RisingTrigger(Line0, ENABLE);
exti.Interrupt(Line0 , ENABLE); //开启EXTI0, EXTI13, EXTI15的中断

//////////////////////////////////////

led0.Init(); // LED0初始化
led1.Init(); // LED1初始化
Delay_ms(1000); //延时1s点亮LED
led0.Switch(ENABLE);
led1.Switch(ENABLE);
// 重载值 0xfff, 对40kHz进行8分频,在LIS理想40kHz下,每0.8ms需要喂狗一次。
iwdg.Config(0xfff, 8);
iwdg.Start();

while(1)
{}
}

上传工程代码:




stm32_cppTest-10独立看门狗实验.zip

473.64 KB, 下载次数: 1651

https://github.com/roxma
回复 支持 反对

使用道具 举报

6

主题

30

帖子

0

精华

初级会员

Rank: 2

积分
86
金钱
86
注册时间
2011-7-22
在线时间
1 小时
发表于 2012-4-3 19:30:02 | 显示全部楼层
希望你以后要是写程序的话要规范,不然你以后会吃大亏的!!真的!
https://gzdtxht.taobao.com/shop/view_shop.htm?spm=a1z09.1.a1zvx.d53.lMB0kW&mytmenu=mdianpu&user_numb
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-4-3 20:48:56 | 显示全部楼层
回复【25楼】电子爱好平凡:
---------------------------------
请问楼上能不能具体指出我现在存在的问题?谢谢!
https://github.com/roxma
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165516
金钱
165516
注册时间
2010-12-1
在线时间
2116 小时
发表于 2012-4-3 21:03:16 | 显示全部楼层
楼上的意思是你的代码适合自己玩,而不适合推广.
也就是说,你的代码除了自己在用,基本没有别人会用的了.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-4-3 21:23:15 | 显示全部楼层
回复【27楼】正点原子:
---------------------------------

确实用C++的人不多,
就算有的,在网上开源的,少之又少。
而且我看到的基本上是基于官方库用C++做上层应用的,
摒弃官方库用C++的,除了我,在AM论坛只见过一个,

不过我还是打算一边学习一边积累下去的,
现在这点代码确实没有什么的,一点一点积累,时间长了,也是一个系统。
https://github.com/roxma
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165516
金钱
165516
注册时间
2010-12-1
在线时间
2116 小时
发表于 2012-4-3 21:53:15 | 显示全部楼层
呵呵.也不错.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-4-4 17:35:52 | 显示全部楼层
这次不是新的实验,还是按键中断,提一个使用比较方便的地方,pa0, pa1, ..., 之类的定义吧,
之前类型转换操作符没重载好,编译器给我报个错说什么cv-qualifiers...不懂,刚刚把两个成员函数改成静态的,就编译成功了,

具体的实现的使用方法是这样的:
bool a = pa0;       //读PA0引脚的电平
pa1 = a;            //PA1脚输出a。

这种简单的IO操作,在51上简直就是家常便饭
因为STM32的读和写是分开两个寄存器的,想把读和写操作做到像51那样方便,
统一成一个名字,在C语言下确实有点无能为力了,
而C++提供了一种运算符重载的机制,就可以很好的解决这个问题,
于是我就在代码里定义了pa0 ---- pd15 的引脚,
每个引脚都可以像上面那样使用,还算是比较方便的了,
当然,如果一个引脚不是配置成开漏
(外接上拉)的方式, (如) a = pa0;  pa0 = a;  这两种使用方式是不允许同存在的。

实现机制并不难,核心还是运算符重载,
比如上面的 bool a = pa0 就是利用了重载的 bool()操作符,
而下一句 pa1 = a 就是利用了重载的 = 操作符
因为每个引脚的操作都是有很多共同的特点的,所以我使用了模板来实现。

我把实现的代码简要的写出来,完整的代码可以自己下载来参考。

// 用来给模板提供端口信息的类
class CPort_InfoA{public: enum{PORT = (uint32_t)GPIOA}; };
class CPort_InfoB{public: enum{PORT = (uint32_t)GPIOB}; };
//用来给模板提供引脚号信息的类
class CPin_Info0{public: enum{PIN = Pin0}; };
class CPin_Info1{public: enum{PIN = Pin1}; };

template<class CPort_Info, class CPin_Info>
class CPin
{
protected:
// 注意一定要定义为static的,否则 bool() 运算符重载编译不通过。
static finline GPIO_TypeDef* GetPort() {return (GPIO_TypeDef*)(CPort_Info:ORT);}
static GPIO_Pin GetPin() {return (GPIO_Pin)(CPin_Info:IN);}
public:
void finline Config(const GPIO_Input input, const InputStyle style = PULL,const InputPull pull_way = UP)
{GetPort()->Config(GetPin(), input, style, pull_way);}
void finline Config(const _Output output, const OutStyle style = OPEN_DRAIN,const OutSpeed speed = SPEED_10M )
{GetPort()->Config(GetPin(), output, style, speed);}


finline CPin& operator <<(bool val) {GetPort()->Write(GetPin(), val);return *this;}
finline CPin& operator =(bool val) { GetPort()->Write(GetPin(), val);return *this; }

// 读引脚,如果引脚为高电平,则读到非零值,否则读到0
finline CPin& operator >>(bool& val) {val = GetPort()->Read(GetPin());return *this;}
finline CPin& operator >>(int& val) {val = GetPort()->Read(GetPin());return *this;}
finline CPin& operator >>(u16& val) {val = GetPort()->Read(GetPin());return *this;}
finline CPin& operator >>(u32& val) {val = GetPort()->Read(GetPin());return *this;}
finline operator bool() const {return GetPort()->Read(GetPin());}
};

extern CPin<CPort_InfoA, CPin_Info0> pa0;
extern CPin<CPort_InfoA, CPin_Info1> pa1;

extern CPin<CPort_InfoB, CPin_Info0> pb0;
extern CPin<CPort_InfoB, CPin_Info1> pb1;

        类中是没有数据成员的,所以只声明不定义pa0也行,但是需要编译器的优化等级调高点,才不会报错,
        或者,随便在一个文件里定义定义一来就行了。

        这部分的具体代码可参考 myfwlib 文件夹中的 GPIO_CPin.h 和 GPIO_CPin.cpp 中的内容。

        实验还是按键中断实验,不过对部分名字做了修改,不和原来的代码兼容
        因为标准库里也有个iostream,我怕搞混了,所以就给自己的iostream里的内容加了个命名空间,namespace nstd,非标准,哈哈,因为只有我在用~

stm32_cppTest-11完善引脚定义.zip

462.52 KB, 下载次数: 1657

https://github.com/roxma
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-4-5 14:11:09 | 显示全部楼层
发现我之前的方式效率稍低一些,
虽然以前测试的时候对比过位带的方式,但是发现位带的代码会略多一点,
估计是用位带的操作的宏的时候编译器会产生一些常量,看上去吃了点flash。但是使用的多了,确实是位带的方式效率高。
我反汇编对比了原子哥提供的位带操作的宏,和我之前的引脚操作的方式,发现位带的操作方式速度是比较快的,所以我把楼上的代码改成位带操作去实现了。
基本原理差不多的,具体的就自己看了。
示例代码是很简单的闪烁灯,,,我发现我测试啥都喜欢来个闪烁灯

#include "sys.h"

using namespace periph;
int main(void) 
{
sys.Init();
rcc.Enable(PERIPH_GPIOA);
pa8.Config(OUTPUT, PUSH_PULL);
while(1)
{
Delay_ms(1000);
pa8 = !pa8;            // led 取反,这句话用起来有点像用51单片机的感觉,好亲切
}
}







stm32_cppTest-12简单的闪烁灯.zip

461.12 KB, 下载次数: 1665

https://github.com/roxma
回复 支持 反对

使用道具 举报

6

主题

114

帖子

0

精华

初级会员

Rank: 2

积分
180
金钱
180
注册时间
2011-2-28
在线时间
8 小时
发表于 2012-4-10 14:14:12 | 显示全部楼层
  这个主题非常酷,一般人不会想到用C++开发呢。
我会很乐意与你交流的。
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-4-15 01:20:18 | 显示全部楼层
这个实验是窗口看门狗实验。。。
吐糟一下STM32的WWDG,设计的实在不咋的,
1. 不如独立看门狗,WWDG还可以由RCC控制,想关就关(实验代码里有这个演示)
2. 这个可能是我自己的问题,有经验的帮我看看。手册上说WWDG的计数器达到0x40就会产生中断,而这个时候计数器的值肯定是在窗口中。但是测试结果发现,不在窗口中的时候也可以进入中断的。
3. 居然可以中断喂狗。。。这样简单的功能,随便用个定时器+几行代码也能做到。。。要这狗来干啥。。。

另外发现软件仿真不了,参考了原子哥以前在别的网站的博文,说是MDK不能仿真硬件复位。MDK真不厚道,不能仿真的东西也搞上去,这个是坑人么。。。

吐糟归吐糟,实验还是要做的。。。
实验的功能是,上电复位后,
延时 1s  --------  两个 LED 亮 ---------1s --------两个LED灭 -------- 放狗开中断 --------进入死循环延时 10s
在中断中,如果发现看门狗计数器在窗口内,喂狗,翻转LED1,否则不喂狗,翻转LED0。
延时10s后,通过RCC 把狗收了,延时1s,发现系统没复位,发条串口信息,over

上主函数代码,

#include "sys.h"
#include "iostream.H"

using namespace periph;
using namespace nstd;

ARMAPI void WWDG_IRQHandler()
{
        
if(wwdg.IsInWindow()) // 如果计数器的值在指定的窗口中
{
wwdg.Reflash(); // 喂狗
pd2 = !pd2;
}
else
{
pa8 = !pa8; // 程序虽然会跳进这里。。。奇怪。。。
}
wwdg.ClearPending();
}

int main(void) 
{
  sys.Init();
scb.SetPriorityGrouping(GRP4_SUB4); // 4个组优先级,4个子优先级
nvic.Config(wwdg, ENABLE); // 使能窗口听门狗的中断
rcc.Enable(gpioa);        //这个的配置和以前不同了哦,具体实现利用的是函数重载,看GPIO_TypeDef的定义就知道了。感觉这样用方便些。
rcc.Enable(gpiod);
// 两个LED对应引脚的配置
pa8.Config(OUTPUT, PUSH_PULL);
pd2.Config(OUTPUT, PUSH_PULL);
// 两个LED灭
pa8 = 1;
pd2 = 1;
Delay_ms(1000);
// 两个LED翻转
pa8 = !pa8;
pd2 = !pd2;
Delay_ms(1000);
// 两个LED翻转
pa8 = !pa8;
pd2 = !pd2;
Delay_ms(1000);
rcc.Enable(wwdg);
wwdg.SetTimerBase(PCLK1_DIV_4096_DIV_8); //设置窗口看门狗的时钟
wwdg.SetWindow(0x7f); // 窗口值
wwdg.Start(); // 启动窗口看门狗
wwdg.Reflash(); // 刷新窗口看门狗的计数器
wwdg.EWI_Enable(); // 使能窗口看门狗的中断
while(1)
{
for(u8 i = 10; i; i--)
Delay_ms(1000);
rcc.Disable(wwdg);
Delay_ms(1000);
output<<"TMD, 系统没有复位,WWDG还是可以关闭的。。。"<<endl;
while(1);
}
}


stm32_cppTest-13窗口看门狗.zip

250.73 KB, 下载次数: 1583

https://github.com/roxma
回复 支持 反对

使用道具 举报

2

主题

50

帖子

0

精华

初级会员

Rank: 2

积分
78
金钱
78
注册时间
2012-4-15
在线时间
0 小时
发表于 2012-4-15 14:57:54 | 显示全部楼层
看了一下,  不是很懂啊  !! 压力大
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-4-15 15:37:24 | 显示全部楼层
回复【34楼】gongkansb:
---------------------------------

哪里不懂?
说一下,因为我这是在自建库,所以以后是打算写文档的,
https://github.com/roxma
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-4-17 09:49:31 | 显示全部楼层
有些手机和平板有在运行的时候自由设置CPU频率的功能,昨天心血来潮也做了一个,

实验现象就是,LED闪烁,越来越快,一直把频率升到72M。

主函数的代码比较简单,因为只顾着闪烁LED,其实如果调整了CPU频率,串口之类的很多外设都是需要调整的。

#include "sys.h"
#include "iostream.H"

using namespace periph;
using namespace nstd;

// 应用程序中修改 CPU 频率的实验
int main(void) 
{
  sys.Init();
pa8.Config(OUTPUT);
while(1){
for(int pll = 2; pll <= 9; pll++)
{
                       //需要注意的是这个函数内部没有判断CPU是否超频。。。不要随便超频。。。
rcc.SysclkConfig(PLL_AS_SYSCLK    //以PLL输出作为系统的时钟源
                            , HSE_DIV_1                                // pll 的时钟源
                            , pll                                                // pll倍频数
                            , 8000000);                                // HSE频率
for(u8 i = 0; i < 10; i++)
{
Delay_ms(100);
pa8 = !pa8;
}
}
}
}

不废话了。。。去上课先

stm32_cppTest-14设置CPU频率.zip

255.99 KB, 下载次数: 1584

https://github.com/roxma
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-4-17 13:02:22 | 显示全部楼层
回来改了一下,函数 SysclkConfig 不允许超频,如果超频,调用这个函数会把系统时钟变成使用8M的内部时钟。虽然很多人喜欢超频,不过我可不想搞这个,
函数的定义那里在有更具体的说明,有兴趣的自己看了。
实验代码也简单的改了一下,总之就是以不同的系统时钟频率来做闪烁LED

// 应用程序中修改 CPU 频率的实验
int main(void) 
{
  sys.Init();
pa8.Config(OUTPUT);
while(1){
// 使用 HSE 作为系统时钟
rcc.SysclkConfig(HSE_AS_SYSCLK);
for(u8 i = 0; i < 4; i++)
{
Delay_ms(100);
pa8 = !pa8;
}
// 使用 HSI 作为系统时钟
rcc.SysclkConfig(HSI_AS_SYSCLK);
for(u8 i = 0; i < 4; i++)
{
Delay_ms(100);
pa8 = !pa8;
}
// 后面的是使用PLL输出作为系统时钟
for(u8 pll = 2; pll <= 9; pll++)
{
rcc.SysclkConfig(PLL_AS_SYSCLK, HSE_DIV_1, pll); // PLL时钟源为 HSE
for(u8 i = 0; i < 4; i++)
{
Delay_ms(100);
pa8 = !pa8;
}
}
for(u8 pll = 2; pll <= 16; pll++)
{
rcc.SysclkConfig(PLL_AS_SYSCLK, HSI_DIV_2, pll); // PLL 时钟源为HSI
for(u8 i = 0; i < 4; i++)
{
Delay_ms(100);
pa8 = !pa8;
}
}
}
}

stm32_cppTest-14设置CPU频率(2).zip

255.93 KB, 下载次数: 1557

https://github.com/roxma
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-4-19 19:24:10 | 显示全部楼层
定时器中断实验,闪烁灯。。。STM32的定时器功能太强大了。。。寄存器我封装得不怎么好,以后再想办法改进吧。。。
函数多了,使用者找起来辛苦,要是集成一个函数,但是参数都是数字的情况下会导致代码的阅读性不好,估计我以后也会像官方库那样,搞个 InitStruct 了。。。
一直觉得用结构体的话代码的效率会比较低,不过就配置定时器来说,这点效率似乎关系不大。

直接上代码

#include "sys.h"
#include "iostream.H"

using namespace periph;
using namespace nstd;

ARMAPI void TIM2_IRQHandler()
{
pa8 = !pa8;
// 清除中断标志
timer2.ClearFlag(UpdateEventInterruptFlag);
}

int main(void) 
{
  sys.Init();
rcc.PeriphControl(gpioa, ENABLE);
pa8.Config(OUTPUT);

rcc.PeriphControl(timer2, ENABLE);
timer2.SetPrescaler(7200); // 设置分频器
timer2.SetAutoReloadValue(10000); // 自动重装值
timer2.SetCounter(0); // 计数器初值
timer2.SetCounterMode(UP_COUNTING); // 向上计数
timer2.InterruptConfig(UpdateEventInterrupt, ENABLE); // 开中断
nvic.Config(TIM2_IRQn, ENABLE); // 开中断
timer2.Counter(ENABLE);    //开启计数器
while(1){}
}

stm32_cppTest-15定时器中断.zip

260.09 KB, 下载次数: 1565

https://github.com/roxma
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-4-23 13:43:10 | 显示全部楼层
比赛需要,昨晚花了两个多钟头移植了以前在51上写的文件系统。。。
还以十几分钟就能移植成功。。。结果。。。有点悲剧。。。

SD卡驱动还是用51的时候留下来的,
SPI的初始化代码我就直接移植了原子哥的。

文件系统的使用还是C语言的方式,但是我的工程用的是C++,
如果有网友需要移植到自己的C语言的工程,需要做一些改动。。。比较繁锁。。。估计大家更倾向于现在网上流行的FATFS,
我的代码,有兴趣下载来直接用C++玩玩就行,功能也不多,实际意义不大。
以后打算用C++来完善这个系统,C++能直接使用C语言的代码,不过既然要走向C++,就基本上意味着没办法再给C语言使用了。

我又哆嗦了。。。上代码。。。

#include "sys.h"
#include "iostream.h"
using namespace periph;
using namespace nstd;
#include "utilities.H"
#include "MMC_SD.H"
#include "memory.H"
#include "FAT_FS.h"

const u8* NAME = "welcome.txt"; // 实验用的文件名
const u8* CONTENT = "欢迎来到开源电子网~大家相互学习,多多指教!"; // 写入到文件中的内容
u8 buf[60] = {0};

int main(void) 
{
  sys.Init();
Mem_Init(); //初始化动态内存管理模块
Delay_ms(1000);
while(SD_Init()!=0)//检测不到SD卡
{
output<<"failed..."<<endl;
Delay_ms(1000);
}
output<<"sd init success!!!"<<endl;
// 初始化文件系统
if(FileSysInit())
output<<"\nfat_fs init failed!\n";
while(1); 
output<<"\nfat_fs init success!\n"; 

File* p;
// 打开文件,以创建方式打开
p = fopen(NAME, FILE_READ | FILE_WRITE | FILE_CREATE);
if(p == NULL){
output<<"open file failed."<<endl;
while(1);
}
output<<"open file success."<<endl;
fwrite(p,CONTENT, StringLength(CONTENT));
fclose(p); // 关闭文件,如果文件中写入了新的数据,那么必须关闭文件,新的数据才不会丢失
// 以只读方式打开刚才创建的文件
p = fopen(NAME, FILE_READ );
if(p == NULL){
output<<"文件打开失败"<<endl;
while(1);
}
output<<"文件打开成功."<<endl;
u16 cnt = fread(p, buf, 60); // 从刚刚写入的文件中读取内容,并把读取到的数据量保存到 cnt
output<<"fread 读到了 "<<cnt<<"个字节."<<endl
<<"从文件中读到的文本为 :"<<buf<<endl;
while(1){}
}

上面的代码应该不难懂,实现的功能就是,打开一个文件,写入文件,关闭文件。然后重新打开,读取文件,串口显示。


顺便提一下,我比较希望我开源的代码,网友下载了,只要把所有的文件添加的工程里就能编译成功的,

这也算是我脱离库的原因之一,一般更倾向于使用自己写的函数,不喜欢依赖库

现在MDK的新版本默认是用3.5版的STM32固件库了。。。好多以前的源码都不能直接编译了。。。

才移植没多久,重新审视了一下,感觉比较乱,但是现在没多少时间去整理。。。

stm32_FAT32.zip

2.27 MB, 下载次数: 759

https://github.com/roxma
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-4-26 11:56:14 | 显示全部楼层
STM32的功能比较强大,
短时间内什么都自己完成不太现实,遇到项目的时候难免还是需要官方库的
C++版的代码以后还是会不断更新,不过我把代码的结构修改了一下,

现在的版本可以完美的兼容官方的库,我的C++版的代码在cppfwlib文件夹下,
依赖于官方库的两个头文件 stm32f10x.h 和 core_cm3.h,主要是需要官方提供的寄存器结构体定义,
还有几个类型定义,然后再用C++去拓展
为了避免以后编译器又更新默认使用的库,所以上传的代码里面都会有包含这些需要的头文件,而不用系统提供

兼容官方库,也方便自己学习吸收官方库里有价值的东西

#include "sys.h"
#include "iostream.h"

// 官方库的头文件
#include "stm32f10x.h"

using namespace nstd;
using namespace periph;

int main(void)
{
  sys.Init();
// C++版本
// 使能 GPIOA
rcc.ClockControl(gpioa, ENABLE);
// 库函数版本
// 使能 GPIOD
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
// C++版本
// 初始化 pa8 为输出模式,默认为开漏输出, 10M速度
pa8.Config(OUTPUT);

// 库函数版本初始化
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
while(1){
// 库函数版本
// PD2 取反
GPIO_WriteBit(GPIOD, GPIO_Pin_2,
(GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_2)==Bit_RESET)?Bit_SET:Bit_RESET);

// C++版本
// pa8 取反
pa8 = !pa8;
Delay_ms(200);
}
}



stm32_cppTest-16完美兼容官方库版本.zip

4.11 MB, 下载次数: 1891

https://github.com/roxma
回复 支持 反对

使用道具 举报

27

主题

274

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
472
金钱
472
注册时间
2011-11-2
在线时间
11 小时
发表于 2012-4-29 18:47:16 | 显示全部楼层
五一前夕来顶下楼主。  学习的很细致。 
回复 支持 反对

使用道具 举报

27

主题

274

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
472
金钱
472
注册时间
2011-11-2
在线时间
11 小时
发表于 2012-4-29 19:50:32 | 显示全部楼层
觉定跟着你学习,练手一下C++兴许将来会有大用处。
比较浮躁,看看能坚持多久。
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-4-29 21:25:53 | 显示全部楼层
回复【42楼】 zenghi :
---------------------------------
呵呵,我上面的帖子过程性比较明显

也就是说前面提到的东西,可能到后面又没有使用或者又进行了其他的改动了,

所以如果通篇帖子都认真看的话,确实会比较花时间的

看个大概就行了,我的代码不必费工夫仔细研究,有比较重要的内容我都会在帖子里说明

就目前来讲,感觉做的比较好的也就是GPIO的部分了,不长进啊

写到现在,有点向官方库靠拢,使用结构体的趋势,

除了使用C++的特性,我对寄存器的封装过程差不多也算是有个固定的风格了,这个也许算是我代码里稍有些参考价值的地方吧
https://github.com/roxma
回复 支持 反对

使用道具 举报

27

主题

274

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
472
金钱
472
注册时间
2011-11-2
在线时间
11 小时
发表于 2012-4-30 21:45:43 | 显示全部楼层
回复【43楼】Pony279:
---------------------------------
确实感觉很棒,这个工程文件结构还得了解下,其实就当玩玩C++。也蛮特别的用C++写这个。
而且感觉能从里面发觉不少好东东。 
回复 支持 反对

使用道具 举报

0

主题

58

帖子

0

精华

初级会员

Rank: 2

积分
78
金钱
78
注册时间
2012-4-29
在线时间
0 小时
发表于 2012-4-30 22:36:17 | 显示全部楼层
单片机 用C++  个人觉得有必要吗??
1.环保数采仪监控系统?2.物联网、管网监控系统?3.水文水利监控系统?4.油烟在线监控系统.?5.智能小区水电表监控系统.?提供各种自动化监控解决方案!?有需要请联系,QQ 237324479!
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-5-1 02:18:33 | 显示全部楼层
回复【45楼】MartinARM:
---------------------------------

楼上想说没必要,但是希望楼上能够提出事实的论据,
而不是看着网上大部分人的观点就跟风,轻易的下个结论
如果你有自己的观点,希望你能继续回帖交流意见。


下面我简单说一下我的观点,

如果说是仅仅是对于底层硬件驱动的开发,
C++对于C的优势确实不大,
但是C++有C的全部功能,也就是说,
C语言的代码几乎可以不加任何改动地就移植到C++上,
C++对硬件驱动所能提供的更多的是“语法糖”

不过,一旦上了比较大的系统,
代码对抽象的事物的描述,
C语言绝对远不及C++,

总结上面的内容就是,使用C++完全是有益无害的。

PS:关于网上比较多人提到的效率问题,如果使用C语言风格写的代码,我反汇编测试过一些,和C语言无差别。
C++对效率的影响主要是在
1. 构造函数,但是这个对代码的健壮性和使用的方便性上都是有意义的,这个可以看成是初始化函数。
2. 虚函数,关于这个,需要用到的地方,用C语言写也是一个样。
3. 模板,不知道算不算,用的好了,方便。滥用了就悲剧。

https://github.com/roxma
回复 支持 反对

使用道具 举报

3

主题

123

帖子

1

精华

中级会员

Rank: 3Rank: 3

积分
364
金钱
364
注册时间
2012-4-30
在线时间
11 小时
发表于 2012-5-2 01:35:39 | 显示全部楼层
厉害,顶一个,就是要百花齐花,百家争鸣。
真的勇士,敢于直面惨淡的warning,敢于正视淋漓的error.
回复 支持 反对

使用道具 举报

27

主题

274

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
472
金钱
472
注册时间
2011-11-2
在线时间
11 小时
发表于 2012-5-5 13:44:22 | 显示全部楼层
要顶一下呀 ,找了半天才找到。
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2199
金钱
2199
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-5-5 20:55:45 | 显示全部楼层
回复【48楼】 zenghi :
---------------------------------

呵呵,谢谢支持,

哎,好久没更新了。。。





今天更新一个简单点的,不是什么实验,仅仅是软件仿真而已。。。

快速8位IO操作,可以参考这个帖子http://www.openedv.com/posts/list/5215.htm?fromAll=0

程序里定义了 PAL(GPIOA低8位), PAH(GPIOA高8位),PBL,PBH ,操作起来,跟51单片机的P1 P2 P3 没什么两样,效率也很高,

定义在cppfwlib文件夹里面的 stm32f10x__gpio.h里面,用 go to definition 找符号更快一点,

原理我相信大家看实现代码就懂了,看那个帖子也行,那个帖子使用的是 union 类型,而 这里使用的是强制指针转换

keil 的工程文件在 uvproj 文件夹下,

最近使用了新的IDE,TKStudio ,主要是喜欢这的代码辅助功能,可惜感觉还是不够强大。。。TKStudio 的工程文件在 TKStudioproj文件夹下。

示例代码很简单,不要下载到单片机测试(这次的测试代码没有考虑实际硬件的。。。),软件仿真测试就行了。

PAL.Config(INPUT, PULL_UP);
PAH.Config(OUTPUT, PUSH_PULL, SPEED_50M);

PBL.Config(OUTPUT, OPEN_DRAIN);
PBH.Config(INPUT, PULL_DOWN);

u8 a = PAL; // 读 GPIOA 低8位
u8 b = PBH; // 读 GPIOB 高8位
a = PAL; // 读 GPIOA 低8位
b = PBH; // 读 GPIOB 高8位
a = PAL; // 读 GPIOA 低8位
b = PBH; // 读 GPIOB 高8位
PAH = a+0x55; // 写 GPIOA 高8位
PBL = b+0xaa; // 写 GPIOB 低8位

反汇编看过,效率一点也不比操作16位IO低,印象中有人说STM32操作8位IO输过51,呵呵,其实不是这样的。



另外发现了以前的一个BUG,把某些脚设置为上拉输入的时候会错误的设置成输出,原因是 mylib 文件夹里的 BitOperation.h 的 AssignField 函数的 bug,细节问题,已经修正。


stm32_cppTest-17快速8位IO操作(勿下载到单片机测试).zip

4.01 MB, 下载次数: 651

https://github.com/roxma
回复 支持 反对

使用道具 举报

27

主题

274

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
472
金钱
472
注册时间
2011-11-2
在线时间
11 小时
发表于 2012-5-5 22:12:03 | 显示全部楼层
突然觉得楼主这样发展下去可以去芯片厂写官方库了。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-5-20 17:32

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表