STM32嵌入式实验报告

报告写作规范

1. 实验功能描述:按键控制LED,串口计算器

2. 电路硬件原理、接线等分析

3. 程序下载工具,超级终端等使用说明

4. 程序分析说明(基于OS的分析各任务结构、关系)

5. 实验中遇到的问题及解决办法分析

6. 总结,心得体会

一实验功能描述:

1. 一个按键调节1个LED的闪烁频率;(1-4秒)

2. 一个按键调节另1个LED的亮度变化周期;(1-4秒)

3. 整合串口计算器(要求超级终端内实现,带删除功能);

4. 以上内容采μc/os-Ⅱ用多任务操作系统方式实现。

二实验原理

本实验基于ALIENTEKMiniSTM32开发板进行,其板载资源非常丰富,本实验主要用到如下资源:

1.USB串口/串口1这是USB转串口(P4)同STM32F103RCT6的串口1进行连接的接口,标号RXD和TXD是USB转串口的2个数据口(对CH340G来说),而PA9(TXD)和PA10(RXD)则是STM32的串口1的两个数据口(复用功能下)。他们通过跳线帽对接,就可以和连接在一起了,从而实现STM32的程序下载以及串口通信。

2.两个LED灯这是开发板板载的两个LED灯,它们在开发板上的标号为:DS0和DS1。DS0是红色的,DS1是绿色的,主要是方便识别。一般的应用2个LED足够了,在调试代码的时候,使用LED来指示程序状态,是非常不错的一个辅助调试方法。ALIENTEK开发板几乎每个实例都使用了LED来指示程序的运行状态。

3.两个普通按键这是开发板板载的两个普通按键,可以用于人机交互的输入,这两个按键是直接连接在STM32的IO口上的,两个按键在开发板上的标号分别为:KEY0、KEY1。2.1实验分析

功能一:第一个按键调节1个LED的闪烁频率;(1-4秒)

本功能用到的硬件资源有:(1)指示灯DS0,DS0接PA8(2)定时器TIM3

(3)按键KEY_0

要实现按键改变LED的闪烁频率需要用STM32的定时器来产生PWM输出。使用TIM1的通道1产生PWM来控制DS0的亮度。通过按键改变闪烁频率,每按一次键修改一次TIM1_CCR1改变的梯度,从而改变输出PWM的占空比改变的频率,从而改变LED闪烁的频率。

配置步骤:

(1)开启TIM1时钟,配置PA8为复用输出。

(2)设置TIM3的ARR和PSC。

在开启了TIM1的时钟之后,我们要设置ARR和PSC两个寄存器的值来控制输出PWM的周期。当PWM周期太慢(低于50Hz)的时候,我们就会明显感觉到闪烁了。因此,PWM周期在这里不宜设置的太小。

(3)设置TIM1_CH1的PWM模式及通道方向。

设置TIM1_CH为PWM模式,因为DS0是低电平亮,而希望当CCR1的值小的时候,DS0就暗,CCR1值大的时候,DS0就亮,所以要通过配置TIM1_CCMR1的相关位来控制TIM1_CH1的模式。另外,我们要配置CH1为输出,所以要设置CC1S[1:0]为00。

(4)使能TIM1的CH1输出,使能TIM1。

接下来,需要开启TIM1的通道1的输出以及TIM1的时钟。前者通过TIM1_CCER来设置,是单个通道的开关,而后者则通过TIM1_CR1来设置,是整个TIM1的总开关。只有设置了这两个寄存器,这样我们才可能在TIM1的通道1上看到PWM波输出。

(5)设置MOE输出,使能PWM输出。

(6)修改TIM1_CCR1来控制占空比。

最后,在经过以上设置之后,PWM其实已经开始输出了,只是其占空比和频率都是固定的,而我们通过修改TIM1_CCR1则可以控制CH1的输出占空比。继而控制DS0的亮度。

功能二:一个按键调节另1个LED的亮度变化周期;(1-4秒)

本功能用到的硬件资源有:(1)指示灯DS1,DS1接PD2(2)按键KEY_1

通过调用Delay_ms()延时函数就可改变LED亮和暗的时间,初始时延时1秒,每按一次键,使延时增加1秒,当周期变成4秒的时候在回到1秒。

功能三:整合串口计算器(要求超级终端内实现,带删除功能)

本实验需要用到的硬件资源有:   (1)串口1

