#include "stc.h"
#include "usb.h"
#include "uart.h"
#include <math.h>
/************* 功能说明 **************
程序编写: STC技术支持 梁工
用户请先别修改程序, 直接下载"Objects"里的"audio-play.hex"测试. 下载时选择主频30MHZ。
本例程使用STC32G12K128验证,MCU内部ROSC工作于30MHz,PLL到180MHz给PWM时钟,同时6分频得到30MHz给CPU做系统时钟。
使用2路HSPWM输出播放声音,由上位机CDC串口连续发送音频流。
两路HSPWM工作时钟为180MHz,PWM周期为4097,输出PWM频率大约为44KHz(与CD采样率44.1KHz相近),占空比为12位。
PWMA1P--P1.0 左声道
PWMA3P--P1.4 右声道
支持PCM、ADPCM、A-Law播放程序,上位机CDC串口下传音频流。
支持16位PCM格式, 单声道或双声道采样率最高支持44.1KHz。
支持IMA-ADPCM格式, 单通道或双通道采样率最高支持44.1KHz。
支持A-law(A率)格式,单通道或双通道采样率最高支持44.1KHz。
串口助手“每个数据包的大小”设置为1024,选择“自动连续发送”,“数据包之间的延时(ms)”根据播放的文件类型和采样率设置,或者均设置为0。
PCM 采样率 包间延时时间 数据率
44.1KHz 0ms 176.4KB/S
32KHz 1ms或2ms 128KB/S
16KHz 4ms或5ms 64KB/S
ADPCM 采样率 包间延时时间 数据率
44.1KHz 5ms 44.1KB/S
32KHz 10ms 32KB/S
16KHz 10ms 16KB/S
A law 采样率 包间延时时间 数据率
44.1KHz 2ms或3ms 88.2KB/S
32KHz 4ms或5ms 64KB/S
16KHz 10ms 32KB/S
输出PWM要经过低通滤波器获得光滑的模拟信号输出,请参考附图的电路:3阶巴特沃斯低通滤波器。
语音解码算法可能看似跟网上的有差异,但算法实际是一样的,我只是将其简化操作而已。
******************************************/
/************* 本地常量声明 **************/
/************* IO口定义 **************/
/************* 本地变量声明 **************/
#define VOICE_BUFF_LENTH 4096 //播放缓冲长度
u16 rd_index; //读缓冲索引
u16 wr_index; //写缓冲索引
u8 xdata voice_buff[VOICE_BUFF_LENTH]; //播放缓冲
u8 MusicType; //WAV类型, fmttag=0x01-->PCM, 0x02-->Windows ADPCM, 0x06-->A Law, 0x07-->Mu Law, 0x11-->IMA ADPCM
u8 MusicChannel; //音乐声道
u8 MusicBitNum; //采样长度
u32 MusicSampleRate; //采样率
u32 ByteLength; //数据长度(字节)
u32 ByteCnt; //播放字节计数
bit B_PlayEn; //允许播放
u16 left_pwm; //左声道PWM值
u16 right_pwm; //右声道PWM值
u8 law_data; //Alaw加码专用变量
//======= ADPCM解码专用变量 ==========
u8 edata ADPCM_Data_L[4]; //左声道局部缓冲
u8 edata ADPCM_Data_R[4]; //右声道局部缓冲
u8 decode; //编码值
long delta; //偏差
long cur_sample_L; //左声道解码值
long cur_sample_R; //右声道解码值
char index_L; //左声道解码索引
char index_R; //右声道解码索引
u16 dac_L, dac_R; //解码后输出的左后声道DAC值
u8 DecodeCnt; //解码计数
u16 MusicBlock; //BLOCK大小
bit B_high_nibble; //高半字节指示
//====================================
u8 PWMA_ISR_En; //每个通道可以单独允许中断处理, bit4:通道4, bit3:通道3, bit2:通道2, bit1:通道1.
u16 TX1_Limit; //接收数据限时
/************* 本地函数声明 **************/
u8 Timer0_Config(u8 t, u32 reload); //t=0: reload值是主时钟周期数, t=1: reload值是时间(单位us)
void delay_ms(u16 ms);
void PWMA_config(void);
void delay(void);
u8 ReadPWMA(u8 addr);
void WritePWMA(u8 addr, u8 dat);
/**************** 外部函数声明和外部变量声明 *****************/
/*********************** 主函数 ******************************/
void main(void)
{
u16 j;
WTST = 0;
CKCON = 0;
EAXFR = 1; //SFR enable, 允许访问扩展寄存器
P2M1 = 0x00;
P2M0 = 0x00;
P2 = 0xff; //一些IO用于测试指示
P5n_standard(0x10); //P5.4设置为准双向口, 输出MCU时钟分频, 用于监测MCU系统时钟.
MCLKOCR = 0x00 + 100; //主时钟输出控制寄存器, 0x00:从P5.4输出, 0x80:从P1.6输出. +0:不输出时钟, +1~127:输出1~127分频.
P3M0 &= ~0x03; //P3.0 P3.1设置为高阻输入
P3M1 |= 0x03;
IRC48MCR = 0x80; //开启USB时钟
while (!(IRC48MCR & 0x01)); //等待USB时钟稳定
uart_init(); //初始化USB-CDC串口
usb_init(); //初始化USB
EA = 1; //允许全局中断
delay_ms(1500); //延时一下
while(DeviceState != DEVSTATE_CONFIGURED) //等待设置完成
{
NOP(3);
}
left_pwm = 2048;
right_pwm = 2048;
PWMA_config(); // 初始化PWM
Timer0_Config(0, MAIN_Fosc / 16000); //设置采样率(中断频率)
B_PlayEn = 1;
while (1)
{
if (RxFlag) //当RxFlag为1时,表示已接收到CDC串口数据, 接收的数据大小保存在RxCount里面,每个包最多接收64字节, 数据保存在RxBuffer缓冲区.
{
if(RxCount == 14) //PC下传命令, 文件相关信息
{
B_PlayEn = 0; //禁止播放
MusicType = RxBuffer[1]; //WAV类型, fmttag=0x01-->PCM, 0x02-->Windows ADPCM, 0x06-->A Law, 0x07-->Mu Law, 0x11-->IMA ADPCM
MusicChannel = RxBuffer[2]; //声道数
MusicBitNum = RxBuffer[3]; //采样位数
MusicSampleRate = ((u32 *)&RxBuffer)[1]; //采样率, Buffer)[4]~Buffer)[7], 大端模式
ByteLength = ((u32 *)&RxBuffer)[2]; //数据字节长度, Buffer)[8]~Buffer)[11], 大端模式
MusicBlock = ((u16 *)&RxBuffer)[6]; //数据BLOCK长度(ADPCM使用), Buffer)[12]~Buffer)[13], 大端模式
MusicBlock--;
Timer0_Config(0, MAIN_Fosc / MusicSampleRate); //设置采样率(中断频率)
rd_index = 0; //读索引清0
wr_index = 0; //写索引清0
ByteCnt = 0; //播放字节计数清0
dac_L = 0; //初始DAC值
dac_R = 0; //初始DAC值
B_PlayEn = 1; //禁止播放
uart_recv_done(); //对接收的数据处理完成后,一定要调用一次这个函数,以便CDC接收下一笔串口数据
P21 = 0; //接收指示,用于测试
}
else if(RxCount == 64) //PC下传数据, 最多64字节一包
{
j = rd_index;
if(j != rd_index) j = rd_index; //避免读rd_index时,刚好中断改变
j = j - wr_index; //计算空闲缓冲字节数
j &= 0x0fff; //缓冲位4096字节
if((j > 64) || (j == 0)) //空出了超过64字节, 则开始接收一帧64字节数据
{
wr_index &= 0x0fc0; // 64字节,低6位为0
for(j=0; j<64; j++) voice_buff[wr_index++] = RxBuffer[j]; //接收到的64字节送播放缓冲
wr_index = wr_index & 0x0fff; //避免溢出
P20 = 0; //播放指示灯, 低亮
TX1_Limit = 4096;
P21 = ~P21; //接收指示灯取反,用于测试
uart_recv_done(); //对接收的数据处理完成后,一定要调用一次这个函数,以便CDC接收下一笔串口数据
}
}
else uart_recv_done(); //对接收的数据处理完成后,一定要调用一次这个函数,以便CDC接收下一笔串口数据
}
}
}
/**********************************************/
//========================================================================
// 函数: void delay_ms(u16 ms)
// 描述: 延时函数。
// 参数: ms,要延时的ms数, 1~65535ms. 自动适应主时钟.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-1
// 备注:
//========================================================================
void delay_ms(u16 ms)
{
u16 i;
do
{
i = MAIN_Fosc / 6000;
while(--i) ;
}while(--ms);
}
/*
======================================================= WAV数据格式 ==========================================================================
================ 单声道 =======================================
16位单声道PCM(小端模式,低字节在前):
双字0(4字节) | 双字1(4字节) | .......
采样1 | 采样2 | 采样3 | 采样4 | .......
字节0 字节1 | 字节2 字节3 | 字节0 字节1 | 字节2 字节3 | .......
低字节 高字节 | 低字节 高字节 | 低字节 高字节 | 低字节 高字节 | .......
================ 双声道 =======================================
16位双声道PCM(小端模式,低字节在前):
双字0(4字节) | 双字1(4字节) | .......
采样1 | 采样2 | .......
左声道 | 右声道 | 左声道 | 右声道 | .......
字节0 字节1 | 字节2 字节3 | 字节4 字节5 | 字节6 字节7 | .......
低字节 高字节 | 低字节 高字节 | 低字节 高字节 | 低字节 高字节 | .......
=================================================================================================================================================
======================================================= A-law数据格式 ==========================================================================
================ 单声道 =======================================
单声道A-law数据(小端模式,低字节在前):
双字0(4字节) | 双字1(4字节) | .......
字节0 字节1 字节2 字节3 | 字节4 字节5 字节6 字节7 | .......
采样0 采样1 采样2 采样3 | 采样4 采样5 采样6 采样7 | .......
================ 双声道 =======================================
双声道A-law数据(小端模式,低字节在前):
双字0(4字节) | 双字1(4字节) | .......
采样0 采样1 | 采样2 采样3 | .......
字节0 字节1 字节2 字节3 | 字节4 字节5 字节6 字节7 | .......
左声道 右声道 左声道 右声道| 左声道 右声道 左声道 右声道| .......
=================================================================================================================================================
======================================================= ADPCM数据格式 ==========================================================================
IMA-ADPCM(Adaptive Differential Pulse Code Modulation)
================ 单声道 =======================================
单声道ADPCM一个BLOCK(256或512字节)数据格式(小端模式,低字节在前):
双字0(4字节) | .......
字节0 字节1 | 字节2 | 字节3 | 字节4 | 字节5 |...| 字节511 |
采样0未压缩低字节 高字节 | 上一个BLOCK最后一个index | 保留 | 低4位采样1 高4位采样2 | 低4位采样3 高4位采样4 |...| 低4位采样1015 高4位采样1016 |
================ 双声道 =======================================
双声道ADPCM一个BLOCK(512或1024字节)数据格式(小端模式,低字节在前):
双字0(4字节)左声道 | 双字1(4字节)左声道 |
字节0 字节1 | 字节2 | 字节3 | 字节4 字节5 | 字节6 | 字节7 |
采样0未压缩低字节 高字节 | 上一个BLOCK最后一个index | 保留 | 采样0未压缩低字节 高字节 | 上一个BLOCK最后一个index | 保留 |
| 双字2(4字节)左声道 | 双字3(4字节)右声道 |...| 双字254 | 双字255 |
| B0~B3 B4~B7 B8~B11 B12~B15 B16~B19 B20~B23 B24~B27 B28~B31 | B0~B3 B4~B7 B8~B11 B12~B15 B16~B19 B20~B23 B24~B27 B28~B31
| 采样1 采样2 采样3 采样4 采样5 采样6 采样7 采样8 | 采样1 采样2 采样3 采样4 采样5 采样6 采样7 采样8 |...| 左声道 | 右声道 |
=================================================================================================================================================
*/
//**************** A率解压缩 *******************
// 将7bit A-law数据解压缩成11bit数据
//**************************************************
u16 const T_Alaw_decode[128]={
0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000A,0x000B,0x000C,0x000D,0x000E,0x000F,
0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001A,0x001B,0x001C,0x001D,0x001E,0x001F,
0x0020,0x0022,0x0024,0x0026,0x0028,0x002A,0x002C,0x002E,0x0030,0x0032,0x0034,0x0036,0x0038,0x003A,0x003C,0x003E,
0x0040,0x0044,0x0048,0x004C,0x0050,0x0054,0x0058,0x005C,0x0060,0x0064,0x0068,0x006C,0x0070,0x0074,0x0078,0x007C,
0x0080,0x0088,0x0090,0x0098,0x00A0,0x00A8,0x00B0,0x00B8,0x00C0,0x00C8,0x00D0,0x00D8,0x00E0,0x00E8,0x00F0,0x00F8,
0x0100,0x0110,0x0120,0x0130,0x0140,0x0150,0x0160,0x0170,0x0180,0x0190,0x01A0,0x01B0,0x01C0,0x01D0,0x01E0,0x01F0,
0x0200,0x0220,0x0240,0x0260,0x0280,0x02A0,0x02C0,0x02E0,0x0300,0x0320,0x0340,0x0360,0x0380,0x03A0,0x03C0,0x03E0,
0x0400,0x0440,0x0480,0x04C0,0x0500,0x0540,0x0580,0x05C0,0x0600,0x0640,0x0680,0x06C0,0x0700,0x0740,0x0780,0x07C0};
// IMA-ADPCM的表格数据
char idata index_adjust[16] = {-1,-1,-1,-1,2,4,6,8,-1,-1,-1,-1,2,4,6,8};
u16 const step_table[89] =
{
7,8,9,10,11,12,13,14,16,17,19,21,23,25,28,31,34,37,41,45,
50,55,60,66,73,80,88,97,107,118,130,143,157,173,190,209,230,253,279,307,337,371,
408,449,494,544,598,658,724,796,876,963,1060,1166,1282,1411,1552,1707,1878,2066,
2272,2499,2749,3024,3327,3660,4026,4428,4871,5358,5894,6484,7132,7845,8630,9493,
10442,11487,12635,13899,15289,16818,18500,20350,22385,24623,27086,29794,32767
};
//========================================================================
// 函数: void timer0_ISR (void) interrupt TIMER0_VECTOR
// 描述: timer0中断函数.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2018-12-20
//========================================================================
void timer0_ISR (void) interrupt TMR0_VECTOR
{
P27 = 1; //解码时间指示
if(B_PlayEn) //正在播放
{
if(ByteCnt >= ByteLength)
{
B_PlayEn = 0; //播放完成了
P20 = 1; //播放指示灯, 低亮
}
if(TX1_Limit != 0)
{
if(--TX1_Limit == 0) //超时无数据,重新请求发送
{
P20 = 1; //播放指示灯, 低亮
}
}
if(rd_index != wr_index) //数据流未溢出
{
WritePWMA((u8)&PWMA_CCR1H, (u8)(left_pwm/256)); // 比较值, 控制占空比(高电平时钟数)
WritePWMA((u8)&PWMA_CCR1L, (u8)(left_pwm%256));
WritePWMA((u8)&PWMA_CCR3H, (u8)(right_pwm/256)); // 比较值, 控制占空比(高电平时钟数)
WritePWMA((u8)&PWMA_CCR3L, (u8)(right_pwm%256));
if(MusicType == 1) //PCM, WAV类型, fmttag=0x01-->PCM, 0x02-->Windows ADPCM, 0x06-->A Law, 0x07-->Mu Law, 0x11-->IMA ADPCM
{
if(MusicChannel == 1) //单声道 5.5us
{
left_pwm = (u16)voice_buff[rd_index] + ((u16)voice_buff[rd_index+1]<<8); //左声道
rd_index += 2;
rd_index &= 0x0fff;
ByteCnt += 2; //每个采样2个字节
left_pwm += 32768; //有符号转位无符号
left_pwm /= 16; //转位12位PWM值
right_pwm = left_pwm; //单声道
}
else //双声道 6.5us
{
left_pwm = (u16)voice_buff[rd_index] + ((u16)voice_buff[rd_index+1]<<8); //左声道
rd_index += 2;
right_pwm = (u16)voice_buff[rd_index] + ((u16)voice_buff[rd_index+1]<<8); //左声道
rd_index += 2;
rd_index &= 0x0fff;
ByteCnt += 4; //每个采样4个字节
left_pwm += 32768; //有符号转位无符号
left_pwm /= 16; //转位12位PWM值
right_pwm += 32768; //有符号转位无符号
right_pwm /= 16; //转位12位PWM值
}
}
else if(MusicType == 6) //A-law, WAV类型, fmttag=0x01-->PCM, 0x02-->Windows ADPCM, 0x06-->A Law, 0x07-->Mu Law, 0x11-->IMA ADPCM
{
if(MusicChannel == 1) //单声道 5.5us
{
law_data = voice_buff[rd_index++] ^ 0x55; //Get Complement 偶数位取反, 从缓冲读数据字节,偶数位取反,循环队列,指向下一个数据
left_pwm = T_Alaw_decode[law_data & 0x7f]; // 解压缩码
if(law_data & 0x80) left_pwm = 2048 - left_pwm; //如果是负的
else left_pwm = 2048 + left_pwm; //如果是正的
right_pwm = left_pwm; //单声道
rd_index &= 0x0fff;
ByteCnt++; //一个采样1个字节
}
else //双声道 6.5us
{
law_data = voice_buff[rd_index++] ^ 0x55; //Get Complement 偶数位取反, 从缓冲读数据字节,偶数位取反,循环队列,指向下一个数据
left_pwm = T_Alaw_decode[law_data & 0x7f]; // 解压缩码
if(law_data & 0x80) left_pwm = 2048 - left_pwm; //如果是负的
else left_pwm = 2048 + left_pwm; //如果是正的
law_data = voice_buff[rd_index++] ^ 0x55; //Get Complement 偶数位取反, 从缓冲读数据字节,偶数位取反,循环队列,指向下一个数据
right_pwm = T_Alaw_decode[law_data & 0x7f]; // 解压缩码
if(law_data & 0x80) right_pwm = 2048 - right_pwm; //如果是负的
else right_pwm = 2048 + right_pwm; //如果是正的
rd_index &= 0x0fff;
ByteCnt += 2; //一个采样2个字节
}
}
else if(MusicType == 17) //ADPCM, WAV类型, fmttag=0x01-->PCM, 0x02-->Windows ADPCM, 0x06-->A Law, 0x07-->Mu Law, 0x11-->IMA ADPCM
{
if(MusicChannel == 1) //单声道 平均9us
{
if((rd_index & MusicBlock) == 0) //BLOCK开始, 256或512字节一个BLOAK
{
dac_L = (u16)voice_buff[rd_index] + ((u16)voice_buff[rd_index+1] << 8); // 未压缩的采样值
rd_index += 2;
index_L = voice_buff[rd_index++]; //index值
rd_index++; //空字节
ByteCnt += 4; //4个字节
B_high_nibble = 0;
dac_L += 32768; //转成线性
cur_sample_L = dac_L;
cur_sample_L -= 32768; //转回有符号
}
else
{
decode = voice_buff[rd_index]; //从缓冲读数据字节
if(B_high_nibble) //处理高半字节
{
ByteCnt++;
decode >>= 4;
rd_index++; //指向下一个数据
rd_index &= 0x0fff;
}
decode &= 0x0f;
delta = ((u32)step_table[index_L] * ((decode & 0x07)*2 +1)) / 8; // 计算delta
if(decode & 8 ) delta = -delta; //负的delta
cur_sample_L += delta; //计算出当前的波形数据
if(cur_sample_L >= 32768) dac_L = 65535;
else if(cur_sample_L < -32768) dac_L = 0;
else dac_L = (u16)(cur_sample_L + 32768);
index_L += index_adjust[decode];
if (index_L < 0) index_L = 0;
else if (index_L > 88) index_L = 88;
B_high_nibble = ~B_high_nibble;
}
dac_R = dac_L; //单声道
}
else //双声道 平均13us, 峰值15us
{
if((rd_index & MusicBlock) == 0) //BLOCK开始, 512或1024字节一个BLOAK
{
dac_L = (u16)voice_buff[rd_index] + ((u16)voice_buff[rd_index+1] << 8); // 未压缩的采样值
rd_index += 2;
index_L = voice_buff[rd_index]; //index值
rd_index += 2;
dac_R = (u16)voice_buff[rd_index] + ((u16)voice_buff[rd_index+1] << 8); // 未压缩的采样值
rd_index += 2;
index_R = voice_buff[rd_index]; //index值
rd_index += 2;
ByteCnt += 8; //8个字节
B_high_nibble = 0;
dac_L += 32768; //转成线性
cur_sample_L = dac_L;
cur_sample_L -= 32768; //转回有符号
dac_R += 32768; //转成线性
cur_sample_R = dac_R;
cur_sample_R -= 32768; //转回有符号
}
else
{
if((rd_index & 0x0007) == 0) //2个DWORD 8个字节
{
ADPCM_Data_L[0] = voice_buff[rd_index++]; //不用循坏,时间尽量短
ADPCM_Data_L[1] = voice_buff[rd_index++];
ADPCM_Data_L[2] = voice_buff[rd_index++];
ADPCM_Data_L[3] = voice_buff[rd_index++];
ADPCM_Data_R[0] = voice_buff[rd_index++]; //不用循坏,时间尽量短
ADPCM_Data_R[1] = voice_buff[rd_index++];
ADPCM_Data_R[2] = voice_buff[rd_index++];
ADPCM_Data_R[3] = voice_buff[rd_index];
B_high_nibble = 0;
DecodeCnt = 0;
}
//================ 左声道解压缩 ============================
decode = ADPCM_Data_L[DecodeCnt]; //从缓冲读数据字节
if(B_high_nibble) decode >>= 4; //处理高半字节
decode &= 0x0f;
delta = ((u32)step_table[index_L] * ((decode & 0x07)*2 +1)) / 8; // 计算delta
if(decode & 8 ) delta = -delta; //负的delta
cur_sample_L += delta; //计算出当前的波形数据
if(cur_sample_L >= 32768) dac_L = 65535;
else if(cur_sample_L < -32768) dac_L = 0;
else dac_L = (u16)(cur_sample_L + 32768);
index_L += index_adjust[decode];
if (index_L < 0) index_L = 0;
else if (index_L > 88) index_L = 88;
//======================== 左声道解压缩完毕 ==================================
//======================== 右声道解压缩 ============================
decode = ADPCM_Data_R[DecodeCnt]; //从缓冲读数据字节
if(B_high_nibble) //处理高半字节
{
if(DecodeCnt >= 3) //4个字节都处理完成
{
ByteCnt += 8; //4个字节
rd_index++; //指向下一个数据
rd_index &= 0x0fff;
}
DecodeCnt++;
decode >>= 4;
}
decode &= 0x0f;
delta = ((u32)step_table[index_R] * ((decode & 0x07)*2 +1)) / 8; // 计算delta
if(decode & 8 ) delta = -delta; //负的delta
cur_sample_R += delta; //计算出当前的波形数据
if(cur_sample_R >= 32768) dac_R = 65535;
else if(cur_sample_R < -32768) dac_R = 0;
else dac_R = (u16)(cur_sample_R + 32768);
index_R += index_adjust[decode];
if (index_R < 0) index_R = 0;
else if (index_R > 88) index_R = 88;
//======================== 右声道解压缩完毕 ==================================
B_high_nibble = ~B_high_nibble; //高半字节指示
}
}
left_pwm = dac_L/16; //转位12位PWM值
right_pwm = dac_R/16; //转位12位PWM值
}
}
}
P27 = 0;
}
//========================================================================
// 函数:u8 Timer0_Config(u8 t, u32 reload)
// 描述: timer0初始化函数.
// 参数: t: 重装值类型, 0表示重装的是系统时钟数, 其余值表示重装的是时间(us).
// reload: 重装值.
// 返回: 0: 初始化正确, 1: 重装值过大, 初始化错误.
// 版本: V1.0, 2018-12-20
//========================================================================
u8 Timer0_Config(u8 t, u32 reload) //t=0: reload值是主时钟周期数, t=1: reload值是时间(单位us)
{
TR0 = 0; //停止计数
if(t != 0) reload = (u32)(((float)MAIN_Fosc * (float)reload)/1000000UL); //重装的是时间(us), 计算所需要的系统时钟数.
if(reload >= (65536UL * 12)) return 1; //值过大, 返回错误
if(reload < 65536UL) AUXR |= 0x80; //1T mode
else
{
AUXR &= ~0x80; //12T mode
reload = reload / 12;
}
reload = 65536UL - reload;
TH0 = (u8)(reload >> 8);
TL0 = (u8)(reload);
ET0 = 1; //允许中断
PT0 = 1; //高优先级中断
TMOD = (TMOD & ~0x03) | 0; //工作模式, 0: 16位自动重装, 1: 16位定时/计数, 2: 8位自动重装, 3: 16位自动重装, 不可屏蔽中断
TR0 = 1; //开始运行
return 0;
}
void delay(void)
{
u16 i;
for (i=0; i<100; i++);
}
u8 ReadPWMA(u8 addr)
{
u8 dat;
while (HSPWMA_ADR & 0x80); //等待前一个异步读写完成
HSPWMA_ADR = addr | 0x80; //设置间接访问地址,只需要设置原XFR地址的低7位
//HSPWMA_ADDR寄存器的最高位写1,表示读数据
while (HSPWMA_ADR & 0x80); //等待当前异步读取完成
dat = HSPWMA_DAT; //读取异步数据
return dat;
}
void WritePWMA(u8 addr, u8 dat)
{
while (HSPWMA_ADR & 0x80); //等待前一个异步读写完成
HSPWMA_DAT = dat; //准备需要写入的数据
HSPWMA_ADR = addr & 0x7f; //设置间接访问地址,只需要设置原XFR地址的低7位
//HSPWMA_ADDR寄存器的最高位写0,表示写数据
}
//========================================================================
// 函数: void PWMA_config(void)
// 描述: PWM配置函数。
// 参数: noe.
// 返回: none.
// 版本: V1.0, 2022-3-15
// 备注:
//========================================================================
void PWMA_config(void)
{
u8 ccer1;
u8 ccer2;
u8 ps;
u8 eno;
P_SW2 |= 0x80; //SFR enable
CLKSEL |= 0x80; //内部PLL输出时钟选择, |=0x80: 选择PLL 144MHz, &=~0x80: 选择96MHz(默认)
USBCLK = (USBCLK &~0x60) | (1<<5); //选择PLL输入时钟分频,保证输入时钟为12M, 0: 1分频(对应12MHz), 1: 2分频(对应24MHz), 2: 4分频(对应48MHz), 3: 8分频(对应96MHz)
USBCLK |= 0x80; //PLL倍频控制, |=0x80: 使能PLL倍频. &= ~0x80: 禁止PLL倍频
delay(); //等待PLL锁频
CLKSEL |= 0x40; //高速IO时钟源选择, |=0x40: 选择PLLCLK, &=~0x40: 选择MCLK(默认)
HSCLKDIV = 0; //高速时钟分频器 0~255 (默认2)
CLKDIV = 6; //主时钟分频系数, 1~255, 144/5=28.8MHz
CLKSEL = (CLKSEL &~0x0c) | (1<<2); //主时钟源选择, 0: MCKSEL选择的时钟源(默认), 1: 内部PLL输出, 2: 内部PLL输出/2, 3: 内部48MHz高速IRC
HSPWMA_CFG = 0x04 + 0x02 + 0x01; // +0x04:使能异步模式下的PWM中断, +0x00:禁止中断. +0x02:使能异步控制模式, +0x00:禁止异步控制模式. +0x01:固定.
WritePWMA((u8)&PWMA_ENO, 0x00); // IO输出禁止
WritePWMA((u8)&PWMA_IER, 0x00); // 禁止中断
WritePWMA((u8)&PWMA_SR1, 0x00); // 清除状态
WritePWMA((u8)&PWMA_SR2, 0x00); // 清除状态
ccer1 = 0;
ccer2 = 0;
ps = 0;
eno = 0;
PWMA_ISR_En = 0;
//通过异步方式设置PWMA的相关寄存器
WritePWMA((u8)&PWMA_PSCRH, 0x00); // 预分频寄存器, 分频 Fck_cnt = Fck_psc/(PSCR[15:0}+1), 边沿对齐PWM频率 = SYSclk/((PSCR+1)*(AAR+1)), 中央对齐PWM频率 = SYSclk/((PSCR+1)*(AAR+1)*2).
WritePWMA((u8)&PWMA_PSCRL, 0x00);
WritePWMA((u8)&PWMA_DTR, 24); // 死区时间配置, n=0~127: DTR= n T, 0x80 ~(0x80+n), n=0~63: DTR=(64+n)*2T,
// 0xc0 ~(0xc0+n), n=0~31: DTR=(32+n)*8T, 0xE0 ~(0xE0+n), n=0~31: DTR=(32+n)*16T,
WritePWMA((u8)&PWMA_ARRH, 4096/256); // 自动重装载寄存器, 控制PWM周期
WritePWMA((u8)&PWMA_ARRL, 4096%256);
WritePWMA((u8)&PWMA_CCMR1, 0x68); // 通道模式配置, PWM模式1, 预装载允许
WritePWMA((u8)&PWMA_CCR1H, 2048/256); // 比较值, 控制占空比(高电平时钟数)
WritePWMA((u8)&PWMA_CCR1L, 2048%256);
ccer1 |= 0x05; // 开启比较输出, 高电平有效
ps |= 0; // 选择IO, 0:选择P1.0 P1.1, 1:选择P2.0 P2.1, 2:选择P6.0 P6.1,
eno |= 0x03; // IO输出允许, bit7: ENO4N, bit6: ENO4P, bit5: ENO3N, bit4: ENO3P, bit3: ENO2N, bit2: ENO2P, bit1: ENO1N, bit0: ENO1P
// PWMA_ISR_En |= 0x02; // 使能中断
/*
WritePWMA((u8)&PWMA_CCMR2, 0x68); // 通道模式配置, PWM模式1, 预装载允许
WritePWMA((u8)&PWMA_CCR2H, 800/256); // 比较值, 控制占空比(高电平时钟数)
WritePWMA((u8)&PWMA_CCR2L, 800%256);
ccer1 |= 0x50; // 开启比较输出, 高电平有效
ps |= (1<<2); // 选择IO, 0:选择P1.2 P1.3, 1:选择P2.2 P2.3, 2:选择P6.2 P6.3,
eno |= 0x0C; // IO输出允许, bit7: ENO4N, bit6: ENO4P, bit5: ENO3N, bit4: ENO3P, bit3: ENO2N, bit2: ENO2P, bit1: ENO1N, bit0: ENO1P
// PWMA_ISR_En |= 0x04; // 使能中断
*/
WritePWMA((u8)&PWMA_CCMR3, 0x68); // 通道模式配置, PWM模式1, 预装载允许
WritePWMA((u8)&PWMA_CCR3H, 2048/256); // 比较值, 控制占空比(高电平时钟数)
WritePWMA((u8)&PWMA_CCR3L, 2048%256);
ccer2 |= 0x05; // 开启比较输出, 高电平有效
ps |= (0<<4); // 选择IO, 0:选择P1.4 P1.5, 1:选择P2.4 P2.5, 2:选择P6.4 P6.5,
eno |= 0x30; // IO输出允许, bit7: ENO4N, bit6: ENO4P, bit5: ENO3N, bit4: ENO3P, bit3: ENO2N, bit2: ENO2P, bit1: ENO1N, bit0: ENO1P
// PWMA_ISR_En |= 0x08; // 使能中断
/*
WritePWMA((u8)&PWMA_CCMR4, 0x68); // 通道模式配置, PWM模式1, 预装载允许
WritePWMA((u8)&PWMA_CCR4H, 1600/256); // 比较值, 控制占空比(高电平时钟数)
WritePWMA((u8)&PWMA_CCR4L, 1600%256);
ccer2 |= 0x50; // 开启比较输出, 高电平有效
ps |= (1<<6); // 选择IO, 0:选择P1.6 P1.7, 1:选择P2.6 P2.7, 2:选择P6.6 P6.7, 3:选择P3.3 P3.4
eno |= 0xc0; // IO输出允许, bit7: ENO4N, bit6: ENO4P, bit5: ENO3N, bit4: ENO3P, bit3: ENO2N, bit2: ENO2P, bit1: ENO1N, bit0: ENO1P
// PWMA_ISR_En |= 0x10; // 使能中断
*/
// PWMA_ISR_En = 0x01; // 使能更新中断
WritePWMA((u8)&PWMA_CCER1, ccer1); // 捕获/比较使能寄存器1
WritePWMA((u8)&PWMA_CCER2, ccer2); // 捕获/比较使能寄存器2
PWMA_PS = ps; // 选择IO
WritePWMA((u8)&PWMA_IER, PWMA_ISR_En); // 设置允许通道1~4中断处理
WritePWMA((u8)&PWMA_BKR, 0x80); // 主输出使能 相当于总开关
WritePWMA((u8)&PWMA_CR1, 0x81); // 使能计数器, 允许自动重装载寄存器缓冲, 边沿对齐模式, 向上计数, bit7=1:写自动重装载寄存器缓冲(本周期不会被打扰), =0:直接写自动重装载寄存器本(周期可能会乱掉)
WritePWMA((u8)&PWMA_EGR, 0x01); // 产生一次更新事件, 清除计数器和预分频计数器, 装载预分频寄存器的值
WritePWMA((u8)&PWMA_ENO, eno); // 允许IO输出
}
// PWMA_PS = (0<<6)+(0<<4)+(0<<2)+0; //选择IO, 4项从高到低(从左到右)对应PWM1 PWM2 PWM3 PWM4, 0:选择P1.x, 1:选择P2.x, 2:选择P6.x,
// PWMA_PS PWM4N PWM4P PWM3N PWM3P PWM2N PWM2P PWM1N PWM1P
// 00 P1.7 P1.6 P1.5 P1.4 P1.3 P5.4 P1.1 P1.0
// 01 P2.7 P2.6 P2.5 P2.4 P2.3 P2.2 P2.1 P2.0
// 02 P6.7 P6.6 P6.5 P6.4 P6.3 P6.2 P6.1 P6.0
// 03 P3.3 P3.4 -- -- -- -- -- --




674

被折叠的 条评论
为什么被折叠?



