发新帖本帖赏金 20.00元(功能说明)我要提问
12下一页
返回列表
[STM32F1]

大布丁丁-STM32F10x库函数学习笔记

[复制链接]
5562|38
手机看帖
扫描二维码
随时随地手机跟帖
13395410441|  楼主 | 2016-1-29 11:30 | 显示全部楼层 |阅读模式
本帖最后由 13395410441 于 2016-2-4 16:48 编辑

(郑重提示:大布丁丁是我和我女朋友做的微信公众号,过段时间放点小游戏大家没事娱乐娱乐,也算加点人气,希望大家多多关注,没有什么盈利目的,因为没有什么盈利途径,哈哈)


从这篇开始,我准备连载一套笔记作为学习过程的记录,也可以供给大家参考,我遇到的问题可能大家以后也会遇到。大家共勉吧。本人2011年进入大学,在学校学习了51,做过MSP430、STM32的几个小项目。其中STM32的是国家创新项目,当时我和一位师哥在做,有一万块钱的研发资金奥(小吹一下,我感觉在学校有人给研发资金真的很兴奋)。大四下学期接触了LINUX,投入学习并从事了这方面的工作,然而越走越远,越走越远离底层,根本没机会接触单片机。
做了一年的linux平台的软件开发,真是太想念单片机了。于是想重新学习STM32并准备从事这方面的工作。所以准备写这个连载笔记,记录我学习的过程。
我看他们写教程都是打广告的啊,我没有广告可以打奥,哈哈!做了一段时间的软件回头再看单片机会是什么感觉呢?下一篇再说吧!
不过我是以库函数进行学习的奥。而且一版本库给的实例作为基础进行扩展。
特此声明:本人发布的帖子没有个人商业价值,引用到大神们的资料,请不要说我侵权啊,我只是写个学习笔记,请大神们支持,不胜感谢。
备注:我的开发板芯片是STM32F103C8T6。51的P0和STM32的PB8 - PB15 一同对应 8个LED跑马灯。另一端接地,高电平亮灯。
1、原理图JR1-JR8 对应JP12  到时候用的时候跳线连接JP10和JP12
2、开关左边的跳帽接上
3、晶振左边的两根线接在STM32系统板上
STM32F10X_MD,USE_STDPERIPH_DRIVER
startup_stm32f10x_md.s

28页是变量名有数据类型

--------------------------------------------------华丽的分割线----------------------------------------
等学习一段时间后定时进行总结,到时候再写个总结笔记链接进来。学习笔记毕竟是学习过程,会有出错的地方希望大家多多指教

大布丁丁-大家趋之若鹜的嵌入式-真的是你的方向吗(嵌入式和单片机的比较)

大布丁丁-这里是常用外设的总结,一起做笔记

大布丁丁-一个是百度文库免费下载工具,一个是PDF分隔合并工具

大布丁丁-STM32F10x库函数学习笔记00-前篇

大布丁丁-STM32F10x库函数学习笔记01-GPIO点灯

大布丁丁-STM32F10x库函数学习笔记02-RCC配置

大布丁丁-STM32F10x库函数学习笔记03-SysTick滴答时钟延时

大布丁丁-STM32F10x库函数学习笔记04-1-外部中断EXTI

大布丁丁-番外-中断优先级的概念

大布丁丁-STM32F10x库函数学习笔记05-闪存FLASH的读写

大布丁丁-STM32F10x库函数学习笔记06-24C02的I2C时序

















打赏榜单

21ic小喇叭 打赏了 20.00 元 2016-02-02
理由:学习笔记分享

评分
参与人数 1威望 +10 收起 理由
icecut + 10 先加点分吧.
wsnsyy| | 2016-1-29 11:38 | 显示全部楼层
期待你的更新呢

使用特权

评论回复
13395410441|  楼主 | 2016-1-29 12:47 | 显示全部楼层

大布丁丁-STM32F10x库函数学习笔记00-前篇

本帖最后由 13395410441 于 2016-2-3 11:17 编辑

