《DMG474开发指南_V1.1》第二十九章 232实验

第二十九章 232实验


       在本章,我们将通过USB转232连接电机开发板实现与PC端的通信,并将发送与接收到的数据显示在TFTLCD模块上。

       本章分为如下几个部分:

       29.1 232简介

       29.2 硬件设计

       29.3 程序设计

       29.4 下载验证


        29.1 232简介

       RS-232接口符合美国电子工业联盟(EIA)制定的串行数据通信的接口标准,原始编号全称是EIA-RS-232(简称232,RS232)。它被广泛用于计算机串行接口外设连接。连接电缆和机械、电气特性、信号功能及传送过程。RS232的特点包括:

       ① 接口的信号电平值较高,易损坏接口电路的芯片。RS232接口任何一条信号线的电压均 为负逻辑关系,即:逻辑“1”为-3— -15V;逻辑“0”:+3— +15V ,噪声容限为2V。即要求接收器能识别高于+3V的信号作为逻辑“0”,低于-3V的信号作为逻辑“1”,TTL电平为5V为逻辑正,0为逻辑负 。与TTL电平不兼容故需使用电平转换电路方能与TTL电路连接。

       ② RS-232规定的标准传送速率有50b/s、75b/s、110b/s、150b/s、300b/s、600b/s、1200b/s、2400b/s、4800b/s、9600b/s、19200b/s,可以灵活地适应不同速率的设备。

       ③ RS-232总线规定了25条线,包含了两个信号通道,即第一通道(称为主通道)和第二通道(称为副通道)。利用RS- 232总线可以实现全双工通信,通常使用的是主通道,而副通道使用较少。在一般应用中,使用3条~9条信号线就可以实现全双工通信,采用三条信号线(接收线、发送线和信号线)能实现简单的全双工通信过程。

       ④ RS -232采用串行传送方式,将TTL电平转换为RS-232C电平,其传送距离一般可达30 m。若采用光电隔离20 mA的电流环进行传送,其传送距离可以达到1000 m。另外,如果在RS-232总线接口再加上Modem,通过有线、无线或光纤进行传送,其传输距离可以更远。


        29.2 硬件设计


       1. 例程功能

       经过前面的学习我们知道实际的RS232仍是串行通讯的一种电平传输方式,那么我们实际通讯时可以使用串口进行实际数据的收发处理,使用232转换芯片将串口信号转换为232的电平信号进行传输,本章,我们只需要配置好串口2,就可以实现正常的232通信了,串口2的配置和串口1基本类似,只是串口2的时钟来自APB1,最大频率为170Mhz。

       本章将实现这样的功能:通过USB转232连接电机开发板的DB9接口,按下KEY0,发送5个数据给串口调试助手,并在开发板上显示发送和接收到的值,也可以通过USMART调用rs232_send_data函数,实现发送指定的数据。


       2. 硬件资源


       1)LED灯

              LED0 – PE0

       2)USART2,用于232信号串行通讯。

       3)正点原子1.3寸TFTLCD模块(SPI接口)

       4)RS232收发芯片SP3232/TPT3232

       5)USB转232串口线


       3. 原理图

       根据我们需要实现的程序功能,我们设计电路原理如下:


图29.2.2 RS232连接原理设计


       从上图可以看出:电机开发板的串口2(PD5、PD6)连接在TP3232上,COM1是DB9母头,我们只需要将USB转232接在COM1就可以实现PC与电机开发板的通信。


        29.3 程序设计


       29.3.1 RS232的HAL库驱动

       由于232实际上是串口通讯,我们参照串口实验一节使用类似的HAL库驱动即可,在这里分析一下RS232配置步骤。


       RS232配置步骤

       1)使能串口和GPIO口时钟

       本实验用到USART2口,使用PD5和PD6作为串口的TX和RX脚,因此需要先使能USART2和GPIOC、GPIOD时钟。参考代码如下:

