OpenEdv-开源电子网

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

从零开始学STM32(开贴记录我的学习历程)

[复制链接]

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
发表于 2016-3-5 14:52:15 | 显示全部楼层 |阅读模式
本人以前学过51单片机,知道ARM这个东西后一直向往,最近终于入手一块“战舰V3 精英板”(买板子的时候,没注意看板子资源,看到“精英”二字,以为是STM32F103ZET6板子里面最牛的,而且看价格还不是最贵的,果断买了精英板),经过几天的C语言学习、keil安装及使用学习、Jlink使用学习、工程模板(基于固件库)建立……我的STM32学习终于走上正轨。之前听过别人分享如何学习STM32,其中强调了一点,就是加入一个论坛,在论坛中跟大家一起学习,共同进步,于是今天就开贴(以前水过,从来没发过贴),从零开始学习STM32(以前学过51,但是没了解过STM32,不知道算不算从零开始),记录我的学习历程,记录一个系统性地学习STM32的过程,以求跟坛友共同进步,同时以后传递给别人准备学STM32的人一些经验。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-9 12:45:51 | 显示全部楼层
【2016-03-09】IO口的相关寄存器
看了好多个小时的视频,又看了些书,视频里说学习STM32还是需要了解下寄存器的,于是我又去看了寄存器版本的教程和例程,对IO口相关的寄存器也有了一些了解;
以下截图是本人对IO口的相关寄存器的了解:

1、端口配置寄存器-配置输入输入及上下拉模式这些.png
2、端口输入寄存器-从这里读取端口的输入.png
3、端口输出数据寄存器-把要输出的数据放在这.png
4、端口位设置、清除寄存器,和端口位清除寄存器-感觉像置0置1的(好像没用过,不是很懂).png
5、端口配置锁定寄存器-锁定后不能重新修改对应端口配置.png
回复 支持 1 反对 0

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-5 15:40:57 | 显示全部楼层
【2016-03-05】开发环境建立及工程模板建立
1、安装keil5并安装ST包;
2、安装ST-Link驱动、J-Link驱动;
3、板子及ST-Link、J-Link测试;
4、建立自己的工程模板;

1、安装keil5并安装ST包:
    按照教程和自己的理解安装keil5,再安装了STM32F103器件库;
2、安装ST-Link驱动、J-Link驱动:
    买板子的时候顺便买了ST-Link,一拿到就安装了ST-Link的驱动;
后来又从别人那里拿了一个掉了固件的J-link修好了,
3、板子及ST-Link、J-Link测试;
4、建立自己的工程模板;
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-5 15:53:11 | 显示全部楼层
还没打完,不知道按了什么发出去了,重新打……
【2016-03-05】开发环境建立及工程模板建立
1、安装keil5并安装ST包;
2、安装ST-Link驱动、J-Link驱动;
3、板子及ST-Link、J-Link测试;
4、建立自己的工程模板;

1、安装keil5并安装ST包:
    按照教程和自己的理解安装keil5,再安装了STM32F103器件库;
2、安装ST-Link驱动、J-Link驱动:
    买板子的时候顺便买了ST-Link,一拿到就安装了ST-Link的驱动;
   后来又从别人那里拿了一个掉了固件的J-link修好了,感觉J-Link厉害些,于是装了J-Link的驱动准备用J-Link;
3、板子及ST-Link、J-Link测试:
    用下载器下载了一个LED的程序,跑了一下,可用(顺便证明下载器是好的),之后看了视频,又下了综合测试程序进去跑了一下,大概用了下,感觉没什么问题(没花精力仔细了解),没有仔细测试,如果以后碰到问题,就解决问题;
4、建立自己的工程模板:
    按照开发指南建立工程模板,并且经过慎重考虑,还是把project文件新建一个PROJECT文件夹放在里面,USER文件夹里面只放代码,然后因为个人习惯,在模板文件夹目录里建一个相对路径快捷方式,指向这个project文件,以后要新建工程的时候,直接把整个文件夹复制,快捷方式的指向还是新工程;

工程模板

工程模板
回复 支持 反对

使用道具 举报

20

主题

468

帖子

3

精华

金牌会员

Rank: 6Rank: 6

积分
1684
金钱
1684
注册时间
2014-2-25
在线时间
230 小时
发表于 2016-3-5 16:06:44 | 显示全部楼层
可以,加油
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-5 16:42:01 | 显示全部楼层

谢谢,我计划照着教程系统性地学学习
回复 支持 反对

使用道具 举报

56

主题

1237

帖子

0

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2643
金钱
2643
注册时间
2014-2-13
在线时间
518 小时
发表于 2016-3-5 17:12:56 | 显示全部楼层
LZ好样的!一起加油!
技术交流,Sell 中颖单片机,欢迎私信骚扰
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-5 19:40:33 | 显示全部楼层
【2016-03-05】跑马灯
由于刚入手板子,又是周末,虽然我视频还没看到跑马灯这里来,但是还是忍不住想自己写下程序到板子上跑一下;
1、查看板子电路图,找到个LED分别连到MCU的IO口的哪个引脚;
2、查看原子跑马灯例程并理解过程(还不会STM32,先看着学,因为心急,还没看视频);
3、关掉例程,复制自己的空工程模板,写跑马灯代码;
4、仿真调试,思考总结,记录发帖;


1、查看板子电路图,找到个LED分别连到MCU的IO口的哪个引脚;
    我的板子是精英板:LED0->B5;LED1->E5;IO连接到LED阴极(IO口输出0点亮LED);
2、查看原子跑马灯例程并理解过程(还不会STM32,先看着学,因为心急,还没看视频);
    跑马灯程序编写思路:①配置IO口时钟->②配置IO口->③重复“LED0亮,LED1灭” <--> “LED0灭,LED1亮”两种状态切换,并在中间加适当延时;
3、复制自己的空工程模板,写跑马灯代码;
    直接写在main.c里面,以前听别人说这样的习惯不好,我准备调通了再学例程那样单独建个文件;
    这是我仿照例程新建工程模板的写法打的程序,函数从各个库文件里找到复制出来的;(写的第一个程序就失败了
[mw_shl_code=applescript,true]#include "stm32f10x.h"
#include "delay.h"
#include "stm32f10x_rcc.h"

int main(void)
{       
   
   GPIO_InitTypeDef GPIO_InitStructure;
   
   delay_init();
   RCC_APB1PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOE , ENABLE);
   
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
   
   GPIO_Init(GPIOB,&GPIO_InitStructure);
   GPIO_SetBits(GPIOB, GPIO_Pin_5);
   
   GPIO_Init(GPIOE,&GPIO_InitStructure);
   GPIO_SetBits(GPIOE, GPIO_Pin_5);
   
   while(1)
   {
      GPIO_SetBits(GPIOB, GPIO_Pin_5);
      GPIO_ResetBits(GPIOE, GPIO_Pin_5);
      delay_ms(500);
      GPIO_ResetBits(GPIOB, GPIO_Pin_5);
      GPIO_SetBits(GPIOE, GPIO_Pin_5);
      delay_ms(500);
   }
}[/mw_shl_code]
4、仿真调试;

    下载程序到板子上,发现没反应,怕自己没选择复位,就按下板子的复位,还是没反应,然后就去DEBUG,发现程序执行完配置IO口PB5那里后,IO口的PB5模式并没有变化,不得已,又打开例程,DEBUG,观察到执行配置PB5后,PB5模式变成想要设定的那样,然后我就把例程里的配置全部复制到我自己的工程里,一句一句地对,看自己是不是哪个字符写错了(虽然keil5没报错,但是第一次写程序,还是怕打错字),没有观察到哪里不一样,注释掉自己的,DEBUG例程那一段,PB5能正常配置,注释掉例程,DEBUG自己那一段,PB5不能正常配置,纳闷了,后面终于观察到“RCC_APB1PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOE , ENABLE);”这一句有问题,我怎么就复制个APB1的函数出来了,改成APB2,之后一切正常,自己的LED终于闪烁起来了;(之后去翻了下时钟系统相关的资料,知道了IO口是由APB2提供,不能有APB1提供)
    接下来就把LED的代码仿照例程“跑马灯”的样式写在一个led.c文件里,也仿照着写一个led.h文件(其实算是复制的),以后的工程里,就可以直接加入这个文件,使用LED了。


