STC15单片机-串口打印

STC15单片机串口打印

实现功能

1.上电时,通过TTL转USB接口输出系统启动信息

2.运行时,间隔100ms输出hello字符串和一个自动加1的变量

STC15L2K32S2型号串口配置

串口的使用跟之前STC89C52的一样,只不过这个型号具有两个串口,都是UART(通用异步收发器),分别在两组不同的引脚上

串口1建议放在[P3.6/RxD_2,P3.7/TxD_2]或[P1.6/RxD_3/XTAL2,P1.7/TxD_3/XTAL1]上。

寄存器的配置可用STC-ISP软件生成,也可根据手册进行配置,最后记得打开中断

在这里插入图片描述

程序

文件结构

在这里插入图片描述

main.c ->主函数文件,包含main函数等;

Public.c ->公共函数文件,包含Delay延时函数等;

Sys_init ->系统初始化函数,包含GPIO初始化函数等;

LED.c->LED外设函数,包含LED打开、关闭函数等;

Timer0.c ->定时器函数,包含定时器初始化,中断函数等;

KEY1.c->按键1函数,包含按键检测,中断函数等;

KEY2.c ->按键⒉函数,包含按键状态机检测函数等;

PWM. c ->PWM初始化、亮度调节、占空比储存与恢复函数等;

IAP.c->字节读、字节写、扇区擦除等函数。

UART1.c->串口1初始化、发送、中断等函数。

UART.h:

主要是定义波特率的枚举类型,之前用串口都是程序写死波特率,要改的话又要用软件生成一次,这次的灵活点,定义该波特率枚举类型后在后面可以用switch语句列出这四种波特率的TH1、TL1的初始值,在使用时,直接修改波特率的变量的值即可,非常方便

然后就是定义接口类型的枚举,再对串口的结构体进行定义,函数指针是等UART.c里的函数实现后,把名字复制过来的

#ifndef __UART_H_
#define __UART_H_

//定义波特率的枚举类型
typedef enum
{
  Band_4800   = (uint8_t)0,
  Band_9600   = (uint8_t)1,
  Band_19200  = (uint8_t)2,
  Band_115200 = (uint8_t)3
}BandRate_t;

//定义接口类型的枚举类型
typedef enum
{
  TTL     = (uint8_t)0,
  RS_485  = (uint8_t)1,
  RS_232  = (uint8_t)2
}Interface_Type_t;

//定义异步通信串口结构体类型
typedef struct 
{
  BandRate_t        ucBandRate;         //波特率
  uint8_t volatile  ucTX_Busy_Flag;     //发送忙碌标志
  uint8_t volatile  ucRec_Flag;         //接收标志位
  uint8_t volatile  ucRec_Cnt;          //接收计数

  uint8_t *pucSend_Buffer;            //发送缓存指针
  uint8_t *pucRec_Buffer;             //接收缓存指针

  void (*UART_Init)();                        //串口初始化
  void (*UART_SendData)(uint8_t);             //串口发送字符
  void (*UART_SendArray)(uint8_t*,uint16_t);  //串口发送数组
  void (*UART_SendString)(uint8_t *);         //串口发送字符串
  void (*Protocol)();                         //接口协议
  
  uint8_t Interface_Type;                     //接口类型
  void (*RS485_Set_SendMode)();               //RS-485接口设置为发送模式
  void (*RS485_Set_RecMode)();                //RS-485接口设置为接收模式

}UART_t;

/* extern variables-----------------------------------------------------------*/

/* extern function prototypes-------------------------------------------------*/ 

#endif
/********************************************************
  End Of File
********************************************************/
UART1.h:

主要是先宏定义后续会学到的RS-485接口的单片机引脚,然后就把UART.h中的结构体变量声明为外部变量

#ifndef __UART1_H_
#define __UART1_H_

//串口引脚宏定义
#define UART1_TX  P37       //这两个引脚接到RS-485
#define UART1_RX  P36

/* extern variables-----------------------------------------------------------*/
extern UART_t idata UART1;
/* extern function prototypes-------------------------------------------------*/ 

#endif
/********************************************************
  End Of File
********************************************************/
UART1.c:

