proteus实现串口发送(stm32)

发布于:2025-07-04 ⋅ 阅读:(17) ⋅ 点赞:(0)

 一、新建工程

1、工程命名

2、选择工程存储位置

3、默认下一步

4、默认下一步

5、选择没有固件项目,下一步

二、器件放置并连线

1、点击左边工具栏中运放的形状的符号

2、再点击‘P’,搜索器件

3、搜索器件并放置连线

按键控制LED需要的器件有,按键、电容、LED、电阻、主控,LCD1602显示屏,主控选择的是stm32f103r6。

4、串口放置与连接

串口元件名称为“compim”

5、器件连线,如下图所示:

图中使用了网络标签,网络标签是在电源、GND的那个工具栏中的DEFAULT。还有一点要注意的是,stm32的rx连接串口元件的rx,tx连接串口元件的tx,不需要交叉连接

三、网络配置

1、想要仿真成功运行,首先配置主控参数,双击主控,选择Program File,即代码的hex文件路径,以及Crystal Frequency,即晶振的频率8M,具体如下图所示:

2、仿真供电网络的配置,点击工具栏中的“design”,再点击配置供电网络(点击design后的第四个选项),将"VDDA"增加到"VCC/VDD"中,将"VSSA"增加到"GND"中,如下图所示:

3、虚拟串口软件

proteus仿真串口,串口助手接收需要先在虚拟串口软件中绑定一个串口对,我这里绑定的是COM8和COM9。

4、proteus中串口元件的参数设置

我在proteus中设置的为COM8,波特率为9600,那么在串口助手中则设置串口为COM9,波特率为9600,这样才能接收到数据。

四、代码分享

1、LCD.c代码

#include "stm32f10x.h"                  // Device header
#include "LCD1602.h"


void LCD1602_PORT_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOx,ENABLE);	 //使能PC端口时钟	
	GPIO_InitStructure.GPIO_Pin = GPIOx_Pin;				               //端口配置 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		   //推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;		         //IO口速度为10MHz
  GPIO_Init(GPIOx, &GPIO_InitStructure);					               //根据设定参数初始化
}



void delay_nus(uint16_t n)//微妙级延时,通常情况此函数可以不用更改
{
  uint16_t i;
  while(n--)
  {
    i=20;
    while(i--);
  }
}




void delay_nms(uint16_t n) //毫秒级延时,保通常情况此函数可以不用更改
{
    while(n--)
      delay_nus(1100);
}

void LCD_init(void) //初始化,通常情况此函数可以不用更改
{
		LCD1602_PORT_Init(); //LCD 1602使用的PC口初始化
    LCD_RS_0;
    LCD_RW_0; 
    LCD_EN_0; 

    delay_nms(5);
    LCD_cmd(0x38);//16*2显示,5*7点阵,8数据
    delay_nms(1);
    LCD_cmd(0x38);//16*2显示,5*7点阵,8数据
    delay_nms(1);
    LCD_cmd(0x38);//16*2显示,5*7点阵,8数据
    delay_nms(1);
    LCD_cmd(0x08);//先关显示,后开显示
    delay_nms(1);
    LCD_cmd(0x01);//清屏
    delay_nms(1);
    LCD_cmd(0x06);//写命令,注意
    delay_nms(1);   
    LCD_cmd(0x0c);//显示开,关光标
    delay_nms(1);
}

void LCD_clr(void)  //清屏
{
    LCD_cmd(0x01);
}

void LCD_cmd(uint16_t cmd)//写命令
{   
		LCD_RS_0;        
		delay_nus(1);   
		LCD_RW_0;     
		delay_nus(1);   
		LCD_EN_0;      
		delay_nus(1);
		GPIO_SetBits(LCDPORT, LCD_DATA_PORT & cmd);
		GPIO_ResetBits(LCDPORT, LCD_DATA_PORT &(~cmd));
		LCD_EN_1;
		delay_nus(1);    
		LCD_EN_0;     
}

void LCD_dat(uint8_t dat)//写数据
{
    delay_nms(1);   
    LCD_RS_1;
    delay_nus(1);  		 
    LCD_RW_0;       	       
    delay_nus(1);
    LCD_EN_0;                  
    delay_nus(1);
		GPIO_SetBits(LCDPORT, LCD_DATA_PORT & dat);
		GPIO_ResetBits(LCDPORT, LCD_DATA_PORT &(~dat));

    LCD_EN_1;       	   
    delay_nus(1);   
    LCD_EN_0;       	 
}

