加载中…
个人资料
liht1634
liht1634
  • 博客等级:
  • 博客积分:0
  • 博客访问:212,523
  • 关注人气:58
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
相关博文
推荐博文
正文 字体大小:

STM32单片机2

(2017-05-14 20:14:09)
标签:

arm单片机

stm32

分类: 单片机、PLC、PLD
内容包括STM32的Systick、端口、大小端、I2C等的介绍紫色文字是超链接,点击自动跳转至相关博文。持续更新,原创不易!
十、STM32_Systick

1、SysTick介绍

2、SysTick timer 工作分析

十一、STM32_端口

1、端口的配置
1)各种模式配置    2)STM32 端口复用与重映射(如USART Remap)   
2、相关库函数的查询STM32F10x_StdPeriph_Driver_3.5.0(中文版)
1)使用官方固件库查询   2)使用“stm32f103_非常好的函数库查询
3、配置代码的查询STM32F10x_StdPeriph_Driver_3.5.0(中文版)
4、端口功能查询
5、关于GPIO的BSR和BSRR寄存器
1)GPIO的寄存器BSRR和BRR简介   2)使用方法
十二、STM32_大小端
1、STM32大小端
2、大小端判断方法
1)通过程序   2)通过编译器编译结果
十三、STM32_I2C
十五、查找汇编指令
十六、数据类型
1、KELI MDK 数据类型定义
2、STM32数据类型
3、数据类型混合运算
4、数据类型转换规则
5、volatile、const关键词说明
---------------------------------------------
STM32单片机2
---------------------------------------------


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

十、STM32_Systick

1、SysTick介绍

  SysTick定时器被捆绑在 NVIC中,用于产生 SysTick异常(异常号:15)。在以前,操作系统和有所有使用了时基的系统,都必须要一个硬件定时器来产生需要的“滴答”中断,作为整个系统的时基。滴答中断对操作系统尤其重要。例如,操作系统可以为多个任务许以不同数目的时间片,确保没有一个任务能霸占系统;或者把每个定时器周期的某个时间范围赐予特定的任务等,还有操作系统提供的各种定时功能,都与这个滴答定时器有关。因此,需要一个定时器来产生周期性的中断,而且最好还让用户程序不能随意访问它的寄存器,以维持操作系统“心跳”的节律。

  Cortex-M3在内核部分包含了一个简单的定时器——SysTick  timer。因为所有的CM3芯片都带有这个定时器,软件在不同芯片生产厂商的 CM3器件间的移植工作就得以化简。该定时器的时钟源可以是内部时钟(FCLK,CM3上的自由运行时钟),或者是外部时钟( CM3处理器上的STCLK信号)。不过,STCLK的具体来源则由芯片设计者决定,因此不同产品之间的时钟频率可能会大不相同。因此,需要阅读芯片的使用手册来确定选择什么作为时钟源。

  在STM32中 SysTick 以  HCLK(AHB时钟)或 HCLK/8  作为运行时钟(由控制寄存器STK_CTRL的Bit2位决定内部还是外部时钟)。 SysTick定时器能产生中断,CM3为它专门开出一个异常类型,并且在向量表中有它的一席之地。它使操作系统和其它系统软件在 CM3器件间的移植变得简单多了,因为在所有 CM3产品间,SysTick的处理方式都是相同的。SysTick定时器除了能服务于操作系统之外,还能用于其它目的:如作为一个闹铃,用于测量时间等。 Systick 定时器属于 cortex内核部件,可以参考《CortexM3权威指南》或《STM32xxx-Cortex编程手册》来了解。

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

2、 SysTick timer 工作分析

  SysTick是一个24位的定时器,即一次最多可以计数2的24次方个时钟脉冲,这个脉冲计数值被保存到当前计数值寄存器 STK_VAL  (SysTick current valueregister) 中,只能向下计数,每接收到一个时钟脉冲 STK_VAL的值就向下减1,直至0,当 STK_VAL的值被减至 0时,由硬件自动把重载寄存器STK_LOAD(SysTick reload value register)中保存的数据加载到 STK_VAL,重新向下计数。当 STK_VAL的值被计数至 0时,触发异常,就可以在中断服务函数中处理定时事件了。 当然,要使 SysTick进行以上工作必须要进行 SysTick进行配置。它的控制配置很简单,只有三个控制位和一个标志位,都位于寄存器 STK_CTRL。
        表8.9 STK_CTRL, 0xE000E010 控制寄存器