因为之前买的原子哥的板子放在老家了,所以就去淘宝逛了逛,发现有卖51和STM32一体的开发板,就买了一个,顺便玩玩51。
开发板只是个硬件工具,买哪个都一样,没什么区别。
个人感觉STM32的学习思路:首先根据版本库学习STM32,等版本库学熟悉了,没事再改改寄存器什么的。但是我觉得实际项目中开发效率是很重要的,所以能用版本库,尽量不要自己写寄存器操作。大神的话那就没办法了,既然是大神自然不满足版本库,自己动手写自己封装的库也是轻而易举的事情。

所以我学习STM32就从版本库开始:
学习开发环境:
KEIL 4.7.2a (KEIL5功能是改动了很多,但是还是感觉4.7.2a版本稳定,只是作为开发工具,功能够用就行,没必要什么都求新)
TKStudio (稍后会用到)
版本库STM32F10x_StdPeriph_Lib_V3.5.0 (没什么好说的)
STM32固件库使用手册的中文翻译版.pdf (中国人嘛,看中文版的还是比英文版的简单很多)
STM32F10x参考手册第二版.pdf (手册第一版第二版都行吧)


我把用到的资料上传到这里吧:
KEIL和TKStudio 太大,自己百度就可以下载。


KEIL4在edit -> configuation ->text completion勾选前三项,第三项设为3,就可以自动补全,完美。
基本上准备工作完成了,下面就正式开始学习了

工程项目模板.zip

281.1 KB

版本库的工程模板,拿来直接用

STM32F10x参考手册第二版.pdf

3.69 MB

J-LIN仿真器操作步骤.pdf

202.23 KB

STM32的函数说明(中文).pdf

2.79 MB

stm32f103中文.pdf

457.98 KB

MDK环境下利用STM32库V3.5创建工程并调试的方法—00001.pdf

3.7 MB

MDK环境下利用STM32库V3.5创建工程并调试的方法—00002.pdf

3.78 MB

MDK环境下利用STM32库V3.5创建工程并调试的方法—00003.pdf

1.13 MB

第二讲:源源不绝.pdf

397.58 KB

第六讲:川流不息.pdf

281.97 KB

第三讲:事出有因.pdf

429.24 KB

第四讲:白驹过隙.pdf

284.11 KB

第五讲:争分夺秒.pdf

396.38 KB

第一讲:泾渭分明.pdf

381.43 KB

稀里糊涂STM32F4.pdf

1.43 MB

解析STM32的库函数.pdf

310.69 KB

解析STM32的启动过程.zip

11.71 KB

使用特权

评论回复
13395410441|  楼主 | 2016-1-29 14:03 | 显示全部楼层
写了半天,一个手误清空了。唉!重新写

使用特权

评论回复
13395410441|  楼主 | 2016-1-29 14:12 | 显示全部楼层

大布丁丁-STM32F10x库函数学习笔记01 - GPIO点灯

本帖最后由 13395410441 于 2016-2-3 11:17 编辑

先看看稀里糊涂学STM32第一讲,看完再说。(这里说明一下,版本库的模板根据个人情况而定,基本文件有就可以,文件目录怎么安排个人怎么喜欢怎么来,没必要非得一样)
看完了吧。库函数的结果也了解的差不多了吧,上一篇我分享的文档看一看,先了解库函数,并且会配置KEIL了咱再继续。写函数添加stm32f10x.h就算包含库了。

稀里糊涂学STM32第一讲中的芯片架构图中说AHB桥接APB2  APB1,GPIO就在APB2上,记住这一点。
好了,那咱们先打开STM32F10X参考手册和STM32固件库使用手册,边写代码边查资料。这一节咱们需要看的就是RCC和GPIO这两节吧。先粗略过一遍,用到现查就可以。

打开固件库例程 STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples\GPIO\JTAG_Remap中的main.c
下面是文件中对IO口的配置和数据读写代码
//初始化
GPIO_InitTypeDef GPIO_InitStructure;//在固件库中查找发现这个是含三个成员的用于IO初始化的结构体,在stm32f10x_gpio.h中定义。下面是三个成员的初始化
//GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15可以用宏来描述,这样代码更简洁,下面流水灯中再实现
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;//第13 14 15个引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//在固件库中查找发现这个是最高频率
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//在固件库中查找发现这个是推挽输出
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//上面说到GPIO在APB2中,时钟初始化就是这个函数
GPIO_Init(GPIOB, &GPIO_InitStructure);//把GPIO_InitStructure变量和GPIOB关联,就是对GPIOB的13 14 15引脚进行初始化

