当前位置:   article > 正文

STM32智能语音学习笔记day03_syn6288e

syn6288e

1. 使用STM32C8T6的串口3并验证是否配置正确

1.1 配置串口3

        相关代码:

  1. // SYN6288E使用到的串口
  2. #ifndef SYN6288E_USART
  3. #define SYN6288E_USART USART3
  4. #endif
  5. // SYN6288E使用到的串口时钟使能函数的名称
  6. #ifndef SYN6288E_USART_CLK_CMD
  7. #define SYN6288E_USART_CLK_CMD RCC_APB1PeriphClockCmd
  8. #endif
  9. // SYN6288E的串口时钟使能函数要传递的参数(即所在总线的对应的时钟)
  10. #ifndef SYN6288E_USART_CLK
  11. #define SYN6288E_USART_CLK RCC_APB1Periph_USART3
  12. #endif
  13. // SYN6288E的串口发送引脚的GPIO时钟使能函数的名称
  14. #ifndef SYN6288E_TX_CLK_CMD
  15. #define SYN6288E_TX_CLK_CMD RCC_APB2PeriphClockCmd
  16. #endif
  17. // SYN6288E的串口发送引脚的GPIO时钟使能函数要传递的参数(即所在总线的对应的时钟)
  18. #ifndef SYN6288E_TX_CLK
  19. #define SYN6288E_TX_CLK RCC_APB2Periph_GPIOB
  20. #endif
  21. // SYN6288E的串口接收引脚的GPIO时钟使能函数的名称
  22. #ifndef SYN6288E_RX_CLK_CMD
  23. #define SYN6288E_RX_CLK_CMD RCC_APB2PeriphClockCmd
  24. #endif
  25. // SYN6288E的串口接收引脚的GPIO时钟使能函数要传递的参数(即所在总线的对应的时钟)
  26. #ifndef SYN6288E_RX_CLK
  27. #define SYN6288E_RX_CLK RCC_APB2Periph_GPIOB
  28. #endif
  29. // SYN6288E的串口发送端所在的GPIO组
  30. #ifndef SYN6288E_TX_PORT
  31. #define SYN6288E_TX_PORT GPIOB
  32. #endif
  33. // SYN6288E的串口接收端所在的GPIO组
  34. #ifndef SYN6288E_RX_PORT
  35. #define SYN6288E_RX_PORT GPIOB
  36. #endif
  37. // SYN6288E的串口发送端的引脚号
  38. #ifndef SYN6288E_TX_PIN
  39. #define SYN6288E_TX_PIN GPIO_Pin_10
  40. #endif
  41. // SYN6288E的串口接收端的引脚号
  42. #ifndef SYN6288E_RX_PIN
  43. #define SYN6288E_RX_PIN GPIO_Pin_11
  44. #endif
  45. /**
  46. * @brief SYN6288E串口配置
  47. * 是个子函数,只在当前.c文件生效
  48. * @param BaudRate 波特率
  49. */
  50. static void __SYN6288E_USARTInit(uint32_t BaudRate)
  51. {
  52. GPIO_InitTypeDef GPIO_InitTypeStructure; // GPIO初始化要用到的结构体
  53. USART_InitTypeDef USART_InitTypeStructure; // 串口初始化要用到的结构体
  54. // 使能时钟
  55. SYN6288E_USART_CLK_CMD(SYN6288E_USART_CLK, ENABLE); // 使能串口3时钟
  56. SYN6288E_TX_CLK_CMD(SYN6288E_TX_CLK, ENABLE); // 使能SYN6288E发送端口所在的GPIO组的时钟
  57. SYN6288E_RX_CLK_CMD(SYN6288E_RX_CLK, ENABLE); // 使能SYN6288E接收端口所在的GPIO组的时钟
  58. // 初始化引脚(配置引脚)
  59. // 设置为复用推挽输出、给串口发送数据的引脚
  60. GPIO_InitTypeStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用功能,推挽输出
  61. GPIO_InitTypeStructure.GPIO_Pin = SYN6288E_TX_PIN;
  62. GPIO_InitTypeStructure.GPIO_Speed = GPIO_Speed_50MHz;
  63. GPIO_Init(SYN6288E_TX_PORT, &GPIO_InitTypeStructure); // GPIO初始化
  64. // 设置为浮空输入,接收数据的引脚
  65. GPIO_InitTypeStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
  66. GPIO_InitTypeStructure.GPIO_Pin = SYN6288E_RX_PIN;
  67. GPIO_Init(SYN6288E_RX_PORT, &GPIO_InitTypeStructure); // GPIO初始化
  68. // 配置串口3
  69. USART_InitTypeStructure.USART_Mode = USART_Mode_Tx; // 串口发送模式
  70. USART_InitTypeStructure.USART_Parity = USART_Parity_No; // 不使用奇偶校验
  71. USART_InitTypeStructure.USART_StopBits = USART_StopBits_1; // 1个停止位
  72. USART_InitTypeStructure.USART_BaudRate = BaudRate; // 波特率由函数传参传入
  73. USART_InitTypeStructure.USART_WordLength = USART_WordLength_8b; // 字长为8位
  74. USART_InitTypeStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 不使用硬件流控制
  75. USART_Init(SYN6288E_USART, &USART_InitTypeStructure);
  76. // 使能串口3
  77. USART_Cmd(SYN6288E_USART, ENABLE);
  78. }