回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-5 19:44:11 | 显示全部楼层
黑夜之狼 发表于 2016-3-5 17:12
LZ好样的!一起加油!

正在努力,一起加油!
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-6 08:22:14 | 显示全部楼层
【2016-03-06】蜂鸣器
做完LED,心里想着蜂鸣器控制跟LED几乎是一个样,于是马上就开始写蜂鸣器程序跑;
1、查看板子电路图,找到蜂鸣器连到MCU的IO口的哪个引脚;
2、查看原子蜂鸣器例程并理解过程;
3、关掉例程,复制自己的空工程模板,写蜂鸣器代码;
4、仿真调试,思考总结,记录发帖;
1、查看板子电路图,找到蜂鸣器连到MCU的IO口的哪个引脚;
    精英板:BEEP -> PB8;
2、查看原子蜂鸣器例程并理解过程;
    程序跟LED几乎一样,就是换成了控制PB8拉高拉低(看例程的写法,猜测板子上的是有源蜂鸣器,所以直接给高低电平就可以啦);
    蜂鸣器程序编写思路:①配置IO口时钟->②配置IO口->③重复“蜂鸣器响” <--> “蜂鸣器不响”两种状态切换,并在中间加适当延时,顺便把LED闪烁加上去;
3、关掉例程,复制自己的空工程模板,写蜂鸣器代码;
    这次就直接新建beep.c和beep.h文件开始编(led.c和led.h也加到工程里),代码也直接从led文件里复制过来改一下(偷懒);
    这是beep.c的代码:
[mw_shl_code=applescript,true]#include "beep.h"
#include "stm32f10x.h"

void BEEP_Init(void)
{
   GPIO_InitTypeDef GPIO_InitStructure;

   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);
   
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
   
   GPIO_Init(GPIOB,&GPIO_InitStructure);
   GPIO_ResetBits(GPIOB, GPIO_Pin_8);  
}
   
[/mw_shl_code]
    beep.h也仿照着写:
[mw_shl_code=applescript,true]#ifndef __BEEP_H
#define __BEEP_H         
#include "sys.h"

#define BEEP PBout(8)// PB8

void BEEP_Init(void);
                                                     
#endif
[/mw_shl_code]
    然后在main里面这样写:
[mw_shl_code=applescript,true]#include "stm32f10x.h"
#include "led.h"
#include "beep.h"
#include "delay.h"

int main(void)
{       
  
   LED_Init();  
   BEEP_Init();
   delay_init();
   
   while(1)
        {
           LED0 = 1;
      LED1 = 0;
      BEEP = 0;
      delay_ms(200);
      
      LED0 = 0;
      LED1 = 1;
      BEEP = 1;
      delay_ms(500);
        }
}
[/mw_shl_code]
4、仿真调试,思考总结,记录发帖;
    下载这个程序的时候我有预感会成功,果然下载进去,蜂鸣器和LED按照预想的那样工作了。
    又编好了一个程序啦,接下来的按键输入我猜测还是差不多,估计就是把IO配置成输入,等编完按键程序,就去看视频看书去啦,把理论补上来,才更加清楚自己学到了什么;



回复 支持 反对

使用道具 举报

4

主题

24

帖子

0

精华

初级会员

Rank: 2

积分
145
金钱
145
注册时间
2016-2-25
在线时间
44 小时
发表于 2016-3-6 14:24:16 | 显示全部楼层
加油 帮顶
回复 支持 反对

使用道具 举报

3

主题

22

帖子

0

精华

初级会员

Rank: 2

积分
106
金钱
106
注册时间
2016-1-14
在线时间
11 小时
发表于 2016-3-6 15:29:16 | 显示全部楼层
加油,帮顶,跟楼主差不多,我基本外设差不多都会用了,只是熟练度还差点,现在一边复习一边看UCOS,已经大三了 ,时间好少啊~
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-6 16:46:20 | 显示全部楼层
【2016-03-06】按键
这个周末过把手瘾,编完这个按键程序之后,就去看视频看书补理论去了;
1、查看板子电路图,找到各个按键分别连到MCU的IO口的哪个引脚;
2、查看原子按键例程并理解过程;
3、关掉例程,复制自己之前写的蜂鸣器工程(里面有LED和BEEP功能),写按键代码;
4、仿真调试,思考总结,记录发帖;

1、查看板子电路图,找到各个按键分别连到MCU的IO口的哪个引脚;
    精英板:KEY0 -> PE4 ; KEY1 -> PE3;KEY_UP -> PA0 ;
2、查看原子按键例程并理解过程;
    程序还是关于IO口的使用,只不过用的是输入模式(原子的板子上,三个按键既有高电平有效,也有低电平有效的,对学习来说非常棒);
   按键程序编写思路:①配置IO口时钟->②配置IO口->③一直检测按键状态,若有键按下,则判断是哪一个键,并执行对应的动作;
3、关掉例程,复制自己之前写的蜂鸣器工程(里面有LED和BEEP功能),写按键代码;
   老样子,写一个初始化函数,配置按键相关的IO口,再仿照着例程写一个读取按键状态的函数,在main函数里一直检测按键状态,有键按下就点亮一下对应的LED,顺便响一下蜂鸣器;
    这是key.c的代码:
[mw_shl_code=applescript,true]#include "key.h"
#include "stm32f10x.h"  
#include "stm32f10x_rcc.h"
#include "delay.h"

// 3&#245;ê&#188;&#187;ˉ
void KEY_Init(void)
{
        GPIO_InitTypeDef GPIO_InitStructure;

        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE);  //ê1&#196;üPA,PEê±&#214;ó

   // KEY0 -> PE4 ; KEY1 -> PE3 ;
        GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_3|GPIO_Pin_4;
   // &#197;&#228;&#214;&#195;&#213;a&#184;&#246;&#214;&#174;&#199;°£&#172;&#187;1&#207;&#235;×&#197;ê&#199;·&#241;óD&#196;3&#214;&#214;D′·¨£&#172;&#196;üí&#172;ê±&#197;&#228;&#182;à&#184;&#246;IOòy&#189;&#197;£&#172;1&#251;è&#187;óD
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;   //&#196;£ê&#189;éè&#214;&#195;&#206;aé&#207;à-ê&#228;è&#235;
        GPIO_Init(GPIOE, &GPIO_InitStructure);

   // KEY_UP -> PA0 ;
        GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;   //&#196;£ê&#189;éè&#214;&#195;&#206;a&#207;&#194;à-ê&#228;è&#235;
        GPIO_Init(GPIOA, &GPIO_InitStructure);

}