//位读写
GPIO_WriteBit(GPIOB, GPIO_Pin_13, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_13)));
GPIO_WriteBit(GPIOB, GPIO_Pin_13, Bit_SET);//Bit_SET是BitAction枚举中的1值
GPIO_WriteBit(GPIOB, GPIO_Pin_13, Bit_RESET);是BitAction枚举中的0值
到固件库手册查找GPIO_WriteBit会发现有个GPIO_Write一直对应,是用来对GPIO整个进行读写的一套函数。


根据上面的函数可以写一个流水灯,以GPIOB高八位(8 - 15为例)
自己写代码的时候根据自己板子LED连接的引脚为准。

#define PIN_LED         (GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15)
void LED_Init(void)
{
                GPIO_InitTypeDef GPIO_InitStructure;
                GPIO_InitStructure.GPIO_Pin = PIN_LED;
                GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
                GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
                GPIO_Init(GPIOB, &GPIO_InitStructure);
}
void LED_Sets(uint8_t data)
{
        uint16_t setValue;
        setValue = GPIO_ReadOutputData(GPIOB);//读取GPIOB的引脚值
        setValue &= 0x00ff;                                
        setValue |= ((uint16_t)data << 8);//如果是低八位引脚0-7PIN,就不用右移八位了,直接setValue |= (uint16_t)data;
        GPIO_Write(GPIOB, setValue);//写引脚,让IO电平改变
}

void Delay(uint16_t c);
int main(void)
{
        uint8_t endValue = 0;
        uint8_t count = 0;
        endValue = 0x01;
        LED_Init();
        while(1)
        {        
                for(count=0; count<15; count++)//左移16次
                {                                                                                                
                        LED_Sets(endValue);
                        if(endValue == 0x80)
                        {
                                endValue = 0x01;
                        }else{
                                endValue <<= 1;
                        }
                        
                        Delay(10);
                }
                for(count=0; count<15; count++)//右移16次
                {                                                                                                
                        LED_Sets(endValue);
                        if(endValue == 0x01)
                        {
                                endValue = 0x80;
                        }else{
                                endValue >>= 1;
                        }               
                        Delay(10);
                }
        }
}
void Delay(uint16_t c)
{
        uint16_t a, b;
        for(; c>0; c--)
                for(a=1000; a>0; a--)
                        for(b=1000; b>0; b--);
}


代码我晚上上传吧,现在没时间了。具体IO操作自己根据固件库给的例子,去固件库手册查找函数进行扩展,程序自己设计需要的流程就可以实现自己想要的功能了。
GPIO的具体功能区STM32F10x手册查找寄存器之类的资料进行深入了解就可以了。




01流水灯.zip

302.55 KB

使用特权

评论回复
13395410441|  楼主 | 2016-1-29 16:10 | 显示全部楼层

大布丁丁-STM32F10x库函数学习笔记02 - RCC配置

本帖最后由 13395410441 于 2016-2-3 11:17 编辑

先把稀里糊涂学STM32第二讲源源不绝看完。
1、看完了吧,打开STM32手册和STM32固件库手册查阅用。
2、同样打开STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples\RCC\RCC_ClockConfig  main.c 从文件可以看出
/*RCC用于配置系统的运行频率,改变宏定义的不同频率,LED交替闪烁的速率不一样,体现CPU运行速度得快慢*/
//#define SYSCLK_HSE
#define SYSCLK_FREQ_20MHz           //20MHZ
//#define SYSCLK_FREQ_36MHz          //36MH
//#define SYSCLK_FREQ_48MHz          //48MH
//#define SYSCLK_FREQ_72MHz          //72MH

#define DELAY_COUNT    0x3FFFF

\*1、变量定义*\
GPIO_InitTypeDef  GPIO_InitStructure;
RCC_ClocksTypeDef  RCC_ClockFreq;
ErrorStatus  HSEStartUpStatus;