1.2 验证串口3是否配置正确        

配置好串口3之后,我们还需要验证这代码是否正确,可以使用USB转TTL模块来实现:

        USB转TTL模块,里面一般是个CH340主控芯片,与STM32的串口相连后,可以通过PC端的串口调试助手查看STM32串口发送过来的信息。

        注意:实际测试中发现,只连接USB转TTL模块的RXD和STM32的串口发送端,在PC的串口调试助手中看不到STM32发送过来的消息。还要将USB转TTL模块的GND与STM32所在的电路板的GND相连,才能在PC的串口调试助手上观察STM32发送的消息。

2. SYN6288E忙线的配置

        当SYN6288E工作时,它的忙线(busy)为高电平;

        当SYN6288E为空闲状态时,它的忙线为低电平。

        若SYN6288E还在工作时,给它发送数据,那么它会将新的数据覆盖旧的数据。

        我们可以将GPIO设置为浮空输入模式来检测SYN6288E的忙线,判断它是否空闲,若SYN6288E正在工作,则要等待它变为空闲状态,再给它发送数据。

        相关代码:

  1. // SYN6288E的忙线的时钟控制函数的名称
  2. #ifndef SYN6288E_BUSY_CLK_CMD
  3. #define SYN6288E_BUSY_CLK_CMD RCC_APB2PeriphClockCmd
  4. #endif
  5. // SYN6288E的忙线的时钟
  6. #ifndef SYN6288E_BUSY_CLK
  7. #define SYN6288E_BUSY_CLK RCC_APB2Periph_GPIOB
  8. #endif
  9. // SYN6288E的忙线所在的GPIO组
  10. #ifndef SYN6288E_BUSY_PORT
  11. #define SYN6288E_BUSY_PORT GPIOB
  12. #endif
  13. // SYN6288E的忙线所在的GPIO的引脚号
  14. #ifndef SYN6288E_BUSY_PIN
  15. #define SYN6288E_BUSY_PIN GPIO_Pin_1
  16. #endif
  17. /**
  18. * @brief SYN6288E忙线(BUSY)的相关引脚配置
  19. */
  20. static void __SYN6288EBUSY_Init(void)
  21. {
  22. GPIO_InitTypeDef GPIO_InitTypeStructure; // GPIO初始化要用到的结构体
  23. SYN6288E_BUSY_CLK_CMD(SYN6288E_BUSY_CLK, ENABLE);
  24. GPIO_InitTypeStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
  25. GPIO_InitTypeStructure.GPIO_Pin = SYN6288E_BUSY_PIN;
  26. GPIO_Init(SYN6288E_BUSY_PORT, &GPIO_InitTypeStructure);
  27. }

