• 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
    ********************************************************/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    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
    ********************************************************/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    UART1.c:

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

    /* Includes ------------------------------------------------------------------*/
    #include 
    
    /* 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187

    看教学时需要注意的地方

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

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

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

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

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

  • 相关阅读:
    【Vue】vuex 求和案例
    赋能智慧校园!A3D数字孪生可视化,轻量又高效!
    Word文件不能编辑是什么原因?
    操作系统实验五 进程间通信-管道通信
    WalleWeb简化你的DevOps部署流程
    flink教程(2)-source- sink
    Vue13 监视属性
    Centos 安装 Docker
    vue2 ElementUI 表单标签、表格表头添加问号图标提示
    八、C++ 类的构造函数和析构函数
  • 原文地址:https://blog.csdn.net/weixin_46251230/article/details/126679956