本功能用到的串口1与USB串口并没有在PCB上连接在一起,需要通过跳线帽来连接一下。这里我们把P4的RXD和TXD用跳线帽与PA9和PA10连接起来。

串口基本配置直接相关的寄存器。1.串口时钟使能。串口作为STM32的一个外设,其时钟由外设时钟使能寄存器控制,这里我们使用的串口1是在APB2ENR寄存器的第14位。2.串口复位。

3.串口波特率设置。每个串口都有一个自己独立的波特率寄存器USART_BRR,通过设置该寄存器就可以达到配置不同波特率的目的。

4.串口控制。STM32的每个串口都有3个控制寄存器USART_CR1~3,串口的很多配置都是通过这3个寄存器来设置的。这里只要用到USART_CR1。

5.数据发送与接收。STM32的发送与接收是通过数据寄存器USART_DR来实现的,这是一个双寄存器,包含了TDR和RDR。当向该寄存器写数据的时候,串口就会自动发送,当收到数据的时候,也是存在该寄存器内。

6.串口状态。串口的状态可以通过状态寄存器USART_SR读取。

通过以上一些寄存器的操作外加一下IO口的配置,我们就可以达到串口最基本的配置了。

串口配置好后,就可以通过超级终端进行调试。要实现多个数的加减乘除,先要将输入的算式识别出来,再根据四则运算进行运算并回显。并且要实现回删功能,每检测到一个删除键就打印一个空格并打印一个将光标前移一位。

2.2 UCOSII配置步骤

实验要求用UCOSII实时操作系统实现上述三个功能。UCOSII的任何任务都是通过一个叫任务控制块(TCB)的东西来控制的,每个任务管理块有3个最重要的参数:

1.任务函数指针;

2.任务堆栈指针;

3.任务优先级;

任务控制块就是任务在系统里面的身份证。运行UCOSII的步骤如下:1)移植UCOSIIALIENTEK提供的SYSTEM文件夹里面的系统函数直接支持UCOSII,只需要在sys.h文件里面将:SYSTEM_SUPPORT_UCOS宏定义改为1,即可通过delay_init函数初始化UCOSII的系统时钟节拍,为UCOSII提供时钟节拍。2)编写任务函数并设置其堆栈大小和优先级等参数。编写任务函数,以便UCOSII调用。设置函数堆栈大小,这个需要根据函数的需求来设置,如果任务函数的局部变量多,嵌套层数多,那么相应的堆栈就得大一些。

3)初始化UCOSII,并在UCOSII中创建任务调用OSInit,初始化UCOSII,通过调用OSTaskCreate函数创建我们的任务。4)启动UCOSII调用OSStart,启动UCOSII。通过以上4个步骤,UCOSII就开始在STM32上面运行了,这里还需要注意我们必须对os_cfg.h进行部分配置,以满足我们自己的需要。

三程序下载工具,超级终端等使用说明

3.1程序下载工具说明

STM32F103的程序下载有多种方法:USB、串口、JTAG、SWD等,这几种方式,都可以用来给STM32F103下载代码。本实验通过串口给STM32F103下载代码。STM32的串口下载一般是通过串口1下载的,本手册的实验平台ALIENTEKMiniSTM32F103,不是通过RS232串口下载的,而是通过自带的USB串口来下载。看起来像是USB下载(只需一根USB线,并不需要串口线)的,实际上,是通过USB转成串口,然后再下载的。

一键下载电路,则利用串口的DTR和RTS信号,分别控制STM32的复位和B0,配合上位机软件(flymcu,即mcuisp的最新版本),设置:DTR的低电平复位,RTS高电平进BootLoader,这样,B0和STM32的复位,完全可以由下载软件自动控制,只需要安装CH340G的驱动即可。

安装CH340G的驱动后,选择要下载的.hex文件,选择对应的串口再把DTR的低电平复位,RTS高电平进BootLoader,flymcu就会通过DTR和RTS信号来控制板载的一键下载功能电路。

3.2超级终端使用说明

由于windows10系统没有自带的超级终端,需要自己上网下载超级终端。其配置过程如下:

1.输入连接名称

2.选择对应的串口

3.把波特率设置为9600,数据控制流选为:无

4.进入属性进行ASCII码设置,将前两个方框勾上,点击确定即完成超级终端配置。