//写字符串

 void LCD_Write_String(uint8_t x,uint8_t y,uint8_t *s) 
{     
  if (y == 0) 
 	{     
	   LCD_cmd(0x80 + x);     //第一行
 	}
                  
                  else 
 	{      
 	   LCD_cmd(0xC0 + x);      //第二行
 	} 
                  
                  
                 while (*s) 
 	{     
                     LCD_dat( *s);     
                      s ++;     
 	}
 }
//写字符

void LCD_Write_Char(uint8_t x,uint8_t y,uint8_t Data) 
{     
                  if (y == 0) 
 	{     
                    LCD_cmd(0x80 + x);     
 	}    
                  else 
 	{     
                      LCD_cmd(0xC0 + x);     
 	}        
                    LCD_dat( Data);  
}



void LCD_pos(uint16_t x,uint16_t y)//显示位置,不需要修改
{
     if(y)
     LCD_cmd(x | 0xc0);
   	 else
     LCD_cmd(x | 0x80);
}

void LCD_printc(uint16_t x,uint16_t y,uint8_t c)//显示字符,不需要修改
{
    LCD_pos(x,y);
    LCD_dat(c);
}

void LCD_prints(uint16_t x,uint16_t y,uint8_t *s)//显示字符串,不需要修改
{
    LCD_pos(x,y);
    while(*s!='\0')
    {
        LCD_dat(*s);
        s++;
        //delay_nms(1);
    }
}

2、LCD.h代码

#ifndef __LCD1602_H
#define __LCD1602_H



/***********端口定义**********************************************************/
/*#define rs  GPIOSetValue(PORT2,0,rs)
#define rw  GPIOSetValue(PORT2,1,rw)
#define e   GPIOSetValue(PORT2,2,e)
*/

#define  LCDPORT         GPIOC


#define  RS_PIN          GPIO_Pin_8
#define  RW_PIN          GPIO_Pin_9
#define  EN_PIN          GPIO_Pin_10

#define GPIOx            GPIOC 
#define RCC_APB2Periph_GPIOx            RCC_APB2Periph_GPIOC 
#define GPIOx_Pin        GPIO_Pin_All 
#define LCD_DATA_PORT    0xff



#define LCD_RS_1 GPIO_SetBits(LCDPORT, RS_PIN) 		
#define LCD_RS_0 GPIO_ResetBits(LCDPORT, RS_PIN)
#define LCD_RW_1 GPIO_SetBits(LCDPORT, RW_PIN)		
#define LCD_RW_0 GPIO_ResetBits(LCDPORT, RW_PIN)
#define LCD_EN_1 GPIO_SetBits(LCDPORT, EN_PIN)	
#define LCD_EN_0 GPIO_ResetBits(LCDPORT, EN_PIN)






void LCD1602_PORT_Init(void);

void LCD_init(void);			        
void LCD_clr(void);			        
void LCD_cmd(uint16_t cmd);		        
void LCD_dat(uint8_t dat);		        
void LCD_pos(uint16_t x,uint16_t y);	
void LCD_Write_Char(uint8_t x,uint8_t y,uint8_t Data) ;
void LCD_printc(uint16_t x,uint16_t y,uint8_t c);	
void LCD_prints(uint16_t x,uint16_t y,uint8_t *s);	
extern void delay_nus(uint16_t us);	        
extern void delay_nms(uint16_t ms);	  

 void LCD_Write_String(uint8_t x,uint8_t y,uint8_t *s) ;
#endif

3、按键代码

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

/**
  * 函    数:按键初始化
  * 参    数:无
  * 返 回 值:无
  */
void Key_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启GPIOB的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 ;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);						//将PB1和PB11引脚初始化为上拉输入
}

/**
  * 函    数:按键获取键码
  * 参    数:无
  * 返 回 值:按下按键的键码值,范围:0~2,返回0代表没有按键按下
  * 注意事项:此函数是阻塞式操作,当按键按住不放时,函数会卡住,直到按键松手
  */
uint8_t Key_GetNum(void)
{
	uint8_t KeyNum = 0;		//定义变量,默认键码值为0
	
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)			//读PB1输入寄存器的状态,如果为0,则代表按键1按下
	{
		Delay_ms(20);											//延时消抖
		while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1)==0 );	//等待按键松手
//		Delay_ms(20);											//延时消抖
		KeyNum = 1;												//置键码为1
	}
	

	
	return KeyNum;			//返回键码值,如果没有按键按下,所有if都不成立,则键码为默认值0
}

4、串口文件代码

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>

/**
  * 函    数:串口初始化
  * 参    数:无
  * 返 回 值:无
  */