//这种定义函数的方式我很喜欢,没必要这些函数都存在,不同的宏定义打开不同的函数,库函数的开发员还真不错void NVIC_Configuration(void);
void Delay(__IO uint32_t nCount);
void SetSysClock(void);
#ifdef SYSCLK_HSE
  void SetSysClockToHSE(void);
#elif defined SYSCLK_FREQ_24MHz
  void SetSysClockTo24(void);
#elif defined SYSCLK_FREQ_36MHz
  void SetSysClockTo36(void);
#elif defined SYSCLK_FREQ_48MHz
  void SetSysClockTo48(void);
#elif defined SYSCLK_FREQ_56MHz
  void SetSysClockTo56(void);  
#elif defined SYSCLK_FREQ_72MHz
  void SetSysClockTo72(void);
#endif
/*通过宏定义来配置系统的不同时钟*/
void SetSysClock(void)
{   
#if defined SYSCLK_HSE
  SetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHz
  SetSysClockTo24();  
#elif defined SYSCLK_FREQ_36MHz
  SetSysClockTo36();
#elif defined SYSCLK_FREQ_48MHz
  SetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHz
  SetSysClockTo56();
#elif defined SYSCLK_FREQ_72MHz
  SetSysClockTo72();
#endif
}

剩下的就照抄就可以了,不复制了。最后看main函数
int main(void)
{
SetSysClock();//配置系统时钟频率,HCLK,pclk2和pclk1预分频器
RCC_GetClocksFreq(&RCC_ClockFreq);//这个函数填充rcc_clockfreq结构与当前不同芯片时钟频率(用于调试目的)
RCC_ClockSecuritySystemCmd(ENABLE);//使时钟安全系统(CSS):这将产生一个NMI例外当HSE时钟失败
NVIC_Configuration();//这个先放在这,后面再说他
  //开启GPIOA  GPIOB的模拟时钟
RCC_MCOConfig(RCC_MCO_HSE);//在库手册查找这个是选择在MCO管口上输出的时钟源     SYSCLK系统时钟 HSI HSE PLL*2
}
我把自己改的完整的代码放上来,看看不同频率LED闪烁的快慢.这个方法就是自己实现RCC的配置,通过打开不同的宏定义来控制时钟,我先传一份控制流水灯代码,一会我再传一份更简单的方法。


-------------------------------------------------华丽的分割线---------------------------------------------------------

上面的很麻烦吧。不去管它了,我说个简单的方法,我就是这么用的:
在项目中打开system_stm32f10x.c,找到的106行,看到下面的代码:(问题来了,system_stm32f10x.c不能修改,有个钥匙?文件夹中右击属性,把只读去掉就可以了)
#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* #define SYSCLK_FREQ_HSE    HSE_VALUE */
#define SYSCLK_FREQ_24MHz  24000000
#else
/* #define SYSCLK_FREQ_HSE    HSE_VALUE */
/* #define SYSCLK_FREQ_24MHz  24000000 */
/* #define SYSCLK_FREQ_36MHz  36000000 */
/* #define SYSCLK_FREQ_48MHz  48000000 */
/* #define SYSCLK_FREQ_56MHz  56000000 */
#define SYSCLK_FREQ_72MHz  72000000
#endif

我KEIL用的是STM32F10X_MD,USE_STDPERIPH_DRIVER(keil->魔法棒->c/c++ ->define选项你修改的内容)
所以我在下面else后面修改宏就可以
/* #define SYSCLK_FREQ_HSE    HSE_VALUE */
/* #define SYSCLK_FREQ_24MHz  24000000 */
/* #define SYSCLK_FREQ_36MHz  36000000 */
/* #define SYSCLK_FREQ_48MHz  48000000 */
/* #define SYSCLK_FREQ_56MHz  56000000 */
#define SYSCLK_FREQ_72MHz  72000000

打开哪一个宏定义就使用的那个频率。我用的72MHZ。
如果你的KEIL用的是STM32F10X_LD_VL)  (defined STM32F10X_MD_VL)  (defined STM32F10X_HD_VL)中的任意一个,那就在if里面修改频率就可以了。简单的逻辑关系就不说了。
然后在函数中调用SystemInit();搞定,怎么样,简单吗?试一下吧。我把我的LED的那份代码修改一下传上来吧。(遇到个问题,变量放在函数后面声明就会报错,注意点吧)