四程序分析说明(基于OS的分析各任务结构、关系)

由于要实现三个功能,所以除了开始任务之外,还要设置三个任务,LED0任务,LED1任务,计算器任务。主要程序如下:

#include

#include "sys.h"

#include "usart.h"

#include "delay.h"

#include "led.h"

#include "key.h"

#include "timer.h"

#include "includes.h"

/////////////////////////UCOSII任务设置///////////////////////////////////

//START任务,设置任务优先级

#define START_TASK_PRIO      10 //开始任务优先级最低

//设置堆栈大小

#define START_STK_SIZE  64

//任务堆栈

OS_STK START_TASK_STK[START_STK_SIZE];

//任务函数

void start_task(void *pdata);

//LED0任务

//优先级设置

#define LED0_TASK_PRIO       7

//设置堆栈大小

#define LED0_STK_SIZE  64

//任务堆栈

OS_STK LED0_TASK_STK[LED0_STK_SIZE];

//任务函数

void led0_task(void *pdata);

//LED1任务

//优先级设置

#define LED1_TASK_PRIO       6

//设置堆栈大小

#define LED1_STK_SIZE  64

//任务堆栈

OS_STK LED1_TASK_STK[LED1_STK_SIZE];

//任务函数

void led1_task(void *pdata);

//计算器任务

//优先级设置

#define COMPUTE_TASK_PRIO       5

//设置堆栈大小

#define COMPUTE_STK_SIZE  256

//任务堆栈

OS_STK COMPUTE_TASK_STK[LED1_STK_SIZE];

//任务函数

void compute_task(void *pdata);

//主函数

int main(void)

{

Stm32_Clock_Init(9); //系统始终设置

delay_init(72);     //延时初始化

LED_Init(); //初始化LED与硬件接口

OSInit();    OSTaskCreate(start_task,(void*)0,(OS_STK*)&START_TASK_STK[START_STK_SIZE-1],START_TASK_PRIO );                     //创建开始任务

OSStart();

}

//开始任务

void start_task(void *pdata)