3. SYN6288E一帧的数据

        SYN6288E一帧的数据由 帧头(1字节)+ 数据区长度(2字节)+ 命令字(1字节)+ 命令参数(1字节)+ 待发送文本(小于等于200字节) + 异或校验(1字节)组成。

        SYN6288E一帧的数据最小单位由字节组成,我第一时间想到的是移位操作,但是一帧最大可以是206字节,因此定义一个数组(或者是申请一片连续的空间,需要会单片机的内存管理知识),再往里面填入数据,组成一帧的数据。

        相关代码:

  1. /**
  2. * @brief 生成一帧SYN6288E能够识别的数据并发送
  3. *
  4. * @param str 待发送的文本
  5. * @param backgroundmusic 背景音乐
  6. */
  7. void SYN6288E_FrameInfo(char * str, uint8_t backgroundmusic)
  8. {
  9. // 定义存放一帧数据的容器
  10. uint8_t Frame[206] = {0};
  11. uint8_t len = strlen(str); // 存放待发送文本的长度
  12. uint8_t ecc = 0; // 存放异或校验的结果
  13. // 帧头,固定为0xFD
  14. Frame[0] = 0xFD;
  15. // 数据区长度
  16. Frame[1] = 0x00; // 数据区长度高位
  17. Frame[2] = len + 3; // 数据区长度底位(由待发送文本的长度+命令字+命令参数组成)
  18. // 命令字
  19. Frame[3] = 0x01; // 语音播报合成命令
  20. // 命令参数
  21. Frame[4] = backgroundmusic << 3 | 0x00; // 由背景音乐和编码格式组成,这里使用GB2312,编码格式填入0x00
  22. // 待发送文本内放入帧中
  23. uint8_t i;
  24. for (i = 0; i < len; i++)
  25. {
  26. Frame[i + 5] = str[i];
  27. ecc ^= str[i];
  28. }
  29. // 异或校验
  30. for (i = 0; i < 5; i++)
  31. {
  32. ecc ^= Frame[i];
  33. }
  34. // 将异或校验放入帧中
  35. Frame[5 + len] = ecc;
  36. // 发送数据
  37. for (i = 0; i < len + 6; i++)
  38. {
  39. // 判断发送数据寄存器是否清空
  40. while (USART_GetFlagStatus(SYN6288E_USART, USART_FLAG_TXE) == RESET);
  41. USART_SendData(SYN6288E_USART, Frame[i]);
  42. // 等待发送完成(等待发送移位寄存器清空)
  43. while (USART_GetFlagStatus(SYN6288E_USART, USART_FLAG_TC) == RESET);
  44. }
  45. }

        

4. 通过串口发送数据会遇到的问题

        如果串口还在发送数据,又有新的数据到来,就会发生冲突,覆盖了还没发送的数据。因为STM32的串口发送数据时,先将数据放入串口发送数据寄存器,串口发送数据寄存器有数据后,串口发送移位寄存器会将串口发送数据寄存器的数据搬运到发送移位寄存器,再进行数据发送。

        所以在代码中,要发送数据前,先判断串口发送数据寄存器是否清空,发送数据后,要等待串口发送移位寄存器清空。