02-1自己写RCC影响LED闪烁速度.rar

296.85 KB

02-2简单配置RCC修改系统时钟频率-常用.rar

296.11 KB

使用特权

评论回复
mmuuss586| | 2016-1-29 19:15 | 显示全部楼层

这么多,支持下;

使用特权

评论回复
仙女山| | 2016-1-29 20:34 | 显示全部楼层
楼主现在开始学习i2c了吗

使用特权

评论回复
pkuzhx| | 2016-1-30 09:22 | 显示全部楼层
很不错

使用特权

评论回复
wangjianxing| | 2016-1-30 09:46 | 显示全部楼层
这个顶一下

使用特权

评论回复
加油吧小鱼儿| | 2016-1-30 10:13 | 显示全部楼层
给来点个赞,支持一下~

使用特权

评论回复
13395410441|  楼主 | 2016-1-30 13:32 | 显示全部楼层
仙女山 发表于 2016-1-29 20:34
楼主现在开始学习i2c了吗

慢慢来,还没到那里呢。循序渐进,上班没时间,等我年后辞职专心学一段时间再去找工作。

使用特权

评论回复
13395410441|  楼主 | 2016-1-30 13:39 | 显示全部楼层
出去买年礼,回来简单说一下系统滴答时钟
王大江-STM32F10x库函数学习笔记
https://bbs.21ic.com/forum.php?mo ... &fromuid=840036
(出处: 21ic电子技术论坛)
希望加精

使用特权

评论回复
13395410441|  楼主 | 2016-1-30 16:33 | 显示全部楼层

大布丁丁-STM32F10x库函数学习笔记03-用滴答时钟SysTick做延时

本帖最后由 13395410441 于 2016-2-3 11:18 编辑

打开版本库STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples\SysTick\TimeBase中的main.c

SysTick 24位递减计数器,1个系统周期减1,计数到0时重新从初值开始递减,此时内部COUNTFLAG标志位会置位,触发中断。
在STM32的应用为delay等待函数,设定1毫秒(ms)产生一次中断,中断中对计数值减1,计数值为0则退出等待关闭滴答时钟,否则继续等待。这样的好处是延迟时间不随系统时钟频率改变。

使用滴答时钟来实现延时函数就不会因为你修改系统时钟而影响延时的时间了。实现步骤很简单(基于之前的LED跑马灯函数):
1、在main()函数中使用SysTick_Config()设置滴答时钟中断的时间,每经过设置的时间大小就执行一次中断处理函数。
2、写Delay()函数给全局变量TimingDelay 赋值,作为基数,滴答时钟每中断一次,就会调用stm32f10x_it.c的SysTick_Handler()使这个值减1.
2、写中断处理函数TimingDelay_Decrement()使TimingDelay 减1,并在stm32f10x_it.c的SysTick_Handler()中调用TimingDelay_Decrement()。
下面是部分代码:
main.c中:
static vu32 TimingDelay;//全局变量TimingDelay必须定义为volatile,所以定义为vu32
void Delay(u32 nTime)
{
//给定时器中断处理函数的变量赋值
  TimingDelay = nTime;
  while(TimingDelay != 0);           //死等待
}
//定时器中断处理函数,写在这里可以,也可以写在systemf10x_it.c中
void TimingDelay_Decrement(void)
{
   if (TimingDelay != 0x00)
  {
    TimingDelay--;
  }
}

int main(void){
  SystemInit();//RCC系统时钟配置并初始化。
/*
在system_stm32f10x.c中
#define SYSCLK_FREQ_72MHz 72000000
uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz ;所以SystemCoreClock 是72000000。一个时钟时间是1/72000000s,所以SystemCoreClock/1000是0.001s是1ms。
*/
  if (SysTick_Config(SystemCoreClock / 1000)) //设置1ms触发一个中断,在中断中减1,之前版本库一系列SysTick的配置函数都被这一个函数代替了,看别人代码的时候注意了。
  {
      /* 捕捉错误 */
      while (1);
   }        
  while(1)
  {
     Delay(1000);        
     LED灯的设置
  }                                                                     
}