STM32单片机2
首先看STK_CTRL控制寄存器:寄存器内有4个位具有意义
第0位:ENABLE,Systick 使能位 (0:关闭Systick功能;1:开启Systick功能)
第1位:TICKINT,Systick 中断使能位 (0:关闭Systick中断;1:开启Systick中断)
第2位:CLKSOURCE,Systick时钟源选择 (0:使用HCLK/8 作为Systick时钟;1:使用HCLK作为Systick时钟
第16位:COUNTFLAG,Systick计数比较标志,如果在上次读取本寄存器后,SysTick 已经数到了0,则该位为1。如果读取该位。

STM32单片机2

校准寄存器一般很少用。

(以上寄存器操作可以参看以下程序加以理解:)

static u8 fac_us;//us延时倍乘数
static u32 fac_ms;//ms延时倍乘数


* Function Name  :Delay_Init
* Description    : SYSTICK的时钟固定为HCLK时钟的1/8
* Input          : SYSCLK:系统主频时钟(SYSCLK Mhz)
* Return         : None
void Delay_Init(u8 SYSCLK)
{
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);// select HCLK/8 as systick clock source.
    fac_us=SYSCLK/8;  //9    
    fac_ms=(u32)fac_us*1000;
}


* Function Name:delay_ms
* Description : SysTick计数值最大为24位,最大值为16777215,如果fac_us=9000,那么nms的最大值为1864(1864ms)
* Input    : nus,注意nms的范围 nms<=1864
void delay_ms(u16 nms)
{
    SysTick->LOAD=(u32)nms*fac_ms;//时间加载(SysTick->LOAD为24bit) 
    SysTick->VAL   = 0;
    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk ;   //Enable Systick Function and start counting   
    while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));   //等待计数到0
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;              //关闭计数器         
}


* Function Name  :delay_us
* Description    : SysTick计数值最大为24位,最大值为16777215,如果fac_us=72那么nus
的最大值为233016,如果fac_us=9那么nus的最大值为1864128(1864ms)
* Input       : nus
* Return      : None
void delay_us(u32 nus)
{
    SysTick->LOAD=nus*fac_us; //时间加载
    SysTick->VAL   = 0;
    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk ; //Enable Systick Function and start counting   
    while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); //等待计数到0
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //关闭计数器
}

  当我们需要精确延时时,就可以利用 SysTick timer实现,理论上它的最小计时单位为AHB的时钟周期,即1/72000000 秒,72分之一的微秒,足以满足大部分极端应用需求。本小节以实例讲解如何利用 SysTick进行精确延时。抢占优先级要设置最高。

中断分组M3内核的配置方法:

NVIC_SetPriority(SysTick_IRQn,NVIC_EncodePriority(NVIC_PRIORITY_GROUP_5,0,0)) //抢占优先级0,响应优先级0

NVIC_SetPriority(EXTI5_10_IRQn,NVIC_EncodePriority(NVIC_PRIORITY_GROUP_5,1,0))//抢占优先级1,响应优先级0


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

十一、STM32_端口

1、端口的配置
1)各种模式配置
浮空输入_IN_FLOATING     带上拉输入_IPU         带下拉输入_IPD                  模拟输入_AIN
开漏输出_OUT_OD             推挽输出_OUT_PP      复用功能的推挽输出_AF_PP   复用功能的开漏输出_AF_OD
----------------------
2)STM32 端口复用与重映射(如USART Remap)
(1)RCC_APB2Periph_AFIO时钟一般在什么时候下需要开启?
引脚复用,进行重映射时,需要开启AFIO时钟,包括输入管脚中断。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC |RCC_APB2Periph_ADC1 | RCC_APB2Periph_AFIO |RCC_APB2Periph_USART1, ENABLE );   
//使能ADC1通道时钟,各个管脚时钟 就是使能USART1管脚复用
-------------
(2)以USART1的重映射为例
      因为我要一个TFT_LCD屏的主控板,考虑到FSMC 我选用了STM32F103VCT6 型号的CPU,一不小心串口接到USART1上了。因为在调程序时才发现错了,没得办法,只能通过端口重映射来解决。
      STM32上有很多I/O口,也有很多的内置外设像I2C、ADC、ISP、USART等,为了节省引出管脚,这些内置外设基本上是与I/O口共用管脚的,也就是I/O管脚的复用功能。但是STM32还有一特别之处就是:很多复用内置的外设的I/O引脚可以通过重映射功能,从不同的I/O管脚引出,即复用功能的引脚是可通过程序改变的。下面说说我的调试经历。
      不知道是什么原因PCB制图时把串口接到USART1上了,当时也没在意,等我把USART测试程序写好烧进去硬件仿真时,串口给的是乱码,我当时就觉得奇怪。把程序检查了好几遍就是查不出问题来,以为是硬件有问题,但突然想到了STM有复用功能,心想会不会是这里有鬼?于是找来datasheet 一看,真相大白。