// °′&#188;ü′|àíoˉêy£o&#182;áè&#161;°′&#188;ü×′ì&#172;£&#172;2¢·μ&#187;&#216;&#188;ü&#214;μ
// 2&#206;êyo&#172;ò&#229;£o
// mode:SINGLE_TRIGGER,μ¥′&#206;′¥·¢£&#172;2&#187;&#214;§3&#214;á&#172;°′;CONTINUE_TRIGGER,&#214;§3&#214;á&#172;D&#248;°′;
// ·μ&#187;&#216;&#214;μo&#172;ò&#229;£o
//          0: &#195;&#187;óDè&#206;o&#206;°′&#188;ü°′&#207;&#194;
// KEY0_PRESS: KEY0°′&#207;&#194;
// KEY1_PRESS: KEY1°′&#207;&#194;
// WKUP_PRESS: WK_UP°′&#207;&#194;
// ±&#184;×¢£o&#207;ìó|ó&#197;&#207;è&#188;&#182;: KEY0>KEY1>WK_UP
u8 KEY_Scan(u8 mode)
{         
        static u8 key_up=1;  //°′&#188;ü°′&#203;é&#191;a±ê&#214;&#190;
       
        if(mode)
           key_up=1;   //&#214;§3&#214;á&#172;°′       
                     
        if(key_up&&(KEY0==0||KEY1==0||WK_UP==1))  // óD&#188;ü°′&#207;&#194;
        {
                delay_ms(10);//è¥&#182;&#182;&#182;ˉ
                key_up=0;
                if(KEY0==0)             // &#188;ü0°′&#207;&#194;
                   return KEY0_PRESS;
                else if(KEY1==0)        // &#188;ü1°′&#207;&#194;
                   return KEY1_PRESS;
                else if(WK_UP==1)       // &#188;üWK_UP°′&#207;&#194;
                   return WKUP_PRESS;
        }
        else if(KEY0==1&&KEY1==1&&WK_UP==0)       // &#206;T&#188;ü°′&#207;&#194;
           key_up=1;   // &#206;T&#188;ü°′&#207;&#194;£&#172;&#188;′°′&#188;ü&#203;é&#191;a       
               
   return 0;   // &#206;T°′&#188;ü°′&#207;&#194;
}
[/mw_shl_code]
再仿照例程写一个key.h:
[mw_shl_code=applescript,true]#ifndef __KEY_H
#define __KEY_H         
#include "sys.h"

//#define KEY0 PEin(4)           //PE4
//#define KEY1 PEin(3)        //PE3
//#define WK_UP PAin(0)        //PA0  WK_UP

#define KEY0  GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)//&#182;áè&#161;KEY0
#define KEY1  GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)//&#182;áè&#161;KEY1
#define WK_UP   GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)//&#182;áè&#161;WK_UP

#define SINGLE_TRIGGER     0        //μ¥′&#206;′¥·¢
#define CONTINUE_TRIGGER   1        //á&#172;D&#248;′¥·¢

#define KEY0_PRESS   1        //KEY0°′&#207;&#194;
#define KEY1_PRESS   2        //KEY1°′&#207;&#194;
#define WKUP_PRESS   3        //KEY_UP°′&#207;&#194;

void KEY_Init(void);//IO3&#245;ê&#188;&#187;ˉ
u8 KEY_Scan(u8);          //°′&#188;üé¨&#195;èoˉêy               

#endif
[/mw_shl_code]
然后main.c这样写:
[mw_shl_code=applescript,true]#include "stm32f10x.h"
#include "led.h"
#include "beep.h"
#include "delay.h"
#include "key.h"

int main(void)
{       
   vu8 key_value;
  
   LED_Init();  
   BEEP_Init();
   delay_init();
   KEY_Init();
   
   while(1)
        {
      key_value = KEY_Scan(SINGLE_TRIGGER);
      
      if(key_value)  // óD&#188;ü°′&#207;&#194;
      {
         switch(key_value)
         {   
            case KEY0_PRESSED0 = 0;LED1 = 1;BEEP = 1;break;
            case KEY1_PRESSED0 = 1;LED1 = 0;BEEP = 1;break;
            case WKUP_PRESSED0 = 0;LED1 = 0;BEEP = 1;break;
            defaultED0 = 1;LED1 = 1;BEEP = 1;break;
         }
      }
      else           // &#206;T&#188;ü°′&#207;&#194;
      {
         LED0 = 1;LED1 = 1;BEEP = 0;
      }
      
      delay_ms(50);
        }
}
[/mw_shl_code]
4、仿真调试,思考总结,记录发帖;
    下载的时候,没注意,下载了例程的文件进去,吓得我以为自己一上午的努力又有哪里出错了,赶紧去翻代码,发现并不是自己的代码,还有机会,然后下了自己的代码进去,运行结果跟预想的一样;
    跑了LED,BEEP,KEY这几个程序,都是使用IO口的,对IO口的基本使用有了粗浅的了解,接下来就先不继续写程序跑了,看下视频,看下书,等理论学到了再实践,先打基础;

回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-6 16:53:38 | 显示全部楼层
不知道怎么回事,可能是我复制了格式粘贴了,导致文字看不清楚……还是重发一下吧顺便把注释的乱码矫正
【2016-03-06】按键
这个周末过把手瘾,编完这个按键程序之后,就去看视频看书补理论去了;
1、查看板子电路图,找到各个按键分别连到MCU的IO口的哪个引脚;
2、查看原子按键例程并理解过程;
3、关掉例程,复制自己之前写的蜂鸣器工程(里面有LED和BEEP功能),写按键代码;
4、仿真调试,思考总结,记录发帖;

1、查看板子电路图,找到各个按键分别连到MCU的IO口的哪个引脚;
    精英板:KEY0 -> PE4 ; KEY1 -> PE3;KEY_UP -> PA0 ;
2、查看原子按键例程并理解过程;
    程序还是关于IO口的使用,只不过用的是输入模式(原子的板子上,三个按键既有高电平有效,也有低电平有效的,对学习来说非常棒);
   按键程序编写思路:①配置IO口时钟->②配置IO口->③一直检测按键状态,若有键按下,则判断是哪一个键,并执行对应的动作;
3、关掉例程,复制自己之前写的蜂鸣器工程(里面有LED和BEEP功能),写按键代码;
   老样子,写一个初始化函数,配置按键相关的IO口,再仿照着例程写一个读取按键状态的函数,在main函数里一直检测按键状态,有键按下就点亮一下对应的LED,顺便响一下蜂鸣器;
    这是key.c的代码:
[mw_shl_code=applescript,true]#include "key.h"
#include "stm32f10x.h"  
#include "stm32f10x_rcc.h"
#include "delay.h"

// 初始化
void KEY_Init(void)
{
        GPIO_InitTypeDef GPIO_InitStructure;

        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE);  //使能PA,PE时钟

   // KEY0 -> PE4 ; KEY1 -> PE3 ;
        GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_3|GPIO_Pin_4;
   // 配置这个之前,还想着是否有某种写法,能同时配多个IO引脚,果然有
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;   //模式设置为上拉输入
        GPIO_Init(GPIOE, &GPIO_InitStructure);

   // KEY_UP -> PA0 ;
        GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;   //模式设置为下拉输入
        GPIO_Init(GPIOA, &GPIO_InitStructure);

}

// 按键处理函数:读取按键状态,并返回键值
// 参数含义:
// mode:SINGLE_TRIGGER,单次触发,不支持连按;CONTINUE_TRIGGER,支持连续按;
// 返回值含义:
//          0: 没有任何按键按下
// KEY0_PRESS: KEY0按下
// KEY1_PRESS: KEY1按下
// WKUP_PRESS: WK_UP按下
// 备注:响应优先级: KEY0>KEY1>WK_UP
u8 KEY_Scan(u8 mode)
{         
        static u8 key_up=1;  //按键按松开标志
       
        if(mode)
           key_up=1;   //支持连按       
                     
        if(key_up&&(KEY0==0||KEY1==0||WK_UP==1))  // 有键按下
        {
                delay_ms(10);//去抖动
                key_up=0;
                if(KEY0==0)             // 键0按下
                   return KEY0_PRESS;
                else if(KEY1==0)        // 键1按下
                   return KEY1_PRESS;
                else if(WK_UP==1)       // 键WK_UP按下
                   return WKUP_PRESS;
        }
        else if(KEY0==1&&KEY1==1&&WK_UP==0)       // 无键按下
           key_up=1;   // 无键按下,即按键松开       
               
   return 0;   // 无按键按下
}
[/mw_shl_code]
再仿照例程写一个key.h:
[mw_shl_code=applescript,true]#ifndef __KEY_H
#define __KEY_H         
#include "sys.h"