{

OS_CPU_SR cpu_sr=0;

pdata = pdata;

OS_ENTER_CRITICAL();//进入临界区

OSTaskCreate(led0_task,(void*)0,(OS_STK*)&LED0_TASK_STK[LED0_STK_SIZE-1],LED0_TASK_PRIO);

OSTaskCreate(led1_task,(void*)0,(OS_STK*)&LED1_TASK_STK[LED1_STK_SIZE-1],LED1_TASK_PRIO);

OSTaskCreate(compute_task,(void*)0,(OS_STK*)&COMPUTE_TASK_STK[COMPUTE_STK_SIZE-1],COMPUTE_TASK_PRIO);

OSTaskSuspend(START_TASK_PRIO);

OS_EXIT_CRITICAL();//退出临界区

//LED0任务,按键实现LED0亮暗周期变化周期

void led0_task(void *pdata)

{

u8 t;

static u8 KEY_LED=1;

u16 led0pwmval=0;

u8 dir=1;

KEY_Init();          //按键初始化

uart_init(72,9600);  //串口初始化

TIM1_PWM_Init(899,0);//PWM频率=72000/(899+1)=80Khz

while(1)

{

delay_ms(10);//去抖动

t=KEY_Scan(0);//获得键值

switch(t)

{

case KEY0_PRES:

KEY_LED++;

break;

default:

delay_ms(10);

}

if(KEY_LED%4==0)

{

delay_ms(10);

if(dir)

led0pwmval+=40;

else

led0pwmval-=40;

if(led0pwmval>300)dir=0;

if(led0pwmval==0)dir=1;

LED0_PWM_VAL=led0pwmval;

}

if(KEY_LED%4==1)

{

delay_ms(10);

if(dir)

led0pwmval+=20;

else

led0pwmval-=20;

if(led0pwmval>300)dir=0;

if(led0pwmval==0)dir=1;

LED0_PWM_VAL=led0pwmval;

}

if(KEY_LED%4==2)

{

delay_ms(10);

if(dir)

led0pwmval+=10;

else

led0pwmval-=10;

if(led0pwmval>300)dir=0;

if(led0pwmval==0)dir=1;

LED0_PWM_VAL=led0pwmval;

}

if(KEY_LED%4==3)

{

delay_ms(10);

if(dir)

led0pwmval+=5;

else

led0pwmval-=5;

if(led0pwmval>300)dir=0;

if(led0pwmval==0)dir=1;

LED0_PWM_VAL=led0pwmval;

}

};

}

//LED1任务,实现按键改变LED闪烁频率

void led1_task(void *pdata)

{

u8 num;

u8 a;

static u8 KEY_N=1;

LED1=1;

while(1)

{

for(num=0;num

{

a=KEY_Scan(1);//获得键值

switch(a)

{

case KEY1_PRES:

KEY_N++;

break;

default:

delay_ms(10);

}

if(KEY_N==5)

KEY_N=1;

delay_ms(1000);

}

LED1=~LED1;

};

}

void compute_task(void *pdata)

{

u8 i;

u8 j;

u8 t;

u8 s;

u8 index;

u8 num_of_delete=0;//删除符个数

u8 rest_delete=0;    //剩余删除符数

u8 flag=0;

u8 num_of_operator=0;   //运算符数

u8 num_of_product=0;    //乘除运算符数

u8 rest_operator=0;       //剩余运算符数

u8 rest_product=0;        //剩余乘除运算符数

u8 len;             //输入字符长度

u32 num[10]={0,0,0,0,0,0,0,0,0,0};        //存放要计算的数

int  locate[10]={-1,0,0,0,0,0,0,0,0,0};      //存放运算符所在位数

u8 Operator[10]={0,0,0,0,0,0,0,0,0,0};    //存放运算符

u16 times=0;

u32 mid_result=0; //中间值

u32 final_result=0;   //最后值

uart_init(72,9600);

while(1)

{

if(USART_RX_STA&0x8000)

{

len=USART_RX_STA&0x3fff;//得到数据长度

printf("输入的式子为\r\n\r\n");

for(t=0;t

{

if(USART_RX_BUF[t]==8)//与删除符输出空格

{

printf(" ");

printf("%d",8);

}

USART1->DR=USART_RX_BUF[t];

while((USART1->SR&0X40)==0);//输入结束

}

//对删除键进行处理

for(t=0;t

{

if(USART_RX_BUF[t]==8)

num_of_delete++;

}

rest_delete=num_of_delete;

while(rest_delete!=0)//没有删除键时退出

{

for(t=0;t

{

if(USART_RX_BUF[t]==8)

{

flag=t;

break;

}

}

for(i=flag;i

{

USART_RX_BUF[i-1]=USART_RX_BUF[i+1];

}

rest_delete--;

len=len-2;

}

//获取运算符并存入数组

for(t=0;t

{

if(USART_RX_BUF[t]<48)

{

Operator[num_of_operator]=USART_RX_BUF[t];                locate[num_of_operator+1]=t;

num_of_operator++;

if(USART_RX_BUF[t]=='*'||USART_RX_BUF[t]=='/')

num_of_product++;

}

}

rest_operator=num_of_operator;

locate[num_of_operator+1]=len;

rest_product=num_of_product;

printf("\r\n\r\n");

//获取操作数并存入数组

for (s=0;s

{

for(i=locate[s]+1;i

{

num[s]=10*num[s]+(USART_RX_BUF[i]-48);

}

}

while(rest_product!=0)

{

//计算第一个乘除运算,并将两个数组分别前移,直到乘除运算符数为0

for(j=0;j

{

if(Operator[j]=='*'||Operator[j]=='/')

{

if(Operator[j]=='*')

mid_result=num[j]*num[j+1];

else

mid_result=num[j]/num[j+1];

rest_product--;

rest_operator--;

index=j;

num[index]=mid_result;

break;

}

}

for(j=index;j<8;j++)

{

num[index]=mid_result;

num[j+1]=num[j+2];

}

printf("%d",index);

for(j=index;j<8;j++)

{

Operator[j]=Operator[j+1];

}

}

//实现加减

if(rest_operator==0)

{

final_result=num[0];

}

//实现连续加减

else

{

final_result=num[0];

for(j=0;j

{

if(Operator[j]=='+')

final_result=final_result+num[j+1];

else

final_result=final_result-num[j+1];

}

rest_operator=0;

}

printf("\r\n\r\n答案为:\r\n\r\n");

for(t=0;t

{

USART1->DR=USART_RX_BUF[t];

while((USART1->SR&0X40)==0);

}

printf("=%d",final_result);/

//局部变量清零

for(i=0;i<10;i++)

{

num[i]=0;

}

for(i=0;i<10;i++)

{

Operator[i]=0;

}

num_of_operator=0;

rest_product=0;

num_of_product=0;

rest_operator=0;

num_of_delete=0;

rest_delete=0;

flag=0;

printf("\r\n\r\n");

USART_RX_STA=0;

}

else

{

times++;

if(times%400==0)printf("请输入算式以回车结束\r\n");

delay_ms(10);

}

}

}

五实验中遇到的问题及解决办法分析

1.超级终端无法输入数据

没有进行ASCII设置,将“以换行符作为末尾”和“本地回显输入的字符”两个选项勾上就可在超级终端中输入算式。

2.多个数的加减乘除运算输出结果错误

输出结果总是错误,于是将捕获的字符,识别的操作数数组,运算符数组等中间变量一一打印,观察每一步输出结果正确与否,一步一步调试,终于得以解决。

3.当在超级终端之中算式输入完成,按回车进行运算时,STM32总是死机

计算器任务中定义了很多局部变量,仔细查阅STM32手册后发现,在进行UCOSII任务配置时,将计算器任务堆栈设置的太小,CPU进入HardFault死机,将堆栈改大之后,不再出现死机的情况。

六总结,心得体会

通过这学期嵌入式课的学习,我对嵌入式操作系统有了深入的了解,尤其是UCOSII实时操作系统,它是一种占先式内核,对任务完成时间及其严格,能够方便的移植到各种嵌入式处理器,且占用资源少,可靠性高。

通过STM32的几个实验,增强了自己的动手能力。尤其是串口计算器实验,有一定的难度,要设计合理的算法,并将算法用代码实现,代码写完后还要进行不断的调试才能得到正确的结果。在做实验的过程中,我加深了对UCOSII操作系统的系统的理解,做到了理论与实际的结合。

在今后的学习和研究,我会更加注意将学到的理论知识应用到实际中去,真正做到理论与实际相结合。

(0)

相关推荐

  • 如何写出优秀的电路实验报告

    如何写出优秀的电路实验报告

  • 探究平面镜成像的特点(实验报告)

    探究平面镜成像的特点. 提出问题 01 平面镜成的是实像还是虚像? 02 是放大的还是缩小的像? 03 所成的像的位置是在什么地方? 猜想与假设 01 平面镜成的是虚像. 02 像的大小与物的大小相等 ...

  • win8怎么加载语言包实验报告?

    win8怎么加载语言包实验报告?

  • STM32入门学习经验总结

    STM32系列基于专为要求高性能.低成本.低功耗的嵌入式应用专门设计的ARMCortex-M3内核.按性能分成两个不同的系列:STM32F103"增强型"系列和STM32F101& ...

  • word2007去掉回车符的方法教程

    写实验报告时很想去掉word文档里的难看的回车符,随之上网找找,可是好多回复发现都不是很正确所以决定还是自己试试,不成想还真的找到了,为了大家方便少走弯路特意来分享。 方法如下: word07-> ...

  • word2013插入连字符与显示方法

    学生上交了一份实验报告,导师用Word打开一看,哎,版式先不说,乱七八糟,断词断句丝毫没有,这叫人怎么看?尤其是一些长的英文单词、词组、复合词,不断开,完全都认不出来。下面,就介绍一下Word2013 ...

  • 思科路由器配置站点到站点VPN的过程

    VPN的概念 虚拟专用网络功能是:在公用网络上建立专用网络,进行加密通讯。在企业网络中有广泛应用。VPN网关通过对数据包的加密和数据包目标地址的转换实现远程访问。VPN有多种分类方式,主要是按协议进行 ...

  • 液晶显示器背光类型及优缺点(LCD.CCFL.LED)

    液晶背光显示原理 液晶不同于等离子的最大区别就是液晶必须依靠被动光源,而等离子电视属于主动发光显示设备。目前市场上主流的液晶背光技术包括LED(发光二极管)和CCFL(冷阴极荧光 灯)两类。 冷阴极荧 ...

  • WPS 2012 五大实用功能介绍

    近期,WPS Office 2012正式发布,其灵巧轻快的身材、绚丽的WIN7界面、丰富的在线模板和素材、随时随地办公等特性都给广大用户带来全新的办公体验。借于WPS Office 2012的发布,一 ...