STM32智能语音学习笔记:第二天
目录
1. 点亮LED
1.1 LED.c的代码:(使用了条件编译,方便做移植操作,万一需要修改引脚,通过条件编译和自定义的宏就可以很快的做修改)
1.2 LED.h的代码:
1.3 my_config.h内的代码:(这个文件用来配置端口相关的时钟的引脚,后续添加的硬件也会在这里配置,有利于移植)
2. 编写延时函数
2.1 使用CPU来实现延时
2.2 使用滴答定时器来实现延时(简单略过)
3. 认识语音播报芯片
1. 点亮LED
点亮LED,GPIO口使用通用、推挽输出。编写代码时,推荐先将整体思路用注释的方式写下。
1.1 LED.c的代码:(使用了条件编译,方便做移植操作,万一需要修改引脚,通过条件编译和自定义的宏就可以很快的做修改)
#include "led.h"
#include "my_config.h"
#ifndef LED2_GPIO_CLOCK
#define LED2_GPIO_CLOCK RCC_APB2Periph_GPIOC // LED灯(绿灯)对应的GPIO组的时钟
#endif
#ifndef LED2_GPIO_PORT
#define LED2_GPIO_PORT GPIOC // LED所在的GPIO组
#endif
#ifndef LED2_PIN
#define LED2_PIN GPIO_Pin_13 // LED所在的GPIO组上的具体引脚
#endif
#ifndef LED2_ON
#define LED2_ON() GPIO_ResetBits(LED2_GPIO_PORT, LED2_PIN)
#endif
#ifndef LED2_OFF
#define LED2_OFF() GPIO_SetBits(LED2_GPIO_PORT, LED2_PIN)
#endif
/**
* @brief LED初始化函数
*/
void LED2_Init(void)
{
GPIO_InitTypeDef GPIO_InitTypeStructure; // GPIO初始化要用到的结构体
RCC_APB2PeriphClockCmd(LED2_GPIO_CLOCK, ENABLE); // 使能GPIOC 的时钟
GPIO_InitTypeStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitTypeStructure.GPIO_Pin = LED2_PIN;
GPIO_InitTypeStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(LED2_GPIO_PORT, &GPIO_InitTypeStructure); // 初始化GPIO
// 先熄灭LED灯
LED2_OFF();
}
1.2 LED.h的代码:
#ifndef __LED_H
#define __LED_H
#include "stm32f10x.h"
void LED2_Init(void); // LED初始化
#endif
1.3 my_config.h内的代码:(这个文件用来配置端口相关的时钟的引脚,后续添加的硬件也会在这里配置,有利于移植)
#ifndef __MY_CONFIG_H
#define __MY_CONFIG_H
// ==========================================================================
// LED端口相关配置
#define LED2_GPIO_CLOCK RCC_APB2Periph_GPIOC // LED灯(绿灯)对应的GPIO组的时钟
#define LED2_GPIO_PORT GPIOC // LED所在的GPIO组
#define LED2_PIN GPIO_Pin_13 // LED所在的GPIO组上的具体引脚
#define LED2_ON() GPIO_ResetBits(LED2_GPIO_PORT, LED2_PIN) // 点亮LED2
#define LED2_OFF() GPIO_SetBits(LED2_GPIO_PORT, LED2_PIN) // 熄灭LED2
// ==========================================================================
#endif
以上代码,使用了大量的宏,需要一定的C语言基础来阅读。
2. 编写延时函数
可以使用两种方式实现延时:消耗CPU 和 使用定时器(滴答定时器)。
2.1 使用CPU来实现延时
CPU是根据节拍来执行代码的,而 __nop()函数 刚好占用一次节拍,经过实验/实践测得,(在STM32F103中)for循环占用5个节拍。只要知道了时钟频率,就可以使用循环和__nop()函数来实现延时。
通过查阅相关资料,STM32F103C8T6的时钟最大为72MHz,1s有72 * 10的六次方次节拍。
也就是 1000ms 有 72 * 10 00 0000次节拍;
1000 000 us 有 72 * 10 00 0000次节拍;
化简之后,1us刚好对应72次节拍。
那么可以在代码中使用for循环+(72-5)个__nop()函数来实现微妙级的延时。
相关代码:
/**
* @brief 使用消耗CPU节拍的方式实现微秒级延时
*
* @param nTimes 延时时间,单位:微秒
*/
void simple_delay_us(uint32_t nTimes)
{
for (int i = 0; i < nTimes; i++)
{
__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
__nop();__nop();__nop();__nop();__nop();__nop();__nop();
}
}
/**
* @brief 使用消耗CPU节拍的方式实现毫秒级延时
*
* @param nTimes 延时时间,单位:毫秒
*/
void simple_delay_ms(uint32_t nTimes)
{
while(nTimes--)
{
simple_delay_us(1000);
}
}
/**
* @brief 使用消耗CPU节拍的方式实现秒级延时
*
* @param nTimes 延时时间,单位:秒
*/
void simple_delay_s(uint32_t nTimes)
{
while(nTimes--)
{
simple_delay_ms(1000);
}
}
可以通过MDK内的仿真来观察延时是否准确:
先设置STM32使用的外部晶振:8MHz。(不然后续的软件仿真结果会不同)
点击魔术棒,在Debug栏下,选择"Use Simulator",使用软件仿真。接着在"Dialog DLL"填入"DARMSTM DLL","Parameter"填入"-pSTM32F103C8"a。
在MDK菜单栏点击开始调试按钮(start debug)->添加逻辑分析仪(Logic Analyzer)->在新的窗口中点击setup->新建->填入"PORTC.13"。
接着,在"Display Type"中选择"Bit":
设置完成后,点击运行(RUN)按钮:
当它出现了数个波形后,便可以点击停止按钮,观察相邻的两个边沿的时间间隔是否在0.5s范围内:
对比后发现,相邻的两个边沿在0.5s左右,有微小的误差,可以忽略不计。
2.2 使用滴答定时器来实现延时(简单略过)
可以直接使用正点原子的延时代码,也可以看着CM3/CM4权威指南来一步步配置滴答定时器的相关寄存器来实现延时。
这里我是直接使用正点原子的代码,省略详细的操作。
通过软件仿真发现,使用滴答定时器的延时比使用CPU延时的精度会高一些(0.0001us左右)。
3. 认识语音播报芯片
本项目使用SYN6288芯片作为语音播报的主控芯片,以下是它对应的语音播报模块图片:
SYN6288,也有的型号是SYN6288E,这两种型号的芯片使用方式一致,不影响代码的编写。
在本次项目中,我们使用3.3V电压给它供电。
该芯片使用串口来(异步)通信。在硬件连接上,它与STM32C8T6的串口3(USART3)相连。