//#define KEY0 PEin(4)           //PE4
//#define KEY1 PEin(3)        //PE3
//#define WK_UP PAin(0)        //PA0  WK_UP

#define KEY0  GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)//读取KEY0
#define KEY1  GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)//读取KEY1
#define WK_UP   GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)//读取WK_UP

#define SINGLE_TRIGGER     0        //单次触发
#define CONTINUE_TRIGGER   1        //连续触发

#define KEY0_PRESS   1        //KEY0按下
#define KEY1_PRESS   2        //KEY1按下
#define WKUP_PRESS   3        //KEY_UP按下

void KEY_Init(void);//IO初始化
u8 KEY_Scan(u8);          //按键扫描函数               

#endif
[/mw_shl_code]
然后main.c这样写:
[mw_shl_code=applescript,true]#include "stm32f10x.h"
#include "led.h"
#include "beep.h"
#include "delay.h"
#include "key.h"

int main(void)
{       
   vu8 key_value;
  
   LED_Init();  
   BEEP_Init();
   delay_init();
   KEY_Init();
   
   while(1)
        {
      key_value = KEY_Scan(SINGLE_TRIGGER);
      
      if(key_value)  // 有键按下
      {
         switch(key_value)
         {   
            case KEY0_PRESSED0 = 0;LED1 = 1;BEEP = 1;break;
            case KEY1_PRESSED0 = 1;LED1 = 0;BEEP = 1;break;
            case WKUP_PRESSED0 = 0;LED1 = 0;BEEP = 1;break;
            defaultED0 = 1;LED1 = 1;BEEP = 1;break;
         }
      }
      else           // 无键按下
      {
         LED0 = 1;LED1 = 1;BEEP = 0;
      }
      
      delay_ms(50);
        }
}
[/mw_shl_code]
4、仿真调试,思考总结,记录发帖;
    下载的时候,没注意,下载了例程的文件进去,吓得我以为自己一上午的努力又有哪里出错了,赶紧去翻代码,发现并不是自己的代码,还有机会,然后下了自己的代码进去,运行结果跟预想的一样;
    跑了LED,BEEP,KEY这几个程序,都是使用IO口的,对IO口的基本使用有了粗浅的了解,接下来就先不继续写程序跑了,看下视频,看下书,等理论学到了再实践,先打基础;

回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-6 16:55:28 | 显示全部楼层

谢谢,我会努力学好STM32的
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-6 16:57:20 | 显示全部楼层
it8345 发表于 2016-3-6 15:29
加油,帮顶,跟楼主差不多,我基本外设差不多都会用了,只是熟练度还差点,现在一边复习一边看UCOS,已经大 ...

哇哇,谢谢,你比我厉害多了我刚开始学,很多东西以前没接触过,要一步一步来;
回复 支持 反对

使用道具 举报

1

主题

5

帖子

0

精华

初级会员

Rank: 2

积分
57
金钱
57
注册时间
2016-3-7
在线时间
18 小时
发表于 2016-3-7 00:20:05 来自手机 | 显示全部楼层
我个人觉得Key_up=0这句放在if(KEY0==0) 的下面会合理一点,不然感觉去抖无意义了
回复 支持 反对

使用道具 举报

16

主题

98

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
265
金钱
265
注册时间
2015-11-1
在线时间
34 小时
发表于 2016-3-7 08:11:06 | 显示全部楼层
加油,加油
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-8 13:07:25 | 显示全部楼层
蜗蜗猴 发表于 2016-3-7 00:20
我个人觉得Key_up=0这句放在if(KEY0==0) 的下面会合理一点,不然感觉去抖无意义了

这里的逻辑是这样的:1、有键按下(不管是抖动还是真按);2、延时10ms;3、10ms后再检测是键是否按下,如果没有按下了,就认为是抖动,如果还是按下的,就认为是真按;
如果有键按下之后,没有延时,马上判断是否是键0按下,两次检测之间只隔了一个判断语句的时间,就可能没有达到去抖的效果;
当然,先判断是不是键0按下,再延时,也同样能够去抖的,因为这个键0的输出不会平白无故就为0,如果为0,就必被按下,然后再加一个延时,防止后续抖动造成误判,也到达了去抖的效果,这个在连续触发按键的时候起作用;
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-8 13:09:58 | 显示全部楼层

谢谢,正在狂补理论中,等编下一个程序的时候继续更新,当然看书的总结也会更新,记录我的学习历程;
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-8 13:15:41 | 显示全部楼层
蜗蜗猴 发表于 2016-3-7 00:20
我个人觉得Key_up=0这句放在if(KEY0==0) 的下面会合理一点,不然感觉去抖无意义了

我好像理解你的意思了,我把它改成这样吧?
[mw_shl_code=applescript,true]// 按键处理函数:读取按键状态,并返回键值
// 参数含义:
// mode:SINGLE_TRIGGER,单次触发,不支持连按;CONTINUE_TRIGGER,支持连续按;
// 返回值含义:
//          0: 没有任何按键按下
// KEY0_PRESS: KEY0按下
// KEY1_PRESS: KEY1按下
// WKUP_PRESS: WK_UP按下
// 备注:响应优先级: KEY0>KEY1>WK_UP
u8 KEY_Scan(u8 mode)
{         
        static u8 key_up=1;  //按键按松开标志
       
        if(mode)
           key_up=1;   //支持连按       
                     
        if(key_up&&(KEY0==0||KEY1==0||WK_UP==1))  // 有键按下
        {
                delay_ms(10);//去抖动
               
                if(KEY0==0)             // 键0按下
         {key_up=0;return KEY0_PRESS;}
                else if(KEY1==0)        // 键1按下
                   {key_up=0;return KEY1_PRESS;}
                else if(WK_UP==1)       // 键WK_UP按下
                   {key_up=0;return WKUP_PRESS;}
         
        }
        else if(KEY0==1&&KEY1==1&&WK_UP==0)       // 无键按下
           key_up=1;   // 无键按下,即按键松开       
               
   return 0;   // 无按键按下
}
[/mw_shl_code]
回复 支持 反对

使用道具 举报

1

主题

5

帖子

0

精华

初级会员

Rank: 2

积分
57
金钱
57
注册时间
2016-3-7
在线时间
18 小时
发表于 2016-3-8 23:42:10 来自手机 | 显示全部楼层
忘月19920216 发表于 2016-3-8 13:15
我好像理解你的意思了,我把它改成这样吧?
[mw_shl_code=applescript,true]// 按键处理函数:读取按 ...

嗯嗯,就是这个意思
回复 支持 反对

使用道具 举报

2

主题

15

帖子

0

精华

初级会员

Rank: 2

积分
75
金钱
75
注册时间
2016-3-5
在线时间
26 小时
发表于 2016-3-8 23:56:16 | 显示全部楼层
哈哈,别看我在线时间长,但是积分少啊
回复 支持 反对

使用道具 举报

0

主题

10

帖子

0

精华

初级会员

Rank: 2

积分
99
金钱
99
注册时间
2016-2-21
在线时间
20 小时
发表于 2016-3-9 08:44:21 | 显示全部楼层
顶一个
回复 支持 反对

使用道具 举报

3

主题