STM32单片机2
      三个红框交汇处。STM32F103VCT6 这个CPU的USART1接的是PB6/PB7 但是上电初始化后默认功能并非是USART1。所以想要用串口功能,必须用端口重映射。
      大家知道,STM32的单片机每个功能模块有自己的时钟系统,所以要想要调用STM32单片机的功能模块时,必须先配置对应时钟,然后才能去操作相应的功能模块.端口重映射也一样。如图示:
STM32单片机2
-------------
(3)重映射步骤
 打开重映射时钟和USART重映射后的I/O口引脚时钟
     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO,ENABLE);
 I/O口重映射开启
     GPIO_PinRemapConfig(GPIO_Remap_USART1,ENABLE);
 配制重映射引脚, 这里只需配置重映射后的I/O,原来的不需要去配置
     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
     GPIO_Init(GPIOB, &GPIO_InitStructure);

     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
     GPIO_Init(GPIOB,&GPIO_InitStructure);
只需要这三步,串口就可以正常使用了。

最后总结一下:
简单的说STM32的IO有3个功能:一个是默认的,一个是复用,一个是重映射功能(这个其实也属于复用);
如果配置成复用,则将使用第2个功能;如果配置成复用,同时,相应的重映射配置了,则将使用第3个功能;
通常一个口的复用+重映射有好多,不止两个,这时候就看你使能哪个设备了(哪个被使能就用哪个);
开复用+使能设备+是否重映射就可以决定这个IO口到底使用哪个功能。
-------------

(4)为了优化64脚或100 脚封装的外设数目,可以把一些复用功能重新映射到其他引脚上。设置复用重映射和调试I/O 配置寄存器(AFIO_MAPR) 实现引脚的重新映射。这时,复用功能不再映射到它们的原始分配上。需要用到外设的重映射功能时才需要使能AFIO的时钟。外部中断(EXTI)中与AFIO有关的寄存器是AFIO-EXTICR1、2、3,它们是用来选择EXTIx外部中断的输入脚之用。

举例:重映射USART2

USART2的TX/RX在PA.2/3,但是PA.2已经被Timer2的channel3使用。这时,如果还想使用USART2,但又不想影响Timer2的使用,这就需要把USART2的TX/RX重映射到PD.5/6。

映射库函数的调用过程:

使能被重新映射到的I/O端口时钟

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);

使能被重新映射的外设时钟

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

使能AFIO功能的时钟(勿忘!)

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

进行重映射

    GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);

如在使用EXTI时,如果不启动RCC_APB2Periph_AFIO时钟,无法使用外部中断
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE);

---------------------------------------------
2、相关库函数的查询STM32F10x_StdPeriph_Driver_3.5.0(中文版)
1)使用官方固件库查询
STM32单片机2

STM32单片机2
----------------------
2)使用“stm32f103_非常好的函数库查询
STM32单片机2
-------------------------------------------
3、配置代码的查询STM32F10x_StdPeriph_Driver_3.5.0(中文版)
STM32单片机2