__HAL_RCC_USART2_CLK_ENABLE();          /* 使能USART2时钟 */
__HAL_RCC_GPIOD_CLK_ENABLE();           /* 使能GPIOD时钟 */

       2) 串口参数初始化(波特率、字长、奇偶校验等)

       HAL库通过调用串口初始化函数HAL_UART_Init完成对串口参数初始化,详见例程源码。

       该函数通常会调用:HAL_UART_MspInit函数来完成对串口底层的初始化,包括:串口及GPIO时钟使能、GPIO模式设置、中断设置等。但是本实验避免与USART1冲突,所以把串口底层初始化没有放在HAL_UART_MspInit函数里。

       3)GPIO模式设置(速度,上下拉,复用功能等)

       GPIO模式设置通过调用HAL_GPIO_Init函数实现,详见本例程源码。

       4)开启串口相关中断,配置串口中断优先级

       本实验我们使用串口中断来接收数据。我们使用HAL_UART_Receive_IT函数开启串口中断接收,并设置接收buffer及其长度。通过HAL_NVIC_EnableIRQ函数使能串口中断,通过HAL_NVIC_SetPriority函数设置中断优先级。

       5)编写中断服务函数

       串口5中断服务函数为:USART2_IRQHandler,当发生中断的时候,程序就会执行中断服务函数,在这里就可以对接收到的数据进行处理,详见本例程源码。

       6)串口数据接收和发送

       最后我们可以通过读写USART_RDR/USART_TDR寄存器,完成串口数据的接收和发送,HAL库也给我们提供了:HAL_UART_Receive和HAL_UART_Transmit两个函数用于串口数据的接收和发送。

       大家可以根据实际情况选择使用哪种方式来收发串口数据。


       29.3.2 程序流程图


图29.3.2.1 RS232实验程序流程图


       29.3.3 程序解析


       1. RS232驱动

       这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。RS232驱动相关源码包括两个文件:rs232.c和rs232.h。

       为方便修改,我们在rs232.h中使用宏定义232相关的控制引脚和串口编号,如果需要使用其它的引脚或者串口,修改宏和串口的定义即可,它们在rs232.h中定义,它们列出如下:

/* RS232 引脚 和 串口 定义 */
#define RS232_TX_GPIO_PORT          GPIOD
#define RS232_TX_GPIO_PIN            GPIO_PIN_5
#define RS232_TX_GPIO_CLK_ENABLE()  do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0)
 
#define RS232_RX_GPIO_PORT           GPIOD
#define RS232_RX_GPIO_PIN          GPIO_PIN_6
#define RS232_RX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0)
 
#define RS232_UX                       USART2
#define RS232_UX_IRQn                 USART2_IRQn
#define RS232_UX_IRQHandler         USART2_IRQHandler
#define RS232_UX_CLK_ENABLE()     do{ __HAL_RCC_USART2_CLK_ENABLE(); }while(0)

       1)rs232_init函数

       rs232_init的配置与串口类似,也需要设置波特率等参数,另外还需要配置收发模式的驱动引脚,我们的程序设计如下:

/**
 * @brief       RS232初始化函数
 * @note        该函数主要是初始化串口
 * @param       baudrate: 波特率, 根据自己需要设置波特率值
 * @retval      无
 */