261

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2075
金钱
2075
注册时间
2014-2-20
在线时间
284 小时
发表于 2016-3-9 09:36:12 | 显示全部楼层
不错哦
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-9 12:17:21 | 显示全部楼层
蜗蜗猴 发表于 2016-3-8 23:42
嗯嗯,就是这个意思

谢谢提醒,我都没注意到那个标志没有被去抖。
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-9 12:18:21 | 显示全部楼层
凯歌 发表于 2016-3-8 23:56
哈哈,别看我在线时间长,但是积分少啊

才2小时,积分好像涨得好快,我都有几十点了,但是不知道用来做什么啊?
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-9 12:18:37 | 显示全部楼层
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-9 12:19:06 | 显示全部楼层
回复 支持 反对

使用道具 举报

13

主题

276

帖子

0

精华

高级会员

Rank: 4

积分
697
金钱
697
注册时间
2014-6-20
在线时间
146 小时
发表于 2016-3-9 17:37:48 | 显示全部楼层
难得有这样仔细记录的,坚持下去
回复 支持 反对

使用道具 举报

2

主题

8

帖子

0

精华

新手上路

积分
21
金钱
21
注册时间
2016-3-9
在线时间
3 小时
发表于 2016-3-9 20:28:54 | 显示全部楼层
认同与他人
回复 支持 反对

使用道具 举报

2

主题

8

帖子

0

精华

新手上路

积分
21
金钱
21
注册时间
2016-3-9
在线时间
3 小时
发表于 2016-3-9 20:29:12 | 显示全部楼层
加油
回复 支持 反对

使用道具 举报

2

主题

8

帖子

0

精华

新手上路

积分
21
金钱
21
注册时间
2016-3-9
在线时间
3 小时
发表于 2016-3-9 20:29:28 | 显示全部楼层
不错啊
回复 支持 反对

使用道具 举报

2

主题

8

帖子

0

精华

新手上路

积分
21
金钱
21
注册时间
2016-3-9
在线时间
3 小时
发表于 2016-3-9 20:29:59 | 显示全部楼层
很厉害
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165538
金钱
165538
注册时间
2010-12-1
在线时间
2117 小时
发表于 2016-3-9 21:48:10 | 显示全部楼层
不错,继续加油.
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-10 23:49:06 | 显示全部楼层
【2016-03-10】串口配置与基本收发测试
这两天看了比较久串口相关的资料,然后去写程序,只写了简单的测试收发的程序,结果还出了许多问题
1、查看板子电路图,找到串口的两根线分别连到MCU的IO口的哪个引脚;
2、查看原子串口实验例程并理解过程;
3、关掉例程,自己之前的BEEP工程(里面有LED,BEEP功能),并编写串口收发测试程序;
4、仿真调试,思考总结,记录发帖;


1、查看板子电路图,找到串口的两根线分别连到MCU的IO口的哪个引脚;
    精英板:USART1_TX -> GPIOA.9;USART1_RX -> GPIOA.10;
2、查看原子串口实验例程并理解过程;
    相比之前IO口的简单使用,配置的时候,除了要配置IO口外,还需要配置串口;
    串口收发测试程序编写思路:①配置IO口、串口1时钟->②配置IO口、串口1->③不停地收和发(将收到的数据放在一个变量里,不停地发送),并加适当延时(开始没加延时,结果把调试助手卡死了);
3、关掉例程,自己之前的BEEP工程(里面有LED,BEEP功能),并编写串口收发测试程序;
    老套路,写一个初始化函数,对串口及其IO口进行配置,然后在main函数里使用;
    下面是uart.c(这里的库函数对我来说变得有点复杂了,还没学到中断,于是就只是配置了串口基本参数):
[mw_shl_code=applescript,true]#include "uart.h"
#include "stm32f10x.h"


void uart_init(u32 baud)
{
   GPIO_InitTypeDef GPIO_InitStructure;
   USART_InitTypeDef USART_InitStructure;

   // 初始化时钟
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA , ENABLE);   // 使能USART1、PA时钟
   
   // 初始化IO口
   // USART1_TX -> GPIOA.9;
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        //模式设置为复用推挽输出
   GPIO_Init(GPIOA, &GPIO_InitStructure);
   
   // USART1_RX -> GPIOA.10;
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;        //模式设置为浮空输入
   GPIO_Init(GPIOA, &GPIO_InitStructure);
   
   // 初始化串口1
   USART_DeInit(USART1);
   USART_InitStructure.USART_BaudRate = baud;   // 波特率
   USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 字长:8B
   USART_InitStructure.USART_StopBits = USART_StopBits_1;   // 停止位:1
        USART_InitStructure.USART_Parity = USART_Parity_No;   // 奇偶校验位:无
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;  // 硬件流控制:无
        USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;   // 收、发

   USART_Init(USART1, &USART_InitStructure); // 初始化串口1
   USART_Cmd(USART1, ENABLE);    // 使能串口1
  
}   
[/mw_shl_code]
    然后写一个uart.h:
[mw_shl_code=applescript,true]#ifndef __UART_H
#define __UART_H         
#include "sys.h"

void uart_init(u32 baud);
                                                     
#endif
[/mw_shl_code]
    然后在main.c里使用自己配的串口:
[mw_shl_code=applescript,true]#include "stm32f10x.h"
#include "led.h"
#include "beep.h"
#include "uart.h"
#include "delay.h"

int main(void)
{       
   u16 data_buf;
   
   LED_Init();  
   BEEP_Init();
   uart_init(115200);
   delay_init();
   
   LED1 = 0;
   BEEP = 1;
   delay_ms(200);
   LED0 = 0;
   LED1 = 1;
   BEEP = 0;
   
   while(1)
        {
      data_buf = USART_ReceiveData(USART1);
      USART_SendData(USART1,data_buf);
      delay_ms(500);
        }
}[/mw_shl_code]
4、仿真调试,思考总结,记录发帖;

    一开始编译的时候报错提示我串口初始化函数的函数名有问题,“Undefined symbol UART_Init (referred from main.o)”,但是我函数名都是复制的,不会是因为打错,头文件都正常,我又再复制了一边,还是报错,后面看着我自己明明的“UART_Init”函数名跟库函数的“USART_Init”长得这么像,是不是不能用“UART_Init”作为自己的函数名,于是仿照着例程全部用小写“uart_init”,结果就过了,下载后,运行结果是我预想的,但是我发送得比较快,之间没有加延时,把串口调试助手给卡死了,后来加了点延时,就正常了,已证明串口收发功能正常;
    关于函数名“UART_Init”,我又去库函数stm32f10x_usart.c和stm32f10x_usart.h里搜了下,没搜到同名的,还没想通,还没去网上仔细搜,先记着这个问题;
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-10 23:52:38 | 显示全部楼层
313668972 发表于 2016-3-9 17:37
难得有这样仔细记录的,坚持下去

谢谢支持,我计划系统地学完STM32(基础部分),同时把每次学习都发帖记录
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-10 23:52:55 | 显示全部楼层
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-10 23:53:23 | 显示全部楼层

谢谢原子哥,都是跟着原子哥的教程和例程在学
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-11 12:47:06 | 显示全部楼层
昨晚还截了串口调试的截图忘记发上来了,现在补上,留作纪念

而且一不小心还发现拔了J-Link之后板子的程序就不跑了,情况是这样的:
    我要拔掉J-link的USB,插上自己的USB转串口试下,我拔的是USB接电脑那一端,发现板子上串口开启的指示灯灭了,然后插上,又重新启动了(重新初始化串口,我加了蜂鸣器,初始化的时候会响一下),心里纳闷这板子还不想让我插自己的USB转串口了(电脑这一边的USB已插满,想就近拔掉J-link换自己的串口),于是就只能两个都插着,测试了一下,自己的USB转串口是可用的,今天突然想到STM32的JTAG口上一般设计有一根复位信号线,可能是这根线造成的影响,于是去看了下原理图,果然是有:

当然我现在还没有找到资料或证据确认是这个的原因,等回宿舍的时候试一下把J-link跟板子连接的排线拔了看板子自己能不能跑程序,顺便测一下复位线的电平在拔下前后的变化;

串口调试截图

串口调试截图

JTAG插座

JTAG插座

STM32复位引脚25脚

STM32复位引脚25脚

25脚特性

25脚特性
回复 支持 反对

使用道具 举报

8

主题

37

帖子

0

精华

初级会员

Rank: 2

积分
160
金钱
160
注册时间
2014-9-7
在线时间
18 小时
发表于 2016-3-11 14:53:28 | 显示全部楼层
顶楼主,加油
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-12 01:10:07 | 显示全部楼层
今天回来后发现自己昨天的“UART_Init”函数名错误原来是自己编写的uart.c文件没有添加到工程里去,工程里的SYSTEM里面有一个原子的usart.c,里面的初始化函数的函数名为“uart_init”,所以我后面修改成“uart_init”后没再报错,今天把SYSTEM里面那个文件删了,添加自己的uart.c,跑起来还是有些问题,串口助手向板子连续发送的时候,板子就会异常,不再能刷新接收到的数据,总感觉是接收数据寄存器或者是状态寄存器那里因为连续发送的时候,我没有及时读出来,导致满了的原因,于是查了下文档,在读数据之前强行清除了状态寄存器的 [size=13.3333px]RXNE位,程序跑起来才正常(当然我不清楚这样可不可以,但是想到这个程序的目的是为了测试下串口能不能收发,于是就先跑通再说,周末准备学下中断,将串口中断用上来):[mw_shl_code=applescript,true]   while(1)
        {
      USART1->SR &= ~(1<<5);  // 读之前清零一下
      data_buf = USART_ReceiveData(USART1);
      USART_SendData(USART1,data_buf);
      delay_ms(500);
        }[/mw_shl_code]

RXNE位

RXNE位

终于能收发了

终于能收发了
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-12 01:17:43 | 显示全部楼层

谢谢
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-12 01:40:23 | 显示全部楼层
想了想,还是用中断接收吧,正好也学习了中断还没实际配过,于是就直接在原工程上改了程序,增加了串口接收中断;
【2016-03-11】使用串口中断接收
1、查看原子串口实验例程并理解过程;
2、关掉例程,直接在自己的老串口测试工程上增加串口接收中断部分;
3、仿真调试,思考总结,记录发帖;

1、查看原子串口实验例程并理解过程;
    相比之前IO口的简单使用,配置的时候,除了要配置IO口外,还需要配置串口;
    串口中断接收程序编写思路:①配置IO口、串口1时钟->②配置IO口、串口1、串口1中断->③在接收中断服务函数里读取串口1接收数据寄存器的值存到一个变量里->④不停地用串口发送将接收到的数据发送出去,中间加适当延时;
2、关掉例程,直接在自己的老串口测试工程上增加串口接收中断部分;
    直接修改自己之前的uart.c,增加串口1接收中断(直接复制过来的,没有仔细去找中断每一个配置的具体信息及其寄存器资料);
    下面是增加串口1接收中断的uart.c:[mw_shl_code=applescript,true]#include "uart.h"
#include "usart.h"
#include "stm32f10x.h"

u16 data_buf;

void UART_Init(u32 baud)
{
   GPIO_InitTypeDef GPIO_InitStructure;
   USART_InitTypeDef USART_InitStructure;
   NVIC_InitTypeDef NVIC_InitStructure;

   // 初始化时钟
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA , ENABLE);   // 使能USART1、PA时钟
   
   // 初始化IO口
   // USART1_TX -> GPIOA.9;
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        //模式设置为复用推挽输出
   GPIO_Init(GPIOA, &GPIO_InitStructure);
   
   // USART1_RX -> GPIOA.10;
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;        //模式设置为浮空输入
   GPIO_Init(GPIOA, &GPIO_InitStructure);

   // 初始化串口1
   USART_DeInit(USART1);
   USART_InitStructure.USART_BaudRate = baud;   // 波特率
   USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 字长:8b
   USART_InitStructure.USART_StopBits = USART_StopBits_1;   // 停止位:1
        USART_InitStructure.USART_Parity = USART_Parity_No;   // 奇偶校验位:无
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;  // 硬件流控制:无
        USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;   // 收、发
   
   // Usart1 NVIC 配置
   NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;  // 选择串口1
   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;   // 抢占优先级3
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;                // 子优先级3
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // IRQ通道使能
   NVIC_Init(&NVIC_InitStructure);  // 根据指定的参数初始化VIC寄存器
   
   USART_Init(USART1, &USART_InitStructure); // 初始化串口1
   USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);  // 开启串口1接收中断
   USART_Cmd(USART1, ENABLE);    // 使能串口1
  
}   

// 串口1中断服务函数
void USART1_IRQHandler(void)
{
   data_buf = USART_ReceiveData(USART1);        // 收到一个数据就读掉一个
}
[/mw_shl_code]
    uart.h稍作修改,增加一个变量声明,用于暂存接收到的数据:
[mw_shl_code=applescript,true]#ifndef __UART_H
#define __UART_H         
#include "sys.h"

extern u16 data_buf;

void UART_Init(u32 baud);
                                                     
#endif
[/mw_shl_code]
    main.c也稍作修改(晚上怕吵到别人,把BEEP提示也不要了):
[mw_shl_code=applescript,true]#include "stm32f10x.h"
#include "led.h"
#include "beep.h"
#include "uart.h"
#include "delay.h"

int main(void)
{       
   LED_Init();  
   BEEP_Init();
   UART_Init(115200);
   delay_init();
   
   LED1 = 0;
//   BEEP = 1;
   delay_ms(200);
   LED0 = 0;
   LED1 = 1;
   BEEP = 0;
   
   while(1)
        {
//      USART1->SR &= ~(1<<5);
//      data_buf = USART_ReceiveData(USART1);
      USART_SendData(USART1,data_buf);
      delay_ms(500);
        }
}
[/mw_shl_code]
3、仿真调试,思考总结,记录发帖;
    程序跑起来,很正常,就是中断写得很马虎(其实就算是复制的),周末要解决不会用中断的问题,等会了中断的基本使用之后,写起程序来应该会方便许多。

串口调试截图.png
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-13 00:04:19 | 显示全部楼层
【2016-03-12】继续学习串口,补学中断
学习了嵌套向量中断后,对自己昨天的串口初始化程序作了代码补充和注释补充,加深了对串口配置和嵌套向量中断配置的了解;
代码修改为如下,在板子上跑了一会没有发现问题:
[mw_shl_code=applescript,true]void UART_Init(u32 baud)
{
   GPIO_InitTypeDef GPIO_InitStructure;
   USART_InitTypeDef USART_InitStructure;
   NVIC_InitTypeDef NVIC_InitStructure;

   // 初始化时钟
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA , ENABLE);   // 使能USART1、PA时钟
   
   // 初始化串口的GPIO口
   // USART1_TX -> GPIOA.9;
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        //模式设置为复用推挽输出
   GPIO_Init(GPIOA, &GPIO_InitStructure);
   // USART1_RX -> GPIOA.10;
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;        //模式设置为浮空输入
   GPIO_Init(GPIOA, &GPIO_InitStructure);

   // 初始化串口1
   USART_InitStructure.USART_BaudRate = baud;   // 波特率
   USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 字长:8b
   USART_InitStructure.USART_StopBits = USART_StopBits_1;   // 停止位:1
        USART_InitStructure.USART_Parity = USART_Parity_No;   // 奇偶校验位:无
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;  // 硬件流控制:无
        USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;   // 收、发
