本文章是使用stm32使用LL库实现中断一线制串口
首先看WT588F02BP-14S的通信协议
WT588F02BP-14S的通信是有点类似串口的,但是又不是标准串口不能使用硬件,官方的案例里都是使用堵塞去发送,堵塞整体代码,这里我是用中断去模拟发送
一线制串口发送
思路是去单独开一个较高优先级的中断,快进快出去按每个bit置为io口,尽量不堵塞主程序,发送完成后将中断断闭
代码如下
配置一个100ns的中断,将voice();放进去,要播报就voice_write(uint8_t data);
voice.h
#ifndef _VOICE_H_
#define _VOICE_H_
#include "main.h"
#define VOICE_START 500//最快能连续播放时间重复发送
#define VOICE_LEN 20//声音队列长度
#define HIGH_TICK 2000//紧急播放的间隔时间
#define VOICEL_GPIN GPIOC
#define VOICE_PIN LL_GPIO_PIN_3
#define VOICE_TIM TIM7 //定时器
//接口函数
uint8_t find_voice(uint8_t num);//判断队列是否有该语音,没有就装填并打开播放,重复填充无效,从尾部填充,适合在正常情况下播放
void voice_interrupt(uint8_t num);//紧急播报,会打断正在播放的语音,但不影响后面播报的语音
//测试函数
void voice_write(uint8_t data);//马上写一个字节,并可以马上播放音乐,可打断上面音乐队列
//调度函数
void voice(void);//放入100us的中断里面
void phonic_alarming(void);//放10ms中断,根据队列放声音
#endif
voice.c
volatile uint8_t voice_data,start_tick,stop_tick,bit,bit_tick=0;//这些变量描述在voice_write里
static uint8_t voice_queue[VOICE_LEN];//语音队列
static uint8_t voice_flag=0;//是否播放声音
static uint8_t queue_head=0;//队列头
static uint32_t tick_start=0;//等待播放
static uint32_t High_tick=0;//紧急标志位
/**
* @brief 播放音乐和设置,每次间隔必须在20ms以上,同时连续使用可能会打断上一条语音
* @param 0-223音乐列表,e0-ef设置声音大小
* @retval 无
*
* @example
voice_write(0);
*/
void voice_write(uint8_t data)
{
start_tick=25; //5ms低电平
voice_data=data;//数据
stop_tick=10; //2m高电平
bit=8;
//一个字节8位,用来判断第几位
bit_tick=0; //因为电平要分为2:6来区别高低电平,用来判断高低电平
LL_TIM_EnableCounter (VOICE_TIM); //使能计数
LL_TIM_EnableIT_UPDATE (VOICE_TIM); //使能更新中断
}
/**
* @brief 音乐调度, 必须放在一个100um的中断, 这里是借用定时器7, 这里面只有判断和拉高拉低电平
* @param 无
* @retval 无
* @example
-*/
void voice()
{
if (start_tick)//开始低电平5ms
{
LL_GPIO_ResetOutputPin(VOICEL_GPIN,VOICE_PIN);
start_tick--;
}
else if(bit>0)//一个字节8个bit,一个bit内要进中断8次
{
if(voice_data & 0X01)//高电平
{
if(bit_tick<6)
{
LL_GPIO_SetOutputPin(VOICEL_GPIN,VOICE_PIN);
}else
{
LL_GPIO_ResetOutputPin(VOICEL_GPIN,VOICE_PIN);
}
}
else//低电平
{
if(bit_tick<2)
{
LL_GPIO_SetOutputPin(VOICEL_GPIN,VOICE_PIN);
}
else
{
LL_GPIO_ResetOutputPin(VOICEL_GPIN,VOICE_PIN);
}
}
bit_tick++;
if(bit_tick>=8)//发送下一个bit
{
bit_tick=0;
bit--;
voice_data=voice_data>>1;
}
}
else if(stop_tick--)//结束高电平2ms
{
LL_GPIO_SetOutputPin(VOICEL_GPIN,VOICE_PIN);
}
else//关闭中断
{
LL_TIM_DisableCounter(VOICE_TIM);
LL_TIM_DisableIT_UPDATE(VOICE_TIM);
}
}
同时WT588F02BP-14S能检测是否在播报音乐,可添加一个IO检测和队列连续发送
本身芯片自身是可以连续发送的,但是他不能检测多条同样的语音连续发送,不方便就没用
队列发送语音
/**
* @brief 判断队列是否有该语音,没有就装填并打开播放,重复填充无效,从尾部填充,
* @param 语音地址
* @retval 返回0正常,返回1代表要么语音在队列了,要么队列满了
* @example
find_voice(0x1)
*/
uint8_t find_voice(uint8_t num)
{
for(int i=0;i<queue_head;i++)
{
if(voice_queue[i]==num)//判断队列是否有这条语音了
{
return 2;//存在语音
}
}
if(queue_head<VOICE_LEN)//没超过限幅的情况下存进去
{
voice_queue[queue_head++]=num;
voice_flag=1;//开启播报
return 0;
}
return 1;//超出队列
}
/**
* @brief 紧急播报,会打断正在播放的语音
* @param 语音地址
* @example
voice_interrupt(0x1)
*/
void voice_interrupt(uint8_t num)
{
if(uwTick-High_tick>HIGH_TICK)
{
voice_write(num);
tick_start=uwTick;
voice_flag=1;
High_tick=uwTick;
}
}
/**
* @brief 根据队列播放语音,放1ms或者空闲中断
* @param
* @retval
* @example
computer(0x1)
*/
void phonic_alarming()
{
//detection(VOICE_BUSY)==0是本人封装读io口的,你可自定义
if(uwTick-tick_start>=VOICE_START&&voice_flag==1&&detection(VOICE_BUSY)==0)//防止连续播报(距离上次发送一秒后&&队列有语音&&没语音在发声)
{
tick_start=uwTick;
if(queue_head!=0)//队列里面有语音
{
voice_write(voice_queue[0]);//播放第一条
for(int i=0;i<queue_head-1;i++)//对队列进行递推
{
voice_queue[i]=voice_queue[i+1];
}
queue_head--;
}
else//播放完毕
{
voice_flag=0;
}
}
}
一线制串口接收
本项目是没用,但提供个思路
可以使用上升或下降沿中断来启动定时器中断,中断每次进去采样多少次后关闭中断来实现软件串口,在波特率不高和项目不大的时候可以使用,或者面试回答问题