void Serial_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);	//开启USART1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA9引脚初始化为复用推挽输出
	
	/*USART初始化*/
	USART_InitTypeDef USART_InitStructure;					//定义结构体变量
	USART_InitStructure.USART_BaudRate = 9600;				//波特率
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//硬件流控制,不需要
	USART_InitStructure.USART_Mode = USART_Mode_Tx;			//模式,选择为发送模式
	USART_InitStructure.USART_Parity = USART_Parity_No;		//奇偶校验,不需要
	USART_InitStructure.USART_StopBits = USART_StopBits_1;	//停止位,选择1位
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;		//字长,选择8位
	USART_Init(USART1, &USART_InitStructure);				//将结构体变量交给USART_Init,配置USART1
	
	/*USART使能*/
	USART_Cmd(USART1, ENABLE);								//使能USART1,串口开始运行
}

/**
  * 函    数:串口发送一个字节
  * 参    数:Byte 要发送的一个字节
  * 返 回 值:无
  */
void Serial_SendByte(uint8_t Byte)
{
	USART_SendData(USART1, Byte);		//将字节数据写入数据寄存器,写入后USART自动生成时序波形
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);	//等待发送完成
	/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}

/**
  * 函    数:串口发送一个数组
  * 参    数:Array 要发送数组的首地址
  * 参    数:Length 要发送数组的长度
  * 返 回 值:无
  */
void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)		//遍历数组
	{
		Serial_SendByte(Array[i]);		//依次调用Serial_SendByte发送每个字节数据
	}
}

/**
  * 函    数:串口发送一个字符串
  * 参    数:String 要发送字符串的首地址
  * 返 回 值:无
  */
void Serial_SendString(char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)//遍历字符数组(字符串),遇到字符串结束标志位后停止
	{
		Serial_SendByte(String[i]);		//依次调用Serial_SendByte发送每个字节数据
	}
}

/**
  * 函    数:次方函数(内部使用)
  * 返 回 值:返回值等于X的Y次方
  */
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;	//设置结果初值为1
	while (Y --)			//执行Y次
	{
		Result *= X;		//将X累乘到结果
	}
	return Result;
}

/**
  * 函    数:串口发送数字
  * 参    数:Number 要发送的数字,范围:0~4294967295
  * 参    数:Length 要发送数字的长度,范围:0~10
  * 返 回 值:无
  */
void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i ++)		//根据数字长度遍历数字的每一位
	{
		Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');	//依次调用Serial_SendByte发送每位数字
	}
}

/**
  * 函    数:使用printf需要重定向的底层函数
  * 参    数:保持原始格式即可,无需变动
  * 返 回 值:保持原始格式即可,无需变动
  */
int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);			//将printf的底层重定向到自己的发送字节函数
	return ch;
}

/**
  * 函    数:自己封装的prinf函数
  * 参    数:format 格式化字符串
  * 参    数:... 可变的参数列表
  * 返 回 值:无
  */
void Serial_Printf(char *format, ...)
{
	char String[100];				//定义字符数组
	va_list arg;					//定义可变参数列表数据类型的变量arg
	va_start(arg, format);			//从format开始,接收参数列表到arg变量
	vsprintf(String, format, arg);	//使用vsprintf打印格式化字符串和参数列表到字符数组中
	va_end(arg);					//结束变量arg
	Serial_SendString(String);		//串口发送字符数组(字符串)
}

5、main函数代码

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "LED.h"
#include "Key.h"
#include "LCD1602.h"
#include "stdint.h"
#include "stdio.h"
#include "stm32f10x_conf.h"
#include "Serial.h"

/****全局变量******************************************/
uint8_t lcd_dat1[20];//液晶第一行
uint8_t lcd_dat2[20];//液晶第二行
uint8_t KeyNum=0;

int main(void)
{
	RCC_SYSCLKConfig(RCC_SYSCLKSource_HSI);
	uint16_t temp=0;
	Key_Init();
	LCD_init();    //LCD1602初始化
	LCD_clr();//LCD清屏幕	
	Serial_Init();

	while (1)
	{
		sprintf(lcd_dat1,"%d",temp);
		LCD_prints(0,0,lcd_dat1);//液晶显示第一行
		KeyNum=Key_GetNum();
		if(KeyNum==1)//显示数字加一
		{
			temp++;
			Serial_Printf("%d\r\n",temp);
		}
		
	
	}
}

main函数实现了LCD显示数字,按一下按键数字加一,并且串口发送数据到串口助手。

五、仿真效果

六、仿真与实物不同之处

1、仿真不需要晶振电路

2、串口的波特率和端口一定要设置好,并且proteus元件中的波特率每次打开要重新设置


网站公告

今日签到

点亮在社区的每一天
去签到