使用STC8H单脉冲模拟300-900ns协议遇到的...
本帖最后由 flyarm 于 2022-12-15 15:26 编辑项目需求:使用PWM功能实现,300-900ns的协议控制
硬件实现: MCU--stc8h1k08
需求分析:
根据时序要求 需要产生300ns 和 900ns的高脉冲;此类信号(红外遥控协议,WS2812等)一般精确模拟高脉冲即可,低脉冲要求不高;以24M时钟计算,1us就是需要24个脉冲,300ns需要(24*3/10)约7-8个脉冲;900ns需要(24*9/10)约21-22个脉冲; 程序中使用40个脉冲周期;
如果有PWM+DMA效果应该会更好,因为我用的芯片不支持DMA所以只能放弃;
代码,使用pwm的2P作为输出:
void PWMA_OUT_Configuration(void)
{
//使用PWM2P输出
P_SW2 |= 0x80; //使能XFR访问
//PWMA_Priority(0);
PWMA_CCER1 = 0x00;
PWMA_CCMR2 = 0x60; //设置2p PWM2 模式2 输出,启用预装载
PWMA_CCER1= 0x30; //使能2P通道,比较输出高 有效;
PWMA_SR1 = 0; //清标志位
PWMA_CNTR = 0; //清计数器
PWMA_ENO = 0x00;
PWMA_ENO = ENO2P; //使能输出 ENO4P|ENO3N||ENO1N
PWMA_PS = 0x00; //高级 PWM 通道输出脚选择位
PWMA_PS = PWM2_SW_P12_P13;
PWMA_ARR = 40;
PWMA_CCR2 = 30;
PWMA_BRK = 0x80; //使能主输出
PWMA_CR1 = 0x88; //开始计时单次脉冲
}
void OutPWM_byte(unsigned char Dout) //先发高位,把每一位转化成一个pwm脉冲输出
{
unsigned char i;
bit Dbit=0;
for(i=0;i<8;i++)
{
Dbit = (Dout>>(7-i))&0x01; //从高到低获取bit
if(Dbit) {PWMA_CCR2 = 18;} //40-22
else {PWMA_CCR2 = 33;} //40-7
PWMA_CR1 |= 0x01;
NOP1(); //每个脉冲的总周期逐渐变小,why?
}
NOP40();
}
主程序测试调用:
***********前面省略
PWMA_OUT_Configuration();
EA = 1;
while (1)
{
OutPWM_byte(0xF7);
delay_ms(5);
}
经测试,发现从第MSbit到LSBit,间隔周期逐渐变小,百思不得其解,不知道什么原因;等会补充一个测试图:
WS2812S的标准时序如下:
TH+TL = 1.25us±150ns, RES>50us
T0H = 0.25us±150ns = 0.10us - 0.40us
T0L = 1.00us±150ns = 0.85us - 1.15us
T1H = 1.00us±150ns = 0.85us - 1.15us
T1L = 0.25us±150ns = 0.10us - 0.40us
两个位数据之间的间隔要小于RES的50us.
使用PWM输出控制WS2812并不是好办法,但单脉冲输出不会有问题,先设置好单脉冲的参数,然后启动即可输出一个单脉冲。
控制WS2812最简单的是用IO操作,但会占用MCU时间,一个灯耗时30us,灯数不多的话,时间占比可以接受就可以了。比如擦欧洲哦100个灯要3ms,20ms刷新一次,时间占比15%,仍有85%的时间给别的任务。
建议使用SPI操作,有DMA更好。 飞翔:已请到梁工关注您这个贴,你带老兄弟们在这讨论,将程序放上来,好分析 理论上肯定是优选spi+dma
因为要在现有的pwm脚上实现 模拟协议;这样硬件上可以通用; 所以现在的方式就成了我的最优选择;
单个脉冲实现没问题;现在就是连续输出的情况下,总觉得和寄存器配置配合不起来;可能是因为执行时间问题,我继续摸索 本帖最后由 flyarm 于 2022-12-15 12:03 编辑
问题已解决,因为原发送代码 移位操作造成时间延时不同,所以周期有差异;
改成如下即可:
static void OutPWM_byte(unsigned char Dout) //先发高位,把每一位转化成一个pwm脉冲输出
{
unsigned char i;
unsigned char Dbit=0;
for(i=0;i<8;i++)
{
Dbit = Dout&0x80; //从高到低获取bit
if(Dbit) {PWMA_CCR2 = 18;}
else {PWMA_CCR2 = 33;}
PWMA_CR1 |= 0x01;
Dout = Dout<<1; //等待的时间里 每次移位一次
while(PWMA_CR1 &0x01);
}
}
稍晚改到2812灯,测试后上工程源码 感谢分享,都是爱的奉献 基于PWM单脉冲的ws2812实现源程序
用PWM输出,并且用while等待,不如直接用IO输出,反正都是在那里里等待。
//@24MHz
sbit DIN = P0^0; //任意IO, 注意要在程序开始初始化IO为推挽输出
void Send_1us(void)
{
NOP(16); // 6+16T@24MHz,实际14~40MHz均可驱动
}
void Send_color(u8 color) //发送一个字节颜色值
{
u8 i;
i = 8;
do
{
EA = 0; //发送时禁止中断
if((color & 0x80) != 0) //数据1
{
DIN = 1;
Send_1us();
DIN = 0; //1T
EA = 1; //1T 发送完一个位数据后允许中断, 但任何中断处理时间要小于RES的50us.
NOP(4); //4T @24MHz
}
else
{
DIN = 1;
NOP(5); // 6T @24MHz, 14~44MHz均可驱动
DIN = 0;
EA = 1; //发送完一个位数据后允许中断, 但任何中断处理时间要小于RES的50us.
Send_1us();
}
color <<= 1;
}
while(--i != 0);
}
void Send_GRB(u8 g, u8 r, u8 b)
{
Send_color(g);
Send_color(r);
Send_color(b);
}
本帖最后由 flyarm 于 2022-12-16 14:14 编辑
有道理单任务用io更好;可是我的应用还有各种任务 中断要考虑,还是用pwm 方便
有支持dma的PWM就更方便了,根本就不需要单脉冲;直接 DMA控制占空比发PWM就好了
flyarm 发表于 2022-12-15 11:44
问题已解决,因为原发送代码 移位操作造成时间延时不同,所以周期有差异;
改成如下即可:
while(PWMA_CR1&0X01)?为什么这样写?这样,意思是一个周期输出后,这个寄存器的值会自动的改变吗?我看手册 里面PWMA_CR1=0X01,这样是打开计数器,按照你这样写,难道计数器值等于预设值:PWMA_CCR2之后,这个计数器会自动关闭吗?
页:
[1]
2