-----------------------------------------华丽的分割线----------------------------------------------
主要代码涉及
main.c、common.h
led.c、led.h
systemf10x_it.c

main.c实现Delay()函数,led.c/h中实现流水灯
main.c中的TimingDelay_Decrement()在common.h中声明,
systemf10x_it.c包含common.h头文件,并在void SysTick_Handler(void)调用main.c中的TimingDelay_Decrement()。


具体看代码

我之前说用SystemInit()配置RCC系统时钟,你可以在system_stm32f10x.c,找到的106行,修改自己的系统时钟(忘了的看我上一节RCC的简单配置)看看定时器会不会受RCC的影响。


03SysTick时钟作为延时函数不受系统时钟频率影响.rar

310.23 KB

使用特权

评论回复
13395410441|  楼主 | 2016-1-30 19:42 | 显示全部楼层
本帖最后由 13395410441 于 2016-2-1 15:05 编辑

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; //
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //
   GPIO_Init(GPIOA, &GPIO_InitStructure);  //

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All ;  //
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  //
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //
  GPIO_Init(GPIOB, &GPIO_InitStructure);  //

使用特权

评论回复
13395410441|  楼主 | 2016-2-1 15:26 | 显示全部楼层

大布丁丁-STM32F10x库函数学习笔记04-1-中断(EXTI)

本帖最后由 13395410441 于 2016-2-3 11:18 编辑

看来接下来就该中断了。初学笔记看起来还是比较乱,而且可能会有些地方理解的比较片面,等我学一遍,再重新整理个完整的,做个汇总。写程序一直用while(1)不是长久之计,学会使用中断是必然的选择。 STM32含有复杂的中断系统,但这也正是STM32的过人之处。
下面看看STM32的中断吧:(用到了#include "misc.h" )
1、NVIC(Nested Vectored Interrupt Controller) --------嵌套向量中断控制器, 是M3内核的亮点,“ 嵌套” 是其核心。
这就引出了优先级:(简单两句话)
占先式优先级:高级的会抢断低级的。即中断嵌套
响应优先级:占先式优先级相同时,高响应式的中断先执行,但不会抢断。即非抢断式响应。




STM32为了适应不同的优先级组合,设置了GROUP的概念,组是一个大的框架, 在组下分别分配了占先优先级与副优先级。每一个中断都有一个专门的寄存器(Interrupt PriorityRegisters)来描述该中断的占先式优先级及副优先级。在这个寄存器中STM32使用4个二进制位描述优先级( Cortex-M3定义了8位,但STM32只使用了4位) 。严重要记住:四位。


2、外部中断EXTI

2.1、STM32 中,所有 IO 脚都可以设置为 EINT 中断,对于外部中断线,有 16 路中断,即EINT0-15 ,分别对应于管脚的 0-16 脚。例如, PA0、 PA1 使用的 EINT 中断分别为EXTI_Line0 ,和 EXTI_Line1。如果 PA0、 PB0 呢?那就是共用 EXTI_Line0 咯,所以设计的时候要注意。



2.2、关于中断源,这里,外部中断的 0-4 使用独立的中断源, 5-9 与10-15 各自共用一个。所以你会在中断函数中看到有 EXTI9_5_IRQHandler() 和 EXTI15_10_IRQHandler(),这样的中断函数。那么你会问,如果进入了中断,我怎么知道是那路 EXINT 引起的呢?那就是通过读取中断标志位来判断的了,记住, 16路EXINT都是独立的,只是5-9,10-15共用了中断响应函数而已.程序中,可利用 if(EXTI_GetITStatus(EXTI_Line5)!=RESET)语句判断是否是LINE5 引起的中断。


使用库函数写中断,记得开放 stm32f10x_conf.h的 #include"misc.h"。


2.3、关于软件设置中断: 有时候在程序中我们会强制程序进入中断,这需要我们将中断标志位置位,而STM32提供了一个很方便的寄存器,即软中断事件寄存器,由以下库函数支持:
EXTI_GenerateSWInterrupt(EXTI_Line3);
这个函数运行的结果就是,如果EXINT3 配置了必须的中断寄存器和响应函数,则会马上产生一个EXINT3 中断。
软件配置:
NVIC_Configuration(void)——用以配置中断组与中断优先级;
GPIO_Configuration(void)——用以配置中断功能
在stm32f10x_it.c中给好的函数框架填充代码来实现中断
-----------------------------------------------------------------------------------------------------------------------
注意:1. 整个系统只能选择同一组 2. 编号小的具有较高优先级
写个GPIOA_0(独立中断源)和GPIOA_5(5-9公用中断源)---------再说一遍如果GPIOB_0 GPIOA_0就会公用EXTI_Line0,这就冲突了,注意不要这样设计程序啊。
1、EXYI0(GPIOA_5与之相似,只是把GPIO_Pin_0改成了GPIO_Pin_5,把EXTI_Line0改成了EXTI_Line5,把EXTI0_IRQn改成了EXTI9_5_IRQn)

EXTI_InitTypeDef   EXTI_InitStructure;
GPIO_InitTypeDef   GPIO_InitStructure;
NVIC_InitTypeDef   NVIC_InitStructure;

int main(void){
1、系统时钟配置,RCC配置,前面说过不说了
2、中断的GPIO配置
/* Enable GPIOA clock */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//时钟使能

  /* Configure PA.00 pin as input floating */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//配置浮空输入,不用配置频率
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  /* Enable AFIO clock */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//功能复用 IO 时钟

/* Connect EXTI0 Line to PA.00 pin */
  GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);//将PA_0连接到中断0


3、中断配置
//配置按钮中断线触发方式
  EXTI_InitStructure.EXTI_Line = EXTI_Line0;
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发,EXTI_Trigger_Rising;  上升沿触发
  EXTI_InitStructure.EXTI_LineCmd = ENABLE; //中断线使能
  EXTI_Init(&EXTI_InitStructure); //初始化中断


  /* Enable and set EXTI0 Interrupt to the lowest priority */
  NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//中断通道
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;//抢占优先级
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;//次优先级
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//通道中断使能
  NVIC_Init(&NVIC_InitStructure);//初始化中断


4、连接、启动中断
EXTI_GenerateSWInterrupt(EXTI_Line0);//软件设置中断,中断允许,到这里中断配置完成,可以到stm32f10x_it.c中写中断处理函数了
}
5、主程序完成,现在去stm32f10x_it.c中写中断处理函数
void EXTI0_IRQHandler(void)
{
  if(EXTI_GetITStatus(EXTI_Line0) != RESET)
  {
     /*中断处理部分*/
    /* Clear the  EXTI line 0 pending bit */
    EXTI_ClearITPendingBit(EXTI_Line0);
  }
}