STM32单片机2
STM32单片机2
-------------------------------------------
4、端口功能查询
在数据手册中查询端口功能,比如“STM32F103xCDE_DS_CH_V5.pdf”,FT表示5V容忍。
STM32单片机2
-------------------------------------------
5、关于GPIO的BSR和BSRR寄存器
1)GPIO的寄存器BSRR和BRR简介
1)端口 位设置/复位寄存器BSRR:(如果同时设置了BSy和BRy的对应位,BSy位起作用,低位优先
位31:16  BRy: 清除端口x的位y (y 0…15)   这些位只能写入并只能以字(16位)的形式操作。
    0:对对应的ODRy位不产生影响(ODR=Output Data Register
    1:清除对应的ODRy位为0
位15:0    BSy: 设置端口x的位y (y 0…15)   这些位只能写入并只能以字(16位)的形式操作。
    0:对对应的ODRy位不产生影响
    1:设置对应的ODRy位为1
(2)端口 位复位寄存器BRR:
位31:16  保留
位15:0    BRy: 清除端口x的位y (y 0…15)   这些位只能写入并只能以字(16位)的形式操作。
     0:对对应的ODRy位不产生影响
     1:清除对应的ODRy位为0

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

2)使用方法
(1)混合改变 
需要置1的端口对应的位,在低16位里置1
需要置0的端口对应的位,在高16位里置1,不改变的,都置0. 然后写寄存器BSRR
(2)改变引脚为低 
需要置0的端口对应的位,在低16位里置1,然后写寄存器BRR
(3)改变引脚为高 
需要置1的端口对应的位,在低16位里置1 高16为全0 然后写寄存器BSRR 
另外就是,STM32的库,GPIO_SetBits,GPIO_ResetBits,可以对多个引脚操作的,就是把需要操作的引脚用“|”(或运算)。
最方便的还是自己直接写寄存器(方法1)。

第一步:第一你要用的IO口 比如说要用A口的高8位定义 GPIOA_USE=0xF0 
第二步:写BSRR寄存器。                            GPIOA->BSRR=data&&GPIOA_USE 
第三步:写BRR寄存器。                              GPIOA->BRR=(~data)&&GPIOA_USE

假设data为8位要写入的数据: 
GPIO_SetBits(GPIOD, data 0xff00); 
GPIO_ResetBits(GPIOD, (~data 0xff00)); 

也可以直接操作这两个寄存器:
GPIOD->BSRR data 0xff00; 
GPIOD->BRR ~data 0xff00;

规则:
规则一、置GPIOD->BSRR低16位的某位为'1',则对应的I/O端口置'1';而置GPIOD->BSRR低16位的某位为'0',则对应的I/O端口不变。
规则二、置GPIOD->BSRR高16位的某位为'1',则对应的I/O端口置'0';而置GPIOD->BSRR高16位的某位为'0',则对应的I/O端口不变。
规则三、置GPIOD->BRR低16位的某位为'1',则对应的I/O端口置'0';而置GPIOD->BRR低16位的某位为'0',则对应的I/O端口不变。

STM32单片机2
例如:
1)要设置D0、D5、D10、D11为高,而保持其它I/O口不变,只需一行语句:

  GPIOD->BSRR 0x0C21;// 使用规则一

2)要设置D1、D3、D14、D15为低,而保持其它I/O口不变,只需一行语句:

  GPIOD->BRR 0xC00A; // 使用规则三

3)要同时设置D0、D5、D10、D11为高,设置D1、D3、D14、D15为低,而保持其它I/O口不变,也只需一行语句:

                                    高16位                         低16位

                          31          24            16                8              0

                           1100 0000 0000 1010   0000 1100 0010 0001

  GPIOD->BSRR 0xC00A0C21;// 使用规则一和规则二

  GPIOD->BSRR = (0x00 ff 00 00 | data;//根据data改变低8位端口的值,高8位端口不变(保持高8位不变,低8位为0)


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

十二、STM32_大小端

1、STM32大小端

大端模式:低位字节存在高地址上,高位字节存在低地址上;

小端模式:高位字节存在高地址上,低位字节存在低地址上。

STM32属于小端模式。简单的说,比如u32 temp=0X12345678假设temp地址在0X2000 0010.内存里面存放:

地址              |            HEX         |

0X2000 0010 |   78   56   43  12  |

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

2、大小端判断方法

1)通过程序

现有0x12345678

STM32单片机2

int temp=1; //定义一个int型变量temp并赋值1

char *p=(char *)&temp; //&temp取地址强制转换为指针,定义一个字符型指针指向temp变量的起始地址

if(*p==1) printf("小端");

else printf("大端");

参照上述两个图可知,若为小端,则起始地址处被放入1,否则被放入0。

第3行读出字符型指针指向的地址处的字符,即变量temp的第一个字节处的值,并通过此值判断大端和小端。

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

2)通过编译器编译结果

STM32单片机2


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

十三、STM32_I2C

STM32单片机2

I2C协议详见“算法、协议、信号处理/I2C总线协议及asm程序”。


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

十四、相关总结


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

十五、查找汇编指令

有关Cortex 内核的指令我们可以参考《CM3 权威指南 CnR2》第四章:指令集。剩下的 ARM 的汇编指令我们可以在 MDK->Help->Uvision Help 中搜索到,以 AREA 为例,检索如下:  

STM32单片机2


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