与串口相关的分别是4个函数,串口初始化,串口发送字符,串口发送数组,串口发送字符串,另外对putchar函数进行重写,实现printf函数的重定向,为什么这里修改putchar函数而不是fputc函数,可以看之前的的文章(串口打印-printf重定向

/* Includes ------------------------------------------------------------------*/
#include <main.h>

/* Private define-------------------------------------------------------------*/
#define UART1_Send_LENGTH 20
#define UART1_Rec_LENGTH  10
/* Private variables----------------------------------------------------------*/
static uint8_t idata ucSend_Buffer[UART1_Rec_LENGTH]  = {0};
static uint8_t idata ucRec_Buffer[UART1_Rec_LENGTH]   = {0x00};
/* Private function prototypes------------------------------------------------*/
static void Init();                                 //串口初始化
static void SendData(uint8_t dat);                  //串口发送字符
static void SendArray(uint8_t *p_Arr,uint16_t LEN); //串口发送数组
static void SendString(uint8_t *p_Str);             //串口发送字符串
static void Protocol();                             //串口协议

static void RS485_Set_SendMode();                   //RS-485设置为发送模式
static void RS485_Set_RecMode();                    //RS-485设置为接收模式

/* Public variables-----------------------------------------------------------*/
UART_t idata UART1 = 
{
  Band_115200,
  FALSE,
  FALSE,
  0,

  ucSend_Buffer,
  ucRec_Buffer,
  Init,
  SendData,
  SendArray,
  SendString,
  Protocol,

  TTL,
  RS485_Set_SendMode,
  RS485_Set_RecMode
};

/*
* @name   Init
* @brief  串口1初始化
* @param  None
* @retval None   
*/
static void Init()
{
  //串口1默认映射至P30,P31,因为切换寄存器AUXR1上电默认值为0x00,默认串口1在P3.0和P3.1

  	SCON = 0x50;		//8位数据,可变波特率,REN位置1,开启中断
  //辅助寄存器AUXR的第6位T1x12置1,设置定时器1的速度是传统8051的12倍,不分频
	AUXR |= 0x40;
  //1111 1110 最低位S1ST2清0,选择定时器1作为串口1的波特率发生器
	AUXR &= 0xFE;		//串口1选择定时器1为波特率发生器
  
	TMOD &= 0x0F;		//设定定时器1为16位自动重装方式
	switch (UART1.ucBandRate)
    {
        case Band_4800:   TL1 = 0xCD; TH1 = 0xFD; break;
        case Band_9600:   TL1 = 0xE0; TH1 = 0xFE; break;
        case Band_19200:  TL1 = 0x70; TH1 = 0xFF; break;
        case Band_115200: TL1 = 0xE8; TH1 = 0xFF; break;
        default:          TL1 = 0xCD; TH1 = 0xFD; break;
    }
	ET1 = 0;		//禁止定时器1中断
	TR1 = 1;		//启动定时器1
}

/*
* @name   SendData
* @brief  发送字符
* @param  dat:待发送的数据
* @retval None   
*/
static void SendData(uint8_t dat)
{
  while(UART1.ucTX_Busy_Flag);    //等待前面的数据发送完,串口中断中发送数据后标志位会置FALSE
  UART1.ucTX_Busy_Flag = TRUE;    //置为忙碌标志位
  SBUF = dat;                     //写数据到UART寄存器
}

/*
* @name   SendArray
* @brief  发送数组
* @param  p_Arr:数组首地址,LEN:发送长度
* @retval None   
*/
static void SendArray(uint8_t *p_Arr,uint16_t LEN)
{
  uint16_t i = 0;
  for(i = 0;i<LEN;i++)
  {
    UART1.UART_SendData(*(p_Arr+i));
  }
  while(UART1.ucTX_Busy_Flag);
}

/*
* @name   SendString
* @brief  发送字符串
* @param  p_Arr:字符串首地址
* @retval None   
*/
static void SendString(uint8_t *p_Str)
{
  while(*(p_Str) != '\0')
  {
    UART1.UART_SendData(*(p_Str++));
  }
  while(UART1.ucTX_Busy_Flag);
}

/*
* @name   RS485_Set_SendMode
* @brief  RS_485接口设置为发送模式
* @param  None
* @retval None   
*/
static void RS485_Set_SendMode()
{

}

/*
* @name   RS485_Set_RecMode
* @brief  RS_485接口设置为接收模式
* @param  None
* @retval None   
*/
static void RS485_Set_RecMode()
{

}

/*
* @name   putchar
* @brief  字符发送函数重定向
* @param  c:发送的字符
* @retval char   
*/
extern char putchar(char ch)
{
  UART1.UART_SendData((uint8_t)ch);   //在putchar函数内直接调用串口发送字符函数
  return ch;
}

/*
* @name   UART1_isr
* @brief  串口1中断处理函数
* @param  None
* @retval None   
*/
void UART1_isr() interrupt 4
{
  if(RI)
  {
    RI = (bit)0;                   //清除接收中断标志
    /*UART1_Rec_LENGTH宏定义为10,所以接收的数据不能超过10个字节
    UART1.ucRec_Cnt表示数组下标,初始化为0*/
    if(UART1.ucRec_Cnt < UART1_Rec_LENGTH)
    {
      ucRec_Buffer[UART1.ucRec_Cnt++] = SBUF;
    }
    UART1.ucRec_Flag = TRUE;      //接收完成标志位
  }

  if(TI)
  {
    TI = (bit)0;                    //清除发送中断标志
    UART1.ucTX_Busy_Flag = FALSE;   //清除忙碌标志
  }
}

/*
* @name   Protocol
* @brief  串口协议
* @param  None
* @retval None   
*/
static void Protocol()
{

}
/********************************************************
  End Of File
********************************************************/

看教学时需要注意的地方

1.在串口协议中,奇偶校验位用的不多,一般用CRC校验,在Modbus总线中会有介绍

2.收发双方的比特率要一致

3.串口通信时,STC-ISP软件尽量选择外部时钟,因为内部时钟受环境因素影响比较大,会导致波特率发生偏差,会有不稳定现象

4.发送数组的函数中形参指针不能++,所以改为+i;如果将形参赋给另一个指针,那指针就可++

5.printf打印重定向:首先main.h中添加头文件< stdio.h >,然后Usart1.c中修改putchar函数,内部调用串口发送字符函数,然后main.c主函数中使用printf(“xxx:%d”,xxx)通过串口打印数据

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值