void EXTI9_5_IRQHandler(void)
{

  if(EXTI_GetITStatus(EXTI_Line5) != RESET)
  {
     /*中断处理部分*/
    /* Clear the  EXTI line 0 pending bit */
    EXTI_ClearITPendingBit(EXTI_Line5);
  }

}

使用特权

评论回复
13395410441|  楼主 | 2016-2-1 16:11 | 显示全部楼层

大布丁丁-STM32(Cortex-M3)中的优先级概念

本帖最后由 13395410441 于 2016-2-3 11:18 编辑

网上找的:


STM32(Cortex-M3)中有两个优先级的概念——抢占式优先级和响应优先级,有人把响应优先级称作'亚优先级'或'副优先级',每个中断源都需要被指定这两种优先级。

具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以嵌套低抢占式优先级的中断。

当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。

既然每个中断源都需要被指定这两种优先级,就需要有相应的寄存器位记录每个中断的优先级;在Cortex-M3中定义了8个比特位用于设置中断源的优先级,这8个比特位可以有8种分配方式,如下:

所有8位用于指定响应优先级
最高1位用于指定抢占式优先级,最低7位用于指定响应优先级
最高2位用于指定抢占式优先级,最低6位用于指定响应优先级
最高3位用于指定抢占式优先级,最低5位用于指定响应优先级
最高4位用于指定抢占式优先级,最低4位用于指定响应优先级
最高5位用于指定抢占式优先级,最低3位用于指定响应优先级
最高6位用于指定抢占式优先级,最低2位用于指定响应优先级
最高7位用于指定抢占式优先级,最低1位用于指定响应优先级

这就是优先级分组的概念。




