我的第一个项目是STM32嵌入式系统设计,包括了硬件部分和软件部分。项目与公共卫生学院合作,主要内容是关于药剂检测。
之前做过用51单片机开发板读取温度湿度的小项目,主要的任务是代码的编写,相比于51单片机这种入门级别的MCU,STM32在各方面性能都领先51单片机,但是使用起来也复杂不少,光是选择用库函数法或是寄存器法编程就让人纠结。
我主要通过视频+相关文档的方法来学习STM32。
视频地址:【正点原子】STM32开发板实验教程,视频讲的很细,电路原理图也都有讲,不过涉及到原理乏味而又高深。
相对应的实验配套文档:正点原子战舰版资料
其中个人认为最有用的是:
①软件资料下的软件压缩包,有MDK5的安装包和各种实用软件,比如程序下载软件、串口软件
②STM32F1开发指南,包括库函数版和寄存器版。
③STM32参考资料文件夹下的STM32中文参考手册
④STM32F103ZET6
⑤程序源码,包括库函数版和寄存器版。
STM32快速简介
我总结了下我对STM32的理解:
STM32是ST公司生产的MCU,32位,内核是ARM公司的Cortex内核。Cortex内核有A,R,M三个系列,STM32主要是Cortex-M系列。Cortex-M3内核在各方面都领先于ARM7内核,成本也更低。
配置程序的模板框架其实挺复杂的,但其实不需要自己一步一步的建立,直接套用程序源码中的模板就好了。现成的有STM32F103系列和STM32F407系列的模板。
为了方便开发,我最后选择的是库函数法。本文以串口通信作为例子,尽量详尽的给出模板,中间必须要用到的头函数定义和GPIO函数也进行了一定的介绍。
头文件设置
#ifndef __XXX_H
#define __XXX_H
#include "XXXXXX.h"
void XXX//各种函数声明
#define LED0 PAout(1)//各种宏定义,PAout(1)这样的位带操作在sys.h中定义
#endif
GPIO的设置,包括输入输出
(1)初始化
GPIO_InitTypeDef GPIO_InitStructure; //定义名称为GPIO_InitStructure的结构体
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PB.5 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz,数值感觉不太重要
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB.5
其中GPIO_InitTypeDef是一个结构体,主要包括GPIO的引脚、速度、模式三个参数。
typedef struct
{
uint16_t GPIO_Pin; //GPIO_Pin_0到GPIO_Pin_15
GPIOSpeed_TypeDef GPIO_Speed;
GPIOMode_TypeDef GPIO_Mode;
}GPIO_InitTypeDef;
GPIO_Speed的参数规范为:
typedef enum
{
GPIO_Speed_10MHz = 1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
输出模式必须配置,输入模式无须配置。设置端口的翻转速度级别为50MHz,这种级别时端口能输出频率很高的信号,但要求外设的容性负载很小。另外还有2MHz和10MHz级别的,能驱动容性负载较大的外设。
GPIO_Mode的参数规范为:
typedef enum
{ GPIO_Mode_AIN = 0x0, //模拟输入
GPIO_Mode_IN_FLOATING = 0x04, //浮空输入
GPIO_Mode_IPD = 0x28, //下拉输入
GPIO_Mode_IPU = 0x48, //上拉输入
GPIO_Mode_Out_OD = 0x14, //开漏输出
GPIO_Mode_Out_PP = 0x10, //通用推挽输出
GPIO_Mode_AF_OD = 0x1C, //复用开漏输出
GPIO_Mode_AF_PP = 0x18 //复用推挽输出
}GPIOMode_TypeDef;
各种输出输入模式应用场合:
http://www.openedv.com/posts/list/21980.htm
(2)信号输出和检测输入
有相应的库函数
GPIO_SetBits(GPIOE,GPIO_Pin_5); //LED1对应引脚GPIOE.5拉高
GPIO_ResetBits(GPIOB,GPIO_Pin_5); //LED0对应引脚GPIOB.5拉低
GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4);//读取GPIOE.4的输入
用位带操作法比较方便
PAout(1) = 1;
PBin(2);
按键设置(以低电平触发为例)
其中u8是无符号数字8位的意思,进行过宏定义。
//mode=0时,不支持连按;mode=1时,支持连按
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1;//按键按松开标志
if(mode)key_up=1; //支持连按
if(key_up&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1))
{
delay_ms(10);//去抖动
key_up=0;
if(KEY0==0)return KEY0_PRES;
else if(KEY1==0)return KEY1_PRES;
else if(KEY2==0)return KEY2_PRES;
else if(WK_UP==1)return WKUP_PRES;
}else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)key_up=1;
return 0;// 无按键按下
}
串口通信
串口配置的一般步骤(以USART1为例,PA9为TX发送端,PA10为RX接收端为例):
①串口时钟使能,GPIO时钟使能:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE);
其他串口可能是RCC_APB1PeriphClockCmd();
②串口复位(可不写):
USART_DeInit(USART1);
③GPIO端口模式设置:
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
④串口参数初始化:
USART_InitTypeDef USART_InittStructure;
USART_InittStructure.USART_BaudRate = 115200; //波特率
USART_InittStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件流设置
USART_InittStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx; //接收发送模式
USART_InittStructure.USART_Parity = USART_Parity_No; //奇偶校验位
USART_InittStructure.USART_StopBits = USART_StopBits_1; //停止位
USART_InittStructure.USART_WordLength = USART_WordLength_8b; //字长
USART_Init(USART1, &USART_InittStructure);
⑤开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //分组2,放到主函数main的开头位置
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //设置中断类型,USART_IT_RXNE表示接收缓冲区非空
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //串口1中断,在stm32F10x.h中有定义
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //响应优先级为2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure);
⑥串口使能:
USART_Cmd(USART1, ENABLE);
⑦编写中断处理函数:
//会和system文件夹里的usart.h有冲突,删掉usart.h。
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
}
}
⑧串口数据收发操作:
USART_SendData(USART1, uint16_t Data);
USART_ReceiveData(USART1);
串口通信需要的软硬件设置:
①安装CH340驱动(在软件目录下),让电脑USB口能变成串口
②开发板USB口选择USB_232(如图)
③PA9、PA10通过跳线帽分别与RXD、TXD相连(如图)
利用串口输入指令控制程序
int main(void)
{
u16 len;
u8 flag = 0;
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
LED_Init(); //LED端口初始化
KEY_Init(); //初始化与按键连接的硬件接口
MCP9808_init(); //温度传感器初始化
while(1)
{
if(USART_RX_STA&0x8000) //第15位标志位为1时表示接收完成
{
len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度,第0位~第13位
if(USART_RX_BUF[len-1]=='E')//指令以E(END)结束
{
if(USART_RX_BUF[0]=='R'&USART_RX_BUF[1]=='T'&USART_RX_BUF[2]==' '&len==4) //指令为RT E
{
printf("温度为%.2f℃\r\n",MCP9808_ReadTP()); //显示温度
flag = 0;
}
else
flag = 1;
}
else
flag = 1;
if(flag==1)
{
flag =0;
printf("错误\r\n");
}
else
printf("代码已执行\r\n");
USART_RX_STA = 0;
}
}
}
可以根据需求再增加指令。
关于串口1下载程序:
orient/strip%7CimageView2/2/w/1240)
BOOT0=1,BOOT1=0,如果下载一直识别不出来,尝试按一按RST键。
下载完成后,由于软件勾上了编成后执行,程序会开始执行。
若需要按RST键复位程序,需要再把BOOT0拉低。否则按RST键程序不会执行。