赞
踩
相关代码:
- // SYN6288E使用到的串口
- #ifndef SYN6288E_USART
- #define SYN6288E_USART USART3
- #endif
-
- // SYN6288E使用到的串口时钟使能函数的名称
- #ifndef SYN6288E_USART_CLK_CMD
- #define SYN6288E_USART_CLK_CMD RCC_APB1PeriphClockCmd
- #endif
-
- // SYN6288E的串口时钟使能函数要传递的参数(即所在总线的对应的时钟)
- #ifndef SYN6288E_USART_CLK
- #define SYN6288E_USART_CLK RCC_APB1Periph_USART3
- #endif
-
- // SYN6288E的串口发送引脚的GPIO时钟使能函数的名称
- #ifndef SYN6288E_TX_CLK_CMD
- #define SYN6288E_TX_CLK_CMD RCC_APB2PeriphClockCmd
- #endif
-
- // SYN6288E的串口发送引脚的GPIO时钟使能函数要传递的参数(即所在总线的对应的时钟)
- #ifndef SYN6288E_TX_CLK
- #define SYN6288E_TX_CLK RCC_APB2Periph_GPIOB
- #endif
-
-
- // SYN6288E的串口接收引脚的GPIO时钟使能函数的名称
- #ifndef SYN6288E_RX_CLK_CMD
- #define SYN6288E_RX_CLK_CMD RCC_APB2PeriphClockCmd
- #endif
-
- // SYN6288E的串口接收引脚的GPIO时钟使能函数要传递的参数(即所在总线的对应的时钟)
- #ifndef SYN6288E_RX_CLK
- #define SYN6288E_RX_CLK RCC_APB2Periph_GPIOB
- #endif
-
- // SYN6288E的串口发送端所在的GPIO组
- #ifndef SYN6288E_TX_PORT
- #define SYN6288E_TX_PORT GPIOB
- #endif
-
- // SYN6288E的串口接收端所在的GPIO组
- #ifndef SYN6288E_RX_PORT
- #define SYN6288E_RX_PORT GPIOB
- #endif
-
- // SYN6288E的串口发送端的引脚号
- #ifndef SYN6288E_TX_PIN
- #define SYN6288E_TX_PIN GPIO_Pin_10
- #endif
-
- // SYN6288E的串口接收端的引脚号
- #ifndef SYN6288E_RX_PIN
- #define SYN6288E_RX_PIN GPIO_Pin_11
- #endif
-
-
- /**
- * @brief SYN6288E串口配置
- * 是个子函数,只在当前.c文件生效
- * @param BaudRate 波特率
- */
- static void __SYN6288E_USARTInit(uint32_t BaudRate)
- {
- GPIO_InitTypeDef GPIO_InitTypeStructure; // GPIO初始化要用到的结构体
- USART_InitTypeDef USART_InitTypeStructure; // 串口初始化要用到的结构体
-
- // 使能时钟
- SYN6288E_USART_CLK_CMD(SYN6288E_USART_CLK, ENABLE); // 使能串口3时钟
- SYN6288E_TX_CLK_CMD(SYN6288E_TX_CLK, ENABLE); // 使能SYN6288E发送端口所在的GPIO组的时钟
- SYN6288E_RX_CLK_CMD(SYN6288E_RX_CLK, ENABLE); // 使能SYN6288E接收端口所在的GPIO组的时钟
-
- // 初始化引脚(配置引脚)
- // 设置为复用推挽输出、给串口发送数据的引脚
- GPIO_InitTypeStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用功能,推挽输出
- GPIO_InitTypeStructure.GPIO_Pin = SYN6288E_TX_PIN;
- GPIO_InitTypeStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(SYN6288E_TX_PORT, &GPIO_InitTypeStructure); // GPIO初始化
-
- // 设置为浮空输入,接收数据的引脚
- GPIO_InitTypeStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
- GPIO_InitTypeStructure.GPIO_Pin = SYN6288E_RX_PIN;
- GPIO_Init(SYN6288E_RX_PORT, &GPIO_InitTypeStructure); // GPIO初始化
-
- // 配置串口3
- USART_InitTypeStructure.USART_Mode = USART_Mode_Tx; // 串口发送模式
- USART_InitTypeStructure.USART_Parity = USART_Parity_No; // 不使用奇偶校验
- USART_InitTypeStructure.USART_StopBits = USART_StopBits_1; // 1个停止位
- USART_InitTypeStructure.USART_BaudRate = BaudRate; // 波特率由函数传参传入
- USART_InitTypeStructure.USART_WordLength = USART_WordLength_8b; // 字长为8位
- USART_InitTypeStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 不使用硬件流控制
- USART_Init(SYN6288E_USART, &USART_InitTypeStructure);
-
- // 使能串口3
- USART_Cmd(SYN6288E_USART, ENABLE);
- }
-
配置好串口3之后,我们还需要验证这代码是否正确,可以使用USB转TTL模块来实现:
USB转TTL模块,里面一般是个CH340主控芯片,与STM32的串口相连后,可以通过PC端的串口调试助手查看STM32串口发送过来的信息。
注意:实际测试中发现,只连接USB转TTL模块的RXD和STM32的串口发送端,在PC的串口调试助手中看不到STM32发送过来的消息。还要将USB转TTL模块的GND与STM32所在的电路板的GND相连,才能在PC的串口调试助手上观察STM32发送的消息。
当SYN6288E工作时,它的忙线(busy)为高电平;
当SYN6288E为空闲状态时,它的忙线为低电平。
若SYN6288E还在工作时,给它发送数据,那么它会将新的数据覆盖旧的数据。
我们可以将GPIO设置为浮空输入模式来检测SYN6288E的忙线,判断它是否空闲,若SYN6288E正在工作,则要等待它变为空闲状态,再给它发送数据。
相关代码:
- // SYN6288E的忙线的时钟控制函数的名称
- #ifndef SYN6288E_BUSY_CLK_CMD
- #define SYN6288E_BUSY_CLK_CMD RCC_APB2PeriphClockCmd
- #endif
-
- // SYN6288E的忙线的时钟
- #ifndef SYN6288E_BUSY_CLK
- #define SYN6288E_BUSY_CLK RCC_APB2Periph_GPIOB
- #endif
-
- // SYN6288E的忙线所在的GPIO组
- #ifndef SYN6288E_BUSY_PORT
- #define SYN6288E_BUSY_PORT GPIOB
- #endif
-
- // SYN6288E的忙线所在的GPIO的引脚号
- #ifndef SYN6288E_BUSY_PIN
- #define SYN6288E_BUSY_PIN GPIO_Pin_1
- #endif
-
- /**
- * @brief SYN6288E忙线(BUSY)的相关引脚配置
- */
- static void __SYN6288EBUSY_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitTypeStructure; // GPIO初始化要用到的结构体
-
- SYN6288E_BUSY_CLK_CMD(SYN6288E_BUSY_CLK, ENABLE);
-
- GPIO_InitTypeStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
- GPIO_InitTypeStructure.GPIO_Pin = SYN6288E_BUSY_PIN;
- GPIO_Init(SYN6288E_BUSY_PORT, &GPIO_InitTypeStructure);
- }
-
SYN6288E一帧的数据由 帧头(1字节)+ 数据区长度(2字节)+ 命令字(1字节)+ 命令参数(1字节)+ 待发送文本(小于等于200字节) + 异或校验(1字节)组成。
SYN6288E一帧的数据最小单位由字节组成,我第一时间想到的是移位操作,但是一帧最大可以是206字节,因此定义一个数组(或者是申请一片连续的空间,需要会单片机的内存管理知识),再往里面填入数据,组成一帧的数据。
相关代码:
- /**
- * @brief 生成一帧SYN6288E能够识别的数据并发送
- *
- * @param str 待发送的文本
- * @param backgroundmusic 背景音乐
- */
- void SYN6288E_FrameInfo(char * str, uint8_t backgroundmusic)
- {
- // 定义存放一帧数据的容器
- uint8_t Frame[206] = {0};
- uint8_t len = strlen(str); // 存放待发送文本的长度
- uint8_t ecc = 0; // 存放异或校验的结果
-
- // 帧头,固定为0xFD
- Frame[0] = 0xFD;
- // 数据区长度
- Frame[1] = 0x00; // 数据区长度高位
- Frame[2] = len + 3; // 数据区长度底位(由待发送文本的长度+命令字+命令参数组成)
- // 命令字
- Frame[3] = 0x01; // 语音播报合成命令
- // 命令参数
- Frame[4] = backgroundmusic << 3 | 0x00; // 由背景音乐和编码格式组成,这里使用GB2312,编码格式填入0x00
-
- // 待发送文本内放入帧中
- uint8_t i;
- for (i = 0; i < len; i++)
- {
- Frame[i + 5] = str[i];
- ecc ^= str[i];
- }
-
- // 异或校验
- for (i = 0; i < 5; i++)
- {
- ecc ^= Frame[i];
- }
-
- // 将异或校验放入帧中
- Frame[5 + len] = ecc;
-
- // 发送数据
- for (i = 0; i < len + 6; i++)
- {
- // 判断发送数据寄存器是否清空
- while (USART_GetFlagStatus(SYN6288E_USART, USART_FLAG_TXE) == RESET);
- USART_SendData(SYN6288E_USART, Frame[i]);
- // 等待发送完成(等待发送移位寄存器清空)
- while (USART_GetFlagStatus(SYN6288E_USART, USART_FLAG_TC) == RESET);
- }
- }
如果串口还在发送数据,又有新的数据到来,就会发生冲突,覆盖了还没发送的数据。因为STM32的串口发送数据时,先将数据放入串口发送数据寄存器,串口发送数据寄存器有数据后,串口发送移位寄存器会将串口发送数据寄存器的数据搬运到发送移位寄存器,再进行数据发送。
所以在代码中,要发送数据前,先判断串口发送数据寄存器是否清空,发送数据后,要等待串口发送移位寄存器清空。
一般使用串口1,发送当前单片机的状态到PC,在PC的串口调试助手查看。因为C语言的习惯,我们更喜欢用printf来打印单片机的状态,使用printf前,要先重写fputc函数,该函数的重写例子可以参考ST官方提供的例程。
printf()函数会调用fputc()函数来发送信息,所以我们需要重写fputc()函数,让它不要向标准输出缓冲区发送数据,而是向串口1发送数据。
重写fputc后,在MDK的魔术棒->Target->勾选"use MicroLib"使用C语言的标准库:
调试相关的代码:
- // DEBUG使用到的串口
- #ifndef DEBUG_USART
- #define DEBUG_USART USART1
- #endif
-
- // DEBUG使用到的串口时钟使能函数的名称
- #ifndef DEBUG_USART_CLK_CMD
- #define DEBUG_USART_CLK_CMD RCC_APB2PeriphClockCmd
- #endif
-
- // DEBUG的串口时钟使能函数要传递的参数(即所在总线的对应的时钟)
- #ifndef DEBUG_USART_CLK
- #define DEBUG_USART_CLK RCC_APB2Periph_USART1
- #endif
-
- // DEBUG的串口发送引脚的GPIO时钟使能函数的名称
- #ifndef DEBUG_TX_CLK_CMD
- #define DEBUG_TX_CLK_CMD RCC_APB2PeriphClockCmd
- #endif
-
- // DEBUG的串口发送引脚的GPIO时钟使能函数要传递的参数(即所在总线的对应的时钟)
- #ifndef DEBUG_TX_CLK
- #define DEBUG_TX_CLK RCC_APB2Periph_GPIOA
- #endif
-
-
- // DEBUG的串口接收引脚的GPIO时钟使能函数的名称
- #ifndef DEBUG_RX_CLK_CMD
- #define DEBUG_RX_CLK_CMD RCC_APB2PeriphClockCmd
- #endif
-
- // DEBUG的串口接收引脚的GPIO时钟使能函数要传递的参数(即所在总线的对应的时钟)
- #ifndef DEBUG_RX_CLK
- #define DEBUG_RX_CLK RCC_APB2Periph_GPIOA
- #endif
-
- // DEBUG的串口发送端所在的GPIO组
- #ifndef DEBUG_TX_PORT
- #define DEBUG_TX_PORT GPIOA
- #endif
-
- // DEBUG的串口接收端所在的GPIO组
- #ifndef DEBUG_RX_PORT
- #define DEBUG_RX_PORT GPIOA
- #endif
-
- // DEBUG的串口发送端的引脚号
- #ifndef DEBUG_TX_PIN
- #define DEBUG_TX_PIN GPIO_Pin_9
- #endif
-
- // DEBUG的串口接收端的引脚号
- #ifndef DEBUG_RX_PIN
- #define DEBUG_RX_PIN GPIO_Pin_10
- #endif
-
- /**
- * @brief DEBUG初始化(使用串口作调试)
- */
- void DEBUG_Init(uint32_t BaudRate)
- {
- GPIO_InitTypeDef GPIO_InitTypeStructure; // GPIO初始化要用到的结构体
- USART_InitTypeDef USART_InitTypeStructure; // 串口初始化要用到的结构体
-
- // 使能时钟
- DEBUG_USART_CLK_CMD(DEBUG_USART_CLK, ENABLE); // 使能串口时钟
- DEBUG_TX_CLK_CMD(DEBUG_TX_CLK, ENABLE); // 使能DEBUG发送端口所在的GPIO组的时钟
- DEBUG_RX_CLK_CMD(DEBUG_RX_CLK, ENABLE); // 使能DEBUG接收端口所在的GPIO组的时钟
-
- // 初始化引脚(配置引脚)
- // 设置为复用推挽输出、给串口发送数据的引脚
- GPIO_InitTypeStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用功能,推挽输出
- GPIO_InitTypeStructure.GPIO_Pin = DEBUG_TX_PIN;
- GPIO_InitTypeStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(DEBUG_TX_PORT, &GPIO_InitTypeStructure); // GPIO初始化
-
- // 设置为浮空输入,接收数据的引脚
- GPIO_InitTypeStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
- GPIO_InitTypeStructure.GPIO_Pin = DEBUG_RX_PIN;
- GPIO_Init(DEBUG_RX_PORT, &GPIO_InitTypeStructure); // GPIO初始化
-
- // 配置串口
- USART_InitTypeStructure.USART_Mode = USART_Mode_Tx; // 串口发送模式
- USART_InitTypeStructure.USART_Parity = USART_Parity_No; // 不使用奇偶校验
- USART_InitTypeStructure.USART_StopBits = USART_StopBits_1; // 1个停止位
- USART_InitTypeStructure.USART_BaudRate = BaudRate; // 波特率由函数传参传入
- USART_InitTypeStructure.USART_WordLength = USART_WordLength_8b; // 字长为8位
- USART_InitTypeStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 不使用硬件流控制
- USART_Init(DEBUG_USART, &USART_InitTypeStructure);
-
- // 使能串口
- USART_Cmd(DEBUG_USART, ENABLE);
- }
-
- //
- int fputc(int ch, FILE *f)
- {
- /* Place your implementation of fputc here */
- /* e.g. write a character to the USART */
- USART_SendData(DEBUG_USART, (uint8_t) ch);
-
- /* Loop until the end of transmission */
- while (USART_GetFlagStatus(DEBUG_USART, USART_FLAG_TC) == RESET)
- {}
-
- return ch;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。