//   USART_DeInit(USART1);   // 复位串口1
   USART_Init(USART1, &USART_InitStructure); // 初始化串口1
   // 开启串口1接收中断
   USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);  // 开启串口1中断  
   // 开启串口1
   USART_Cmd(USART1, ENABLE);    // 使能串口1
   
   // Usart1 NVIC 配置
   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 优先级分组位长度模式2:先占优先级 2 位,从优先级 2 位
                                                   // 如果在其他地方配置过NVIC_PriorityGroup,则不用在这里配置
   // 配置串口1中断
   NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;  // 选择串口1
   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;   // 抢占优先级3
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;                // 子优先级3
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // IRQ通道使能
   NVIC_Init(&NVIC_InitStructure);  // 根据指定的参数初始化VIC寄存器
     
} [/mw_shl_code]
NVIC相关库函数.png
设置优先级分组位长度模式.png
初始化指定的外设的NVIC寄存器.png
初始化NVIC寄存器的示例代码(跟实际库的宏定义的名称有点差别).png
串口相关库函数.png
按指定参数初始化USARTx寄存器.png
USART_InitStructure中的一些成员可选参数1.png
USART_InitStructure中的一些成员可选参数2.png
USART_InitStructure中的一些成员可选参数3.png
NVIC_Init串口中断初始化示例.png
使能、失能指定串口.png
使能、失能指定串口中断.png
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-13 13:39:41 | 显示全部楼层
在中断这里吃了很多苦头,还是因为自己资料没看全,开始想不看教程和例程,直接看固件库使用手册和STM32参考手册就完成程序,结果整了几个小时中断都还没有正常工作,仅仅因为参考手册的外部中断/事件线路映像我没有去看,这个一定要记着,使用外设的时候要记得看它的映射关系;
【2016-03-13】原子SYSTEM->usart.c使用和外部中断使用
外部中断配置过程:
①初始化想要映射的IO口为输入;
②开启AFIO功能复用IO时钟;
③将外部中断映射到具体的IO口;
④根据对应中断初始化EXTI寄存器;
⑤根据对应中断初始化NVIC寄存器;
⑥编写对应的中断服务函数;
本次学习中遇到过的问题:
①没有开启AFIO功能复用IO时钟;
②没有了解外部中断/事件线路映像;


1、查看板子电路图,物色作为外部中断的IO口;
2、查看库函数指南、参考手册对应的内容,查看原子的例程并理解过程;
3、复制自己的串口工程(里面使用了SYSTEM文件夹里的usart.c,还包含以前自己写的LED,BEEP,KEY),写代码;
4、仿真调试,思考总结,记录发帖;


1、查看板子电路图,物色作为外部中断的IO口;
    就选择按键KEY0和KEY1所连接的IO口作为外部中断源;
2、查看库函数指南、参考手册对应的内容,查看原子的例程并理解过程;
外部中断配置过程:
    ①初始化想要映射的IO口为输入;
    ②开启AFIO功能复用IO时钟;
    ③将外部中断映射到具体的IO口;
    ④根据对应中断初始化EXTI寄存器;
    ⑤根据对应中断初始化NVIC寄存器;
    ⑥编写对应的中断服务函数;   
3、复制自己的串口工程(里面使用了SYSTEM文件夹里的usart.c,还包含以前自己写的LED,BEEP,KEY),写代码;
    串口直接使用SYSTEM文件夹里的usart.c;
    然后按照外部中断配置步骤编写exti.c:
[mw_shl_code=applescript,true]#include "exti.h"
#include "stm32f10x_exti.h"
#include "key.h"
#include "usart.h"
#include "led.h"
#include "delay.h"

u8 state = 0;

void BOARD_EXTI_Init(void)
{  
   EXTI_InitTypeDef EXTI_InitStructure;
   NVIC_InitTypeDef NVIC_InitStructure;
   
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);        //使能复用功能时钟

   // 初始化外部中断EXTI寄存器
   // KEY0 -> PE4 -> EXTI4;
   GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);  // 外部中断4映射到PE4
   EXTI_InitStructure.EXTI_Line = EXTI_Line4;  // 外部中断4
   EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;   // 设置为中断请求
   EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;   // 设置为下降沿触发
   EXTI_InitStructure.EXTI_LineCmd = ENABLE;    // 开启中断
   EXTI_Init(&EXTI_InitStructure);
   // KEY1 -> PE3 -> EXTI3;
   GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3);  // 外部中断3映射到PE3
          EXTI_InitStructure.EXTI_Line=EXTI_Line3;
          EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;       
          EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
          EXTI_Init(&EXTI_InitStructure);                  //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
   
   // 配置外部中断NVIC寄存器
   NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;  // 外部中断3
   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;   // 抢占优先级2
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;                // 子优先级2
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // IRQ通道使能
   NVIC_Init(&NVIC_InitStructure);  // 根据指定的参数初始化VIC寄存器
   NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;  // 外部中断4
   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;   // 抢占优先级2
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;                // 子优先级2
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // IRQ通道使能
   NVIC_Init(&NVIC_InitStructure);  // 根据指定的参数初始化VIC寄存器

}

// 外部中断3中断服务函数
// KEY1 -> PE3 -> EXTI3;
void EXTI3_IRQHandler(void)
{
   delay_ms(5);
   
   if(KEY1 == 0)
   {
      EXTI->IMR = 0x0;// 屏蔽外部中断
      state = EXTI3_TRIGGER;
   }
   else
      ;
   EXTI_ClearITPendingBit(EXTI_Line3);  //清除LINE3上的中断标志
}

// 外部中断4中断服务函数
// KEY0 -> PE4 -> EXTI4;
void EXTI4_IRQHandler(void)
{
   delay_ms(5);
   
   if(KEY0 == 0)
   {
      EXTI->IMR = 0x0;  // 屏蔽外部中断
      state = EXTI4_TRIGGER;
   }
   else
      ;
   
   EXTI_ClearITPendingBit(EXTI_Line4);  //清除LINE4上的中断标志
}
[/mw_shl_code]
    然后为其写一个exti.h:
[mw_shl_code=applescript,true]#ifndef __EXTI_H
#define __EXTI_H         
#include "sys.h"

#define  NO_TRIGGER    0
#define  EXTI3_TRIGGER 1
#define  EXTI4_TRIGGER 2
extern u8 state;

void BOARD_EXTI_Init(void);
                                                     
#endif
[/mw_shl_code]
    IO口初始化就在key.c的KEY_Init函数里初始化了;
    最后main.c这样写:
[mw_shl_code=applescript,true]#include "stm32f10x.h"
#include "led.h"
#include "beep.h"
#include "usart.h"
#include "delay.h"
#include "exti.h"
#include "key.h"