5. 代码调试

        一般使用串口1,发送当前单片机的状态到PC,在PC的串口调试助手查看。因为C语言的习惯,我们更喜欢用printf来打印单片机的状态,使用printf前,要先重写fputc函数,该函数的重写例子可以参考ST官方提供的例程。

        printf()函数会调用fputc()函数来发送信息,所以我们需要重写fputc()函数,让它不要向标准输出缓冲区发送数据,而是向串口1发送数据。

        重写fputc后,在MDK的魔术棒->Target->勾选"use MicroLib"使用C语言的标准库:

        调试相关的代码:

  1. // DEBUG使用到的串口
  2. #ifndef DEBUG_USART
  3. #define DEBUG_USART USART1
  4. #endif
  5. // DEBUG使用到的串口时钟使能函数的名称
  6. #ifndef DEBUG_USART_CLK_CMD
  7. #define DEBUG_USART_CLK_CMD RCC_APB2PeriphClockCmd
  8. #endif
  9. // DEBUG的串口时钟使能函数要传递的参数(即所在总线的对应的时钟)
  10. #ifndef DEBUG_USART_CLK
  11. #define DEBUG_USART_CLK RCC_APB2Periph_USART1
  12. #endif
  13. // DEBUG的串口发送引脚的GPIO时钟使能函数的名称
  14. #ifndef DEBUG_TX_CLK_CMD
  15. #define DEBUG_TX_CLK_CMD RCC_APB2PeriphClockCmd
  16. #endif
  17. // DEBUG的串口发送引脚的GPIO时钟使能函数要传递的参数(即所在总线的对应的时钟)
  18. #ifndef DEBUG_TX_CLK
  19. #define DEBUG_TX_CLK RCC_APB2Periph_GPIOA
  20. #endif
  21. // DEBUG的串口接收引脚的GPIO时钟使能函数的名称
  22. #ifndef DEBUG_RX_CLK_CMD
  23. #define DEBUG_RX_CLK_CMD RCC_APB2PeriphClockCmd
  24. #endif
  25. // DEBUG的串口接收引脚的GPIO时钟使能函数要传递的参数(即所在总线的对应的时钟)
  26. #ifndef DEBUG_RX_CLK
  27. #define DEBUG_RX_CLK RCC_APB2Periph_GPIOA
  28. #endif
  29. // DEBUG的串口发送端所在的GPIO组
  30. #ifndef DEBUG_TX_PORT
  31. #define DEBUG_TX_PORT GPIOA
  32. #endif
  33. // DEBUG的串口接收端所在的GPIO组
  34. #ifndef DEBUG_RX_PORT
  35. #define DEBUG_RX_PORT GPIOA
  36. #endif
  37. // DEBUG的串口发送端的引脚号
  38. #ifndef DEBUG_TX_PIN
  39. #define DEBUG_TX_PIN GPIO_Pin_9
  40. #endif
  41. // DEBUG的串口接收端的引脚号
  42. #ifndef DEBUG_RX_PIN
  43. #define DEBUG_RX_PIN GPIO_Pin_10
  44. #endif
  45. /**
  46. * @brief DEBUG初始化(使用串口作调试)
  47. */
  48. void DEBUG_Init(uint32_t BaudRate)
  49. {
  50. GPIO_InitTypeDef GPIO_InitTypeStructure; // GPIO初始化要用到的结构体
  51. USART_InitTypeDef USART_InitTypeStructure; // 串口初始化要用到的结构体
  52. // 使能时钟
  53. DEBUG_USART_CLK_CMD(DEBUG_USART_CLK, ENABLE); // 使能串口时钟
  54. DEBUG_TX_CLK_CMD(DEBUG_TX_CLK, ENABLE); // 使能DEBUG发送端口所在的GPIO组的时钟
  55. DEBUG_RX_CLK_CMD(DEBUG_RX_CLK, ENABLE); // 使能DEBUG接收端口所在的GPIO组的时钟
  56. // 初始化引脚(配置引脚)
  57. // 设置为复用推挽输出、给串口发送数据的引脚
  58. GPIO_InitTypeStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用功能,推挽输出
  59. GPIO_InitTypeStructure.GPIO_Pin = DEBUG_TX_PIN;
  60. GPIO_InitTypeStructure.GPIO_Speed = GPIO_Speed_50MHz;
  61. GPIO_Init(DEBUG_TX_PORT, &GPIO_InitTypeStructure); // GPIO初始化
  62. // 设置为浮空输入,接收数据的引脚
  63. GPIO_InitTypeStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
  64. GPIO_InitTypeStructure.GPIO_Pin = DEBUG_RX_PIN;
  65. GPIO_Init(DEBUG_RX_PORT, &GPIO_InitTypeStructure); // GPIO初始化
  66. // 配置串口
  67. USART_InitTypeStructure.USART_Mode = USART_Mode_Tx; // 串口发送模式
  68. USART_InitTypeStructure.USART_Parity = USART_Parity_No; // 不使用奇偶校验
  69. USART_InitTypeStructure.USART_StopBits = USART_StopBits_1; // 1个停止位
  70. USART_InitTypeStructure.USART_BaudRate = BaudRate; // 波特率由函数传参传入
  71. USART_InitTypeStructure.USART_WordLength = USART_WordLength_8b; // 字长为8位
  72. USART_InitTypeStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 不使用硬件流控制
  73. USART_Init(DEBUG_USART, &USART_InitTypeStructure);
  74. // 使能串口
  75. USART_Cmd(DEBUG_USART, ENABLE);
  76. }
  77. //
  78. int fputc(int ch, FILE *f)
  79. {
  80. /* Place your implementation of fputc here */
  81. /* e.g. write a character to the USART */
  82. USART_SendData(DEBUG_USART, (uint8_t) ch);
  83. /* Loop until the end of transmission */
  84. while (USART_GetFlagStatus(DEBUG_USART, USART_FLAG_TC) == RESET)
  85. {}
  86. return ch;
  87. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/524718
推荐阅读
相关标签
  

闽ICP备14008679号