Cortex-M3允许具有较少中断源时使用较少的寄存器位指定中断源的优先级,因此STM32把指定中断优先级的寄存器位减少到4位,这4个寄存器位的分组方式如下:

第0组:所有4位用于指定响应优先级
第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级
第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级
第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级
第4组:所有4位用于指定抢占式优先级

可以通过调用STM32的固件库中的函数NVIC_PriorityGroupConfig()选择使用哪种优先级分组方式,这个函数的参数有下列5种:

NVIC_PriorityGroup_0 => 选择第0组
NVIC_PriorityGroup_1 => 选择第1组
NVIC_PriorityGroup_2 => 选择第2组
NVIC_PriorityGroup_3 => 选择第3组
NVIC_PriorityGroup_4 => 选择第4组


接下来就是指定中断源的优先级,下面以一个简单的例子说明如何指定中断源的抢占式优先级和响应优先级:

// 选择使用优先级分组第1组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  
// 使能EXTI0中断
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 指定抢占式优先级别1

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 指定响应优先级别0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
  
// 使能EXTI9_5中断
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 指定抢占式优先级别0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 指定响应优先级别1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);






要注意的几点是:

1)如果指定的抢占式优先级别或响应优先级别超出了选定的优先级分组所限定的范围,将可能得到意想不到的结果;

2)抢占式优先级别相同的中断源之间没有嵌套关系;

3)如果某个中断源被指定为某个抢占式优先级别,又没有其它中断源处于同一个抢占式优先级别,则可以为这个中断源指定任意有效的响应优先级别。

使用特权

评论回复
13395410441|  楼主 | 2016-2-1 16:54 | 显示全部楼层
本帖最后由 13395410441 于 2016-2-1 17:59 编辑

说到这,应该说一下串口UASRT了,毕竟方便快捷,调试会用得到。#include <stdio.h>
int main(void){
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
//UASRT1 Tx   pin09
GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//UASRT1 Rx   pin10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);



USART_InitStructure.USART_BaudRate = 115200;//该成员设置了 USART 传输的波特
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//帧传输或者接收到的数据位
USART_InitStructure.USART_StopBits = USART_StopBits_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);

USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_ClearFlag(USART1,USART_FLAG_TC);//清除串口的待处理标志位
USART_Cmd(USART1, ENABLE);


NVIC_InitTypeDef NVIC_InitStructure;

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

while(1){
delay(1);
printf("1111111111111111111111\n");//下面重定向的输入
}

}
int fputc(int ch, FILE *f)//执行printf的时候会调用fputc显示在屏幕,但是fputc已经被重写为发送数据到出串口,并返回printf的字符串
{
                /* 发送一个字节数据到串口1 */
                USART_SendData(USART1, (uint8_t) ch);
               
                /* 等待发送完成 */
                while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);               
       
                return (ch);
}

/// 重定向c库scanf到串口,你在串口调试软件sscom中输入一个字符串,串口会调用串口处理函数,函数内写了个printf让它再返回串口显示,字符串会被串口获取,并调用fgetc,而fgetc已经被重写为返回串口接受的数据。
int fgetc(FILE *f)
{
                /* 等待串口1输入*/
                while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);

                return (int)USART_ReceiveData(USART1);
}




在stm32f10x_it.c下的串口处理函数,,,USART_IT_RXNE是接收中断
void USART1_IRQHandler(void)
{
        uint8_t ch;
       
        if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
        {        
            //ch = USART1->DR;
                        ch = USART_ReceiveData(USART1);
                          printf( "%c", ch );    //将接收到的数据直接返回打印
        }
         
}


明天再写,回家了

使用特权

评论回复
Eagle1986| | 2016-2-1 17:12 | 显示全部楼层
好贴,谢谢分享,本人也正在学习STM32

使用特权

评论回复
智达| | 2016-2-1 17:17 | 显示全部楼层
写的挺细致的,要是配上视频就更好了。
对了,taobao上有许多stm32的教学视频。非常不错,适合要快速入门的人。
毕竟,有人给你讲解,接受起来就更容易。

使用特权

评论回复
发新帖 本帖赏金 20.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:峰回路转

11

主题

56

帖子

3

粉丝