int main(void)
{       
   u8 already_printf = 0;
   
   LED_Init();  
   BEEP_Init();
   KEY_Init();
   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
   uart_init(115200);
   delay_init();
   BOARD_EXTI_Init();
   
   // 初始化成功提示
   LED1 = 0;
   BEEP = 1;
   delay_ms(100);
//   LED0 = 0;
   LED1 = 1;
   BEEP = 0;
   printf("\r\n\r\n\r\n请按键触发中断\r\n");
   
   while(1)
        {
      switch(state)
      {
         case NO_TRIGGER:
                  EXTI->IMR |= (1<<3)|(1<<4);
                  LED0 = 0;
                  LED1 = 0;
                  already_printf = 0;
                  break;
         case EXTI3_TRIGGER:
                  LED0 = 0;
                  LED1 = 1;
                  if(already_printf ==0)
                  {
                     already_printf = 1;
                     printf("\r\n触发外部中断3,已锁定为EXTI3_TRIGGER状态,请发送任意字符加回车返回初始状态\r\n");
                     break;
                  }
                  else if(USART_RX_STA&0x8000)
                  {
                     printf("\r\n发送成功,返回初始状态...\r\n");
                     printf("\r\n\r\n已返回初始状态,请按键触发中断\r\n");
                     state = NO_TRIGGER;
                     USART_RX_STA &= 0x0;
                     break;
                  }
                  else
                  {
                     break;
                  }
         case EXTI4_TRIGGER:
                  LED0 = 1;
                  LED1 = 0;
                  if(already_printf ==0)
                  {
                     already_printf = 1;
                     printf("\r\n触发外部中断4,已锁定为EXTI4_TRIGGER状态,请发送任意字符加回车返回初始状态\r\n");
                     break;
                  }
                  else if(USART_RX_STA&0x8000)
                  {
                     printf("\r\n发送成功,返回初始状态...\r\n");
                     printf("\r\n\r\n已返回初始状态,请按键触发中断\r\n");
                     state = NO_TRIGGER;
                     USART_RX_STA &= 0x0;
                     break;
                  }
                  else
                  {
                     break;
                  }
         default:
                  LED0 = 1;
                  LED1 = 1;
                  printf("\r\n进入未知状态,1秒后强制跳转到初始状态...\r\n");
                  delay_ms(1000);
                  printf("\r\n已返回初始状态,请按键触发中断\r\n");
                  state = NO_TRIGGER;
                  break;
      }
   }
}
[/mw_shl_code]
4、仿真调试,思考总结,记录发帖;

    调试的结果,预想的是,在初始状态可以按键触发外部中断,进入中断后屏蔽所有外部中断,不能再触发,然后串口向板子发送一个回车后再次进入初始状态,可以继续按键触发对应中断;
    这次由于资料没看全,导致时钟和中断映射没有配好(开始是以为IO口能作为任意一号外部中断,想把PE4、PE3分别作为外部中断0和1,可是怎么也找不到映射到0和1的函数或者寄存器),后面看了教程后,才知道自己没有去看外部中断的映射方式;将所有需要配置的内容配置好了之后,程序运行正常,运行结果是自己预想的;




串口助手.png
选择GPIO管脚用作外部中断线路.png
EXTI库函数.png
EXTI_Init.png
EXTI_InitTypeDef部分成员可设参数及EXTI_Init使用示例.png
中断屏蔽寄存器.png
回复 支持 反对

使用道具 举报

15

主题

55

帖子

0

精华

初级会员

Rank: 2

积分
154
金钱
154
注册时间
2016-1-18
在线时间
61 小时
发表于 2016-3-13 15:06:48 | 显示全部楼层
加油,加油,同为初学,像楼主学习!!
回复 支持 反对

使用道具 举报

1

主题

7

帖子

0

精华

新手上路

积分
40
金钱
40
注册时间
2016-3-10
在线时间
5 小时
发表于 2016-3-14 10:17:11 | 显示全部楼层
新人报道!!一起学习!
回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-15 00:51:03 | 显示全部楼层
今天去逛街去了,很晚才回宿舍,不过独立看门狗的程序很快就编好了
【2016-03-15】独立看门狗实验
独立看门狗配置过程:
①取消IWDG_PR 和 IWDG_RLR写保护;
②配置预分频值(将预分频值写入IWDG_PR );
③配置重装载值(将重装载值写入IWDG_PLR );
④重装载一次计数值(喂狗);
⑤开启看门狗;
注意:
①独立看门狗有自己的时钟,因此不需要从时钟桥获取时钟;
②独立看门口开启了就不能关闭;由于独立看门狗时钟不精确,喂狗时间尽量靠前,不要太极限;



1、查看库函数指南、参考手册对应的内容,查看原子的例程并理解过程;
2、复制自己的外部中断工程(包含以前的LED,BEEP,KEY,USART,EXTI),写代码;
3、仿真调试,思考总结,记录发帖;


1、查看库函数指南、参考手册对应的内容,查看原子的例程并理解过程;
    独立看门狗配置过程:
    ①取消IWDG_PR 和 IWDG_RLR写保护;
    ②配置预分频值(将预分频值写入IWDG_PR );
    ③配置重装载值(将重装载值写入IWDG_PLR );
    ④重装载一次计数值(喂狗);
    ⑤开启看门狗;

2、复制自己的串口工程(包含以前的LED,BEEP,KEY,USART),写代码;
    直接从库使用手册复制库函数,写好iwdg.c:
[mw_shl_code=applescript,true]#include "iwdg.h"

void IWDG_Init(void)
{
   IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);   // 使能写操作
   IWDG_SetPrescaler(IWDG_Prescaler_16);     // 设置预分频器IWDG_PR为16分频,最长复位时间1.6s
   IWDG_SetReload(0xFFF);     // 设置重装载寄存器IWDG_RLR
   IWDG_ReloadCounter();      // 重载一次计数器
   IWDG_Enable();    // 开启独立看门口IWDG
   IWDG_WriteAccessCmd(IWDG_WriteAccess_Disable);  // 去使能写操作
                                                   //这里只是不允许写IWDG_PR和IWDG_RLR,键寄存器IWDG_KR还是可写
}
[/mw_shl_code]
然后在按键的外部中断服务函数里加上喂狗:
[mw_shl_code=applescript,true]// 外部中断3中断服务函数
// KEY1 -> PE3 -> EXTI3;
void EXTI3_IRQHandler(void)
{
   delay_ms(5);
   
   if(KEY1 == 0)
   {
      IWDG_ReloadCounter();
      printf("\r\n\r\n喂狗成功,请在1秒内再次喂狗,否则重启!\r\n");
   }
   else
      ;
   
   EXTI_ClearITPendingBit(EXTI_Line3);  //清除LINE3上的中断标志
}

// 外部中断4中断服务函数
// KEY0 -> PE4 -> EXTI4;
void EXTI4_IRQHandler(void)
{
   delay_ms(5);
   
   if(KEY0 == 0)
   {
      IWDG_ReloadCounter();
      printf("\r\n\r\n喂狗成功,请在1秒内再次喂狗,否则重启!\r\n");
   }
   else
      ;
   
   EXTI_ClearITPendingBit(EXTI_Line4);  //清除LINE4上的中断标志
}
[/mw_shl_code]
    然后在main.c里复位时给一个BEEP提示,和相关LED提示:
[mw_shl_code=applescript,true]int main(void)
{       
   LED_Init();  
   BEEP_Init();
   KEY_Init();
   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
   uart_init(115200);
   delay_init();
   BOARD_EXTI_Init();
   IWDG_Init();
   
   // 初始化成功提示
   LED1 = 0;
   BEEP = 1;
   delay_ms(20);
//   LED0 = 0;
   LED1 = 1;
   BEEP = 0;
//   printf("\r\n\r\n请按键喂狗,否则重启!\r\n");
   while(1)
        {
      LED0 = 0;
      printf("\r\n\r\n请按键喂狗,否则重启!\r\n");
      delay_ms(1500);
      delay_ms(1500);
   }
}
[/mw_shl_code]
3、仿真调试,思考总结,记录发帖;

    看门狗正常工作,不喂狗就会复位


回复 支持 反对

使用道具 举报

2

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
153
金钱
153
注册时间
2016-2-25
在线时间
27 小时
 楼主| 发表于 2016-3-15 00:52:03 | 显示全部楼层
whispertome 发表于 2016-3-14 10:17
新人报道!!一起学习!

一起学习!
回复 支持 反对

使用道具 举报

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

本版积分规则



关闭

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

正点原子公众号

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

GMT+8, 2025-6-15 16:07

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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