void rs232_init(uint32_t baudrate)
{
 
    /* IO 及 时钟配置 */
    RS232_TX_GPIO_CLK_ENABLE();  /* 使能 串口TX脚 时钟 */
    RS232_RX_GPIO_CLK_ENABLE();  /* 使能 串口RX脚 时钟 */
    RS232_UX_CLK_ENABLE();   /* 使能 串口 时钟 */
 
    GPIO_InitTypeDef gpio_init_struct;
    
    gpio_init_struct.Pin = RS232_TX_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_AF_PP;
    gpio_init_struct.Pull = GPIO_PULLUP;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
    gpio_init_struct.Alternate = RS232_AF_UART2;   /* 复用为串口2 */
    HAL_GPIO_Init(RS232_TX_GPIO_PORT, &gpio_init_struct); /* 串口TX 脚 模式设置 */
 
    gpio_init_struct.Pin = RS232_RX_GPIO_PIN;
    gpio_init_struct.Alternate = RS232_AF_UART2;   /* 复用为串口2 */
    HAL_GPIO_Init(RS232_RX_GPIO_PORT, &gpio_init_struct); /* 串口RX 脚 */
 
    /* USART 初始化设置 */
    g_rs232_handler.Instance = RS232_UX;     /* 选择232对应的串口 */
    g_rs232_handler.Init.BaudRate = baudrate;    /* 波特率 */
    g_rs232_handler.Init.WordLength = UART_WORDLENGTH_8B; /* 字长为8位数据格式 */
    g_rs232_handler.Init.StopBits = UART_STOPBITS_1;  /* 一个停止位 */
    g_rs232_handler.Init.Parity = UART_PARITY_NONE;   /* 无奇偶校验位 */
    g_rs232_handler.Init.HwFlowCtl = UART_HWCONTROL_NONE; /* 无硬件流控 */
    g_rs232_handler.Init.Mode = UART_MODE_TX_RX;   /* 收发模式 */
    HAL_UART_Init(&g_rs232_handler);      /* 使能对应的串口 */
    __HAL_UART_DISABLE_IT(&g_rs232_handler, UART_IT_TC);
 
#if RS232_EN_RX            /* 如果使能了接收 */
    __HAL_UART_ENABLE_IT(&g_rs232_handler, UART_IT_RXNE); /* 开启接收中断 */
    HAL_NVIC_EnableIRQ(RS232_UX_IRQn);      /* 使能RS232中断 */
    HAL_NVIC_SetPriority(RS232_UX_IRQn, 3, 3);  /* 抢占优先级3, 子优先级3 */
#endif
}

       可以看到代码基本跟串口的配置一样,只是多了收发控制引脚的配置。

       2)发送函数

       发送函数用于输出232信号到232总线上,默认的232方式一般空闲时为接收状态,只有发送数据时我们才控制232芯片进入发送状态,发送完成后马上回到空闲接收状态,这样可以保证操作过程中232的数据丢失最小。我们实现的发送函数如下:

/**
 * @brief   RS232发送len个字节
 * @param   buf : 发送区首地址
 * @param   len : 发送字节数(为了和本代码接收匹配,这里不要超过RS232_REC_LEN个字节)
 * @retval   无
 */
void rs232_send_data(uint8_t *buf, uint8_t len)
{
    HAL_UART_Transmit(&g_rs232_handler, buf, len, 1000); /* RS232发送数据 */
    g_RS232_rx_cnt = 0;
}

       3)232接收中断函数

       RS232的接收与串口中断一样,不过要注意空闲时要切换回接收状态,否则会收不到数据。我们定义了一个全局的缓冲区g_RS232_rx_buf进行接收测试,通过串口中断接收数据,编写的接收代码如下:

uint8_t g_RS232_rx_buf[RS232_REC_LEN]; /* 接收缓冲, 最大 RS232_REC_LEN 个字节. */
uint8_t g_RS232_rx_cnt = 0;             /* 接收到的数据长度 */
 