十六、数据类型

1、KELI MDK 数据类型定义

char 占用 1 个字节

short int 占用 2 字节//注意这里!

int 占用 4 字节

long 占用 4 字节 //注意这里!

long int 占用 4 字节 //注意这里!

float 占用 4 字节

double 占用 8 字节即有如下宏定义

typedef unsigned char uint8; // 无符号 8 位字符型变量

typedef signed char int8; // 有符号 8 位字符型变量

typedef unsigned short uint16; // 无符号 16 位短整型变量

typedef signed short int16; // 有符号 16 位短整型变量

typedef unsigned int uint32; // 无符号 32 位整型变量

typedef signed int int32; // 有符号 32 位整型变量

typedef float fp32; // 单精度浮点数(32 位长度)

typedef double fp64; // 双精度浮点数(64 位长度)

注:C 语言中的种类数据:整型:int、short、long,实型:float、double 。其中,unsigned 为无符号, signed 有符号。

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

2、STM32数据类型宏定义

typedef unsigned char u8;0~255 一字节

typedef signed char s8;-128~127

typedef volatile unsigned char vu8;

typedef volatile signed char vs8;

typedef unsigned char const uc8;

typedef signed char const sc8;

typedef volatile unsigned char const vuc8;  

typedef volatile signed char const vsc8;  

typedef unsigned short u16; 0~65535 两字节

typedef signed short s16; -32768~32767 两字节

typedef volatile unsigned short vu16;

typedef volatile signed short vs16;

typedef unsigned short const uc16;  

typedef signed short const sc16;  

typedef signed short const sc16;

typedef volatile unsigned short const vuc16;  

typedef volatile signed short const vsc16;

typedef unsigned long u32; 0~(2^32-1)四字节

typedef signed long s32; /

typedef signed long const sc32;

typedef volatile unsigned long const vuc32;

typedef volatile signed long const vsc32;

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

3、数据类型混合运算

不同类型数据的混合运算在C 语言中,不同类型的数据间是可以混合运算的。在进行运算时,不同类型的数据要先转换成同一类型,然后进行运算。转换的规则如下:

STM32单片机2

注意:箭头的方向只表示数据类型级别的高低,由低向高转换,这个转换过程一步到位。

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

4、数据类型转换规则

各类数据类型的转换,分为两种方式:隐式(编译软件自动完成),显式(程序强制转换)

1)隐式转换规则

字符必须先转换为整数(C 语言规定字符类型数据和整型数据之间可以通用) short 型转换为 int 型(同属于整型)float 型数据在运算时一律转换为双精度(double)型,以提高运算精度(同属于实型) 赋值时,一律是右部值转换为左部类型

注:当整型数据和双精度数据进行运算时,C 先将整型数据转换成双精度型数据,再进行运算,结果为双精度类型数据

当字符型数据和实型数据进行运算时,C 先将字符型数据转换成实型数据,然后进行计算,结果为实型数据

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

2)显式转换规则:例:(int)(x+y);

注:强制类型转换时,得到一个所需要的中间变量,原来变量的类型未发生变化。

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

5、volatile、const关键词说明

1)volatile :这个关键字,很多人只知道用,不知道其含义,有介绍解释是不易被编译器优化的。在 STM32 资料中解释加了易挥发的变量。这些解释都是含糊不清的。为此笔者专门查了下这个修饰关键字含义。

通俗的解释: 随时会改变,并被多函数调用可以加 volatile 修饰。

简称易变变量或易挥发变量。 表示这个变量的真的很容易变。

进阶解释: 加了这个 volatile 意义就是在每次取这个变量值的时候,要求不是取它上次在某个时候取的临时缓存变量(比如说暂存在某个寄存器中),而是直接到内存中取。

个人经验: 

告诉编译器,volatile 定义的变量必须 RAM 变量,不能是寄存器变量,尤其是中断中用全局变量。

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

2)const: 在定义变量时候,如果加上关键词 const,则变量的值在程序运行期间不能改变, 当然不能再赋值了。这种变量称为常变量(constant variable)或是只读变量(read-only-variable,这样觉得更恰当)。


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

我的更多文章:

0

阅读 评论 收藏 转载 喜欢 打印举报/Report
  • 评论加载中,请稍候...
发评论

    发评论

    以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

      

    新浪BLOG意见反馈留言板 电话:4000520066 提示音后按1键(按当地市话标准计费) 欢迎批评指正

    新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

    新浪公司 版权所有