在没有外部硬件支持时,我们会使用KEIL5软件模拟仿真,这是是仿真必须要掌握的技巧。
1、点击“Project”,然后点击“Options for target '项目名字'”,点击“Device”,选择CPU型号。
2、点击“OK”
3、点击“Target”,勾选“Use Micro LIB”。注意:如果不勾选,软件仿真时,会出现无法打印浮点数,或者不打印小数点。
4、点击“OK”
5、点击“Debug”,配置如下:
注意:绿线左边属于软件仿真配置,绿线右边属于硬件仿真配置。
6、点击“OK”
7、重定义fputc函数,编写一个“USART1.c”,文件如下:
#include "USART1.h"
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
void USART1_GPIO_Config(void);
void USART1_NVIC_Cpnfig(void);
void USART1_Mode_Cpnfig(unsigned int bound);
void USART1_SendByte( unsigned char ch );
void USART1_SendString( char *str);
void USART1_Serial_Interface_Enable(unsigned int bound);
//函数功能:USART1的IO口配置
void USART1_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
//设置USART1的APB2外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;//选择PIN9,是USART1的TXD
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//设置引脚为复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//设置引脚的最高工作速率为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //选择PIN10,是USART1的RXD
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //设置引脚为输入悬浮
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//设置引脚的最高工作速率为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
//函数功能:USART1 NVIC 配置
void USART1_NVIC_Cpnfig(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
//NVIC_PriorityGroup_4设置NVIC中断分组4:表示抢占优先级为4位,取值为0~15,没有响应优先级,取值为0
//NVIC_PriorityGroup_3设置NVIC中断分组3:表示抢占优先级为3位,取值为0~7,响应优先级只有1位,取值为0~1
//NVIC_PriorityGroup_2设置NVIC中断分组2:表示抢占优先级为2位,取值为0~3,响应优先级只有2位,取值为0~3
//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
//选择中断源为USART1_IRQn
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 13;
//设置抢先优先级为13
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //设置响应优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
//函数功能:USART1配置:波特率为bound,数字为8位,停止位为1位,无奇偶校验,允许发送和接收数据,允许中断,使能串口模块
void USART1_Mode_Cpnfig(unsigned int bound)
{
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = bound;//一般设置为9600;
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);//开启接收中断
//当开启串口中断,一定要写其中断服务程序,否则可能会导致FreeRTOS的任务不执行
USART_Cmd(USART1, ENABLE); //使能串口
}
//函数功能:串口1发送一个字节
void USART1_SendByte( unsigned char ch )
{
USART_SendData(USART1, ch);
while( USART_GetFlagStatus(USART1,USART_FLAG_TC)!= SET);
//等待发送完成标志位被置1
}
//函数功能:串口1发送字符串
void USART1_SendString( char *str)
{
unsigned int k=0;
do{
USART1_SendByte( *(str + k) );
k++;
}while(*(str + k)!='\0');
}
//函数功能:USART1配置
void USART1_Serial_Interface_Enable(unsigned int bound)
{
USART1_GPIO_Config();
USART1_NVIC_Cpnfig();
USART1_Mode_Cpnfig(bound);
}
//函数功能:USART1中断服务函数
void USART1_IRQHandler(void)
{
unsigned char temp;
(void)temp;//不让temp产生警告
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
temp=USART_ReceiveData(USART1); //从GSM串口读取一个字节;
}
if(USART_GetFlagStatus(USART1,USART_FLAG_PE) != RESET)
{
USART_ReceiveData(USART1);//读串口
USART_ClearFlag(USART1, USART_FLAG_PE);
}
if(USART_GetFlagStatus(USART1,USART_FLAG_ORE) != RESET)
{
USART_ReceiveData(USART1);//读串口
USART_ClearFlag(USART1,USART_FLAG_ORE); //清除溢出中断
}
if(USART_GetFlagStatus(USART1,USART_FLAG_FE) != RESET)
{
USART_ReceiveData(USART1);//读串口
USART_ClearFlag(USART1,USART_FLAG_FE);
}
}
//加入以下代码,支持printf函数
//在进行硬件仿真时,可不需要选择“use MicroLIB”
//但在进行软件仿真时,必须勾选“use MicroLIB”,否则,无法打印“浮点数”
#if VirtualSerialPort == 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
//函数功能:发送ch的值给USART1串口
int fputc(int ch, FILE *f)
{
USART_SendData(USART1, (unsigned char) ch);
while( USART_GetFlagStatus(USART1,USART_FLAG_TC)!= SET);
//等待发送完成标志位被置1
return ch;
}
#else
#define ITM_Port8(n) (*((volatile unsigned char *)(0xE0000000+4*n)))
#define ITM_Port16(n) (*((volatile unsigned short*)(0xE0000000+4*n)))
#define ITM_Port32(n) (*((volatile unsigned long *)(0xE0000000+4*n)))
#define DEMCR (*((volatile unsigned long *)(0xE000EDFC)))
#define TRCENA 0x01000000
struct __FILE
{
int handle; /* Add whatever you need here */
};
FILE __stdout;
FILE __stdin;
int fputc(int ch, FILE *f)
{
if (DEMCR & TRCENA)
{
while (ITM_Port32(0) == 0);
ITM_Port8(0) = ch;
}
return(ch);
}
#endif
8、“USART1.h”文件如下:
#ifndef __USART1_H
#define __USART1_H
#include "stm32f10x.h"
//使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t
#define VirtualSerialPort 1 //使用UART1的printf功能
//#define VirtualSerialPort 0 //使用JLINK虚拟串口的printf功能
extern void USART1_SendByte( unsigned char ch );
extern void USART1_SendString( char *str);
extern void USART1_Serial_Interface_Enable(unsigned int bound);
#endif /* __USART1_H */
#include "stm32f10x.h"
//使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "USART1.h"
//注意:"USART1.h"要放在 "stdio.h" 包含文件之后,如果这个位置颠倒了,业不能打印浮点数。
void Print_Float(void)
{
float f;
f=10.5;
printf("f=%0.2f\r\n", f);
}
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
USART1_Serial_Interface_Enable(115200);//初始化串口1
printf("\r\nCPU reset\r\n");
Print_Float();
while(1)
{
}
}
9、点击“Debug”,点击“View”,点击“Serial Windows”,点击“UART#1”,就可以打开KEIL中的串口1,见下图:
10、点击“Debug”菜单中的“Run”,就可以仿真了。
注意:"USART1.h"要放在 "stdio.h" 包含文件之后,如果这个位置颠倒了,业不能打印浮点数。 这个可能和8个字节对齐有关,如果你知道,请给我留言。