void RS232_UX_IRQHandler(void)
{
    uint8_t res;
 
    if ((__HAL_UART_GET_FLAG(&g_RS232_handler, UART_FLAG_RXNE) != RESET))
    {   /* 接收到数据 */
        HAL_UART_Receive(&g_RS232_handler, &res, 1, 1000);
 
        if (g_RS232_rx_cnt 

       4)232查询接收数据函数

       该函数用于查询232总线上接收到的数据,主要实现的逻辑是:一开始进入函数时,先记录下当前接收计数器的值,再来一个延时去判断接收是否结束(即该期间有无接收到数据),假如说接收计数器的值没有改变,就证明接收结束,我们就可以把当前接收缓冲区传递出去。函数实现如下:

/**
 * @brief       RS232查询接收到的数据
 * @param       buf     : 接收缓冲区首地址
 * @param       len     : 接收到的数据长度
 *   @arg               0   , 表示没有接收到任何数据
 *   @arg               其他, 表示接收到的数据长度
 * @retval      无
 */
void rs232_receive_data(uint8_t *buf, uint8_t *len)
{
    uint8_t rxlen = g_RS232_rx_cnt;
    uint8_t i = 0;
    *len = 0;      /* 默认为0 */
    delay_ms(10);  /* 等待10ms,连续超过10ms没有接收到一个数据,则认为接收结束 */
 
    if (rxlen == g_RS232_rx_cnt && rxlen) /* 接收到了数据,且接收完成了 */
    {
        for (i = 0; i < rxlen; i++)
        {
            buf[i] = g_RS232_rx_buf[i];
        }
 
        *len = g_RS232_rx_cnt;  /* 记录本次数据长度 */
        g_RS232_rx_cnt = 0;     /* 清零 */
    }
}

       RS232的代码就讲到这里,基本是串口的知识,大家不明白的配置可以翻看之前串口章节的知识。

       2. main.c代码

       在main.c中编写如下代码:

int main(void)
{
    uint8_t key;
    uint8_t i = 0, t = 0;
    uint8_t cnt = 0;
    uint8_t rs232buf[5];
    
    HAL_Init();        /* 初始化HAL库 */
    sys_stm32_clock_init(85, 2, 2, 4, 8); /* 设置时钟,170Mhz */
    delay_init(170);       /* 延时初始化 */
    usart_init(115200);      /* 串口初始化为115200 */
    usmart_dev.init(170);     /* 初始化USMART */
    led_init();        /* 初始化LED */
    lcd_init();        /* 初始化LCD */
    key_init();        /* 初始化按键 */
    rs232_init(115200);      /* 初始化RS232 */
    
    /* 显示提示信息 */
    lcd_show_string(10, 10, 140, 32, 32, "STM32", RED);
    lcd_show_string(10, 42, 140, 24, 24, "RS232 TEST", RED);
    lcd_show_string(10, 66, 140, 24, 24, "ATOM@ALIENTEK", RED);
    lcd_show_string(10, 95, 200, 24, 24, "KEY0:Send", RED);    
 
    lcd_show_string(10, 130, 200, 16, 16, "Count:", RED);  /* 显示当前计数值 */
lcd_show_string(10, 150, 200, 16, 16, "Send Data:", RED); /* 提示发送的数据 */
/* 提示接收到的数据 */
    lcd_show_string(10, 190, 200, 16, 16, "Receive Data:", RED); 
 
    while (1)
    {
        key = key_scan(0);
        
        if (key == KEY0_PRES)    /* KEY0按下,发送一次数据 */
        {
            for (i = 0; i < 5; i++)
            {
                rs232buf[i] = cnt + i;  /* 填充发送缓冲区 */
  /* 显示数据 */
                lcd_show_xnum(10 + i * 32, 170, rs232buf[i], 3, 16, 0x80, BLUE); 
            }
 
            rs232_send_data(rs232buf, 5); /* 发送5个字节 */
        }
 
        rs232_receive_data(rs232buf, &key);
 
        if (key)        /* 接收到有数据 */
        {
            if (key > 5)
            {
                key = 5;      /* 最大是5个数据. */
            }
 
            for (i = 0; i < key; i++)
            {
  /* 显示数据 */
                lcd_show_xnum(10 + i * 32, 210, rs232buf[i], 3, 16, 0x80, BLUE); 
            }
        }
 
        t++;
        delay_ms(10);
 
        if (t == 20)
        {
            LED0_TOGGLE();    /* LED0闪烁, 提示系统正在运行 */
            t = 0;
 
            cnt++;
            lcd_show_xnum(10 + 48, 130, cnt, 3, 16, 0x80, BLUE);  /* 显示数据 */
        }
    }
}

       我们是通过按键控制数据的发送。在此部分代码中,cnt是一个累加数,一旦KEY0按下,就以这个数位基准连续发送5个数据。当232总线收到数据的时候,就将收到的数据直接显示在LCD屏幕上。


        29.4 下载验证

       在代码编译成功之后,将代码下载到电机开发板,程序运行效果如图29.4.1所示:


图29.4.1 程序运行效果图


       LED0不停闪烁,提示程序在运行。此时,按下KEY0,在XCOM上位机上面收到这个开发板发送的数据。如图29.4.2和图29.4.3所示:


图29.4.2 发送RS232数据的开发板界面


图29.4.3 接收RS232数据的串口调试助手界面


       图29.4.2,开发板向串口调试助手发送了5个数据(十进制),图29.4.3,XCOM上位机接收到了来自串口调试助手的5个数据(十六进制),XCOM上位机也可以发送5个数据(十六进制)到开发板,开发板上会显示接收到的5个数据(十进制),注意:如果使用RS232接收数据功能,必须使能RS232接收中断。

       本章介绍的232总线时通过串口控制收发的,只需要通过USB转232连接开发板和电脑就可以进行数据传输了。

       另外,利用USMART测试的部分,我们这里就不做介绍了,大家可自行验证下。


请使用浏览器的分享功能分享到微信等