【踩坑记录】STC8H8K64U硬件PWM使用小结

news2024/11/27 12:36:21

快速导航

  • 写在前面
  • 配置时钟
  • 配置GPIO
  • 配置定时器
  • 配置串口
  • 配置硬件PWM
  • 特殊功能
    • 同步功能
  • 总结

写在前面

不出意外这是我第一次也是最后一次使用STC的芯片,写这篇博的目的纯粹记录下前段时间调试的痛苦经历,所有目前打算选或是已经开始调试这款芯片的朋友,建议你们看完本文之后深思熟虑一下。用M0或者M3不香么?

这篇博客主要是用到硬件PWM,实现的是6路PWM同步输出的功能。

使用过程遇到问题,可以感受一下STC原厂的态度,向原厂指出他们的错误,原厂不仅不核查,还会嘲讽你菜鸟(贴个截图给大家感受一下 这是STC原厂庄伟)
在这里插入图片描述

首先不得不说STC弄的库,可谓是四不像,想学arm内核那样用结构体来初始化外设功能,但是官方demo只有最简单的应用,高级功能的库函数是缺失的,自己想要用高级功能,那就自己摸索把,库里面和demo是找不到相应的函数来调用的和参考的。
比如我这里用到STC的硬件PWM,库函数里面就只有两个可以调用的函数:

u8	PWM_Configuration(u8 PWM, PWMx_InitDefine *PWMx);
void UpdatePwm(u8 PWM, PWMx_Duty *PWMx);

一个是初始化函数,另一个是更新占空比的函数。说真的,我没见过这么简陋的库。

贴个新唐的MS51对比一下:

void PWM0_ClockSource(unsigned char u8PWMCLKSource, unsigned char u8PWM0CLKDIV);
void PWM0_ConfigOutputChannel(unsigned char u8PWM0ChannelNum,
                              unsigned char u8PWM0OPMode,
                              unsigned char u8PWM0PwmType,
                              unsigned long u32PWM0Frequency,
                              unsigned int u16PWM0DutyCycle);
void PWM0_DeadZoneEnable(unsigned char u8PWM0Pair, unsigned int u16PWM0DZValue);
void PWM0_DeadZone_ALL_Disable(void);
void PWM0_RUN(void);
void PWM0_STOP(void);

void PWM1_ClockSource(unsigned char u8PWMCLKSource, unsigned char u8PWM0CLKDIV);
void PWM1_ConfigOutputChannel(unsigned char u8PWM1ChannelNum,
                              unsigned char u8PWM1OPMode,
                              unsigned char u8PWM1PwmType,
                              unsigned long u32PWM1Frequency,
                              unsigned int u16PWM1DutyCycle);
void PWM1_DeadZoneEnable(unsigned char u8PWM0Pair, unsigned int u16PWM0DZValue);
void PWM1_DeadZone_ALL_Disable(void);
void PWM1_RUN(void);
void PWM1_STOP(void);

STC你真就是连个暂停和开启的子函数都懒得写么,我真的迷惑。

回到正题,这个库你可以接受,那么开始第一步

配置时钟

如果使用了外部时钟,那么这一步你需要关注一下

void main(void)
{
    P_SW2 = 0x80;
    XOSCCR = 0xc0;                              //启动外部晶振
    while (!(XOSCCR & 1));                      //等待时钟稳定
    CLKDIV = 0x00;                              //时钟不分频
    CLKSEL = 0x01;                              //选择外部晶振
    P_SW2 = 0x00;
}

这几句是初始化了外部晶振。

配置GPIO

调库: GPIO_config();

void	GPIO_config(void)
{
	GPIO_InitTypeDef	GPIO_InitStructure;					//结构定义			EN脚

	GPIO_InitStructure.Pin  = GPIO_Pin_1 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;	//135 PWM 1N 2N 3N   P14 SD为1的时候就不出波
	GPIO_InitStructure.Mode = GPIO_OUT_PP;	
	GPIO_Inilize(GPIO_P1,&GPIO_InitStructure);

	GPIO_InitStructure.Pin  = GPIO_Pin_0;					//P10 EN输入	相当于波形的总开关		
	GPIO_InitStructure.Mode = GPIO_PullUp;	
	GPIO_Inilize(GPIO_P1,&GPIO_InitStructure);

	GPIO_InitStructure.Pin  = GPIO_Pin_3 | GPIO_Pin_4;		//P33 P34 PWM 7_2 8_2
	GPIO_InitStructure.Mode = GPIO_OUT_PP;	
	GPIO_Inilize(GPIO_P3,&GPIO_InitStructure);

	GPIO_InitStructure.Pin  = GPIO_Pin_6 | GPIO_Pin_7;		//UART 配置为双向口   P36 P37 与MS51 UART通信脚
	GPIO_InitStructure.Mode = GPIO_PullUp;	
	GPIO_Inilize(GPIO_P3,&GPIO_InitStructure);
	
	GPIO_InitStructure.Pin  = GPIO_Pin_4;					//P54 PWM 6_2
	GPIO_InitStructure.Mode = GPIO_OUT_PP;	
	GPIO_Inilize(GPIO_P5,&GPIO_InitStructure);
}

结构体里面元素的初值,在GPIO.h文件中都有参考值。
我这里就是初始化了PWM1/2/3/6/7/8这六路的GPIO为输出,然后与主控的串口GPIO,还有输入信号EN和对74HC244芯片的控制脚SD

配置定时器

调库:Timer_config();

void	Timer_config(void)
{
	TIM_InitTypeDef		TIM_InitStructure;						//结构定义
	TIM_InitStructure.TIM_Mode      = TIM_16BitAutoReload;			//指定工作模式,   
	TIM_InitStructure.TIM_Priority  = Priority_0;			//指定中断优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
	TIM_InitStructure.TIM_Interrupt = ENABLE;					//中断是否允许,   ENABLE或DISABLE
	TIM_InitStructure.TIM_ClkSource = TIM_CLOCK_1T;			//指定时钟源,     TIM_CLOCK_1T,TIM_CLOCK_12T,TIM_CLOCK_Ext
	TIM_InitStructure.TIM_ClkOut    = DISABLE;				//是否输出高速脉冲, ENABLE或DISABLE
	TIM_InitStructure.TIM_Value     = 65536UL - (MAIN_Fosc / 1000);		//初值,1ms
	TIM_InitStructure.TIM_Run       = ENABLE;					//是否初始化后启动定时器, ENABLE或DISABLE
	Timer_Inilize(Timer0,&TIM_InitStructure);					//初始化Timer0	  Timer0,Timer1,Timer2,Timer3,Timer4
}

这个初值 MAIN_Focs就是你的实际晶振,比如32Mhz的 这个就定义为32000000L,如果是用的内部RC震荡,那么库函数里面config.h文件要注意改一下:

#ifndef		__CONFIG_H
#define		__CONFIG_H

/*********************************************************/
#define MAIN_Fosc       32000000L       //定义主时钟
// #define MAIN_Fosc		22118400L	//定义主时钟
//#define MAIN_Fosc		12000000L	//定义主时钟
//#define MAIN_Fosc		11059200L	//定义主时钟
//#define MAIN_Fosc		 5529600L	//定义主时钟
// #define MAIN_Fosc		24000000L	//定义主时钟


#define STC8Hxx		//STC8H系列
//#define STC8Gxx		//STC8G系列

/*********************************************************/

#include	"STC8xxxx.H"


#endif

定时时间就比较好算了:STC是1T的指令周期,那么定时器自增/自减一次的时间就是:1/32000000 s 即 1000000/32000000 = 1/32us 那么计时1ms就需要 32000次。如果是定时器是上数模式的话,初值就是 65536-32000. STC给的公式就比较简单了,MAIN_Fosc是32000000,如果定时1ms,那么这里就写 65536-(MAIN_Fosc/1000);如果需要定时10ms,那么就改为12T模式,初值填 65536UL - (MAIN_Fosc / (100*12))。参考STC官方Demo。其实自己手动配寄存器也就是几句话的事。

还需要提一下的就是,库函数依旧是没有暂停/开启定时器和使能/失能定时器中断的函数可以调用,用户需要自己根据Timer_Inilize函数去看开启定时器是调用了STC8xxxx.H文件里面的哪个函数,或者简单粗暴一点就是直接看手册,直接去改变控制寄存器相应的位来实现自己的需求。

使用下来一个心得就是,一定要会到STC8xxxx.H文件里面找相应的宏或是函数,结合注释和手册,来调用芯片的某些高级功能

配置串口

调库:void UART_config(void)

void	UART_config(void)
{
	COMx_InitDefine		COMx_InitStructure;
	COMx_InitStructure.UART_Mode      = UART_8bit_BRTx;
	COMx_InitStructure.UART_BRT_Use   = BRT_Timer1;
	COMx_InitStructure.UART_BaudRate  = 115200ul;
	COMx_InitStructure.UART_RxEnable  = ENABLE;
	COMx_InitStructure.BaudRateDouble = DISABLE;
	COMx_InitStructure.UART_Interrupt = ENABLE;
	COMx_InitStructure.UART_Priority    = Priority_0;
	COMx_InitStructure.UART_P_SW      = UART1_SW_P36_P37;
	UART_Configuration(UART1, &COMx_InitStructure);
}

没啥好说的,没用到串口的高级功能

配置硬件PWM

调库:PWM_config();

void	PWM_config(void)
{
	PWMx_InitDefine		PWMx_InitStructure;
	
	PWMx_InitStructure.PWM1_Mode    =	CCMRn_PWM_MODE1;//CCMRn_MATCH_VALID;
	PWMx_InitStructure.PWM2_Mode    =	CCMRn_PWM_MODE1;//CCMRn_MATCH_VALID;
	PWMx_InitStructure.PWM3_Mode    =	CCMRn_PWM_MODE1;//CCMRn_MATCH_VALID;
	PWMx_InitStructure.PWM4_Mode    =	CCMRn_PWM_MODE1;//CCMRn_MATCH_VALID;
	PWMx_InitStructure.PWM5_Mode    =	CCMRn_PWM_MODE1;//CCMRn_MATCH_VALID;
	PWMx_InitStructure.PWM6_Mode    =	CCMRn_PWM_MODE1;//CCMRn_MATCH_VALID;
	PWMx_InitStructure.PWM7_Mode    =	CCMRn_PWM_MODE1;//CCMRn_MATCH_VALID;
	PWMx_InitStructure.PWM8_Mode    =	CCMRn_PWM_MODE1;//CCMRn_MATCH_VALID;

	PWMx_InitStructure.PWM1_SetPriority  = Priority_0;
	PWMx_InitStructure.PWM2_SetPriority  = Priority_0;
	PWMx_InitStructure.PWM3_SetPriority  = Priority_0;
	PWMx_InitStructure.PWM4_SetPriority  = Priority_0;
	PWMx_InitStructure.PWM5_SetPriority  = Priority_0;
	
	PWMx_InitStructure.PWM_Period   = 333;
	PWMx_InitStructure.PWM1_Duty    = PWMA_Duty.PWM1_Duty;
	PWMx_InitStructure.PWM2_Duty    = PWMA_Duty.PWM2_Duty;
	PWMx_InitStructure.PWM3_Duty    = PWMA_Duty.PWM3_Duty;
	PWMx_InitStructure.PWM4_Duty    = PWMA_Duty.PWM4_Duty;
	PWMx_InitStructure.PWM5_Duty    = PWMA_Duty.PWM5_Duty;
	PWMx_InitStructure.PWM6_Duty    = PWMA_Duty.PWM6_Duty;
	PWMx_InitStructure.PWM7_Duty    = PWMA_Duty.PWM7_Duty;
	PWMx_InitStructure.PWM8_Duty    = PWMA_Duty.PWM8_Duty;
	PWMx_InitStructure.PWM_DeadTime = 0;
	
	PWMx_InitStructure.PWM_EnoSelect   = ENO1N | ENO2N | ENO3N;	
	PWMx_InitStructure.PWM_PS_SW       = PWM1_SW_P10_P11| PWM2_SW_P12_P13 | PWM3_SW_P14_P15;


	PWMx_InitStructure.PWM_CC1NEnable  = ENABLE;
	PWMx_InitStructure.PWM_CC2NEnable  = ENABLE;
	PWMx_InitStructure.PWM_CC3NEnable  = ENABLE;
	
	PWMx_InitStructure.PWM_MainOutEnable= ENABLE;		//主输出使能
	PWMx_InitStructure.PWM_CEN_Enable   = DISABLE;		//主时钟失能

	PWM_Configuration(PWMA, &PWMx_InitStructure);		//初始化PWMA

	PWMx_InitStructure.PWM_CC6Enable   = ENABLE;
	PWMx_InitStructure.PWM_CC7Enable   = ENABLE;
	PWMx_InitStructure.PWM_CC8Enable   = ENABLE;

	PWMx_InitStructure.PWM_EnoSelect   = ENO6P | ENO7P | ENO8P;
	PWMx_InitStructure.PWM_PS_SW       = PWM6_SW_P54 | PWM7_SW_P33 | PWM8_SW_P34;

	PWM_Configuration(PWMB, &PWMx_InitStructure);		//初始化PWMB

	EAXSFR();		/* MOVX A,@DPTR/MOVX @DPTR,A指令的操作对象为扩展SFR(XSFR) */

	PWMA_ENO = 0;	//禁止所有输出
	PWMB_ENO = 0;

	// PWMB_MainModeSel(MMSn_UPDATE);		//配置PWMB为主模式 输出更新信号UEV
	// PWMA_SMCR_Source(SMCR_TSn_ITR2);	//触发源选择为PWMB产生的更新信号
	// PWMA_SMCR_SMS(SMCR_SMSA_RESET);		//配置PWMA为复位模式

	// PWMA_CC1IE_Enable();					//允许PWMA更新中断
	// PWMA_SR1 = 0;						//清楚所有中断标志

	EAXRAM();		/* MOVX A,@DPTR/MOVX @DPTR,A指令的操作对象为扩展RAM(XRAM) */
}

说实话这个初始化函数真的一言难尽,你们写这个库函数的时候,没有发现这个函数没有配置PWM输出的有效电平和计数器的计数方向么?(需要设置PWM有效电平和计数方向的 请到STC8xxxx.H文件里面找到相应的库函数)
我把这两句自己加在了u8 PWM_Configuration(u8 PWM, PWMx_InitDefine *PWMx);里面:
在这里插入图片描述
还有PWMB的:
在这里插入图片描述

再比如这句:PWMx_InitStructure.PWM_MainOutEnable= ENABLE; //主输出使能,观察发现这句配置的是PWMA_BKR寄存器或是PWMB_BKR寄存器,首先是寄存器名字错了,库里面叫做PWMA_BRK和PWMB_BRK,其次这个是PWMA/PWMB刹车寄存器,不懂为什么禁止/使能通道和互补通道输出要配置这一位,只能理解为总开关。
在这里插入图片描述

开了总开关之后,还需要配置PWMA_CCER1这个寄存器。
在这里插入图片描述
CC1P控制的是OC1的输入捕获/比较输出的有效电平,CC1E控制的是输入捕获/比较输出 的使能/失能。
比如我这里用到的是比较输出,那么我需要配置有效电平,然后把使能打开。
这是第二个开关。

这是第三个开关。。。。。

在这里插入图片描述
说真的,这第三个开关,我不细看STC8xxxx.H文件我是真的不知道,库函数和demo例程都一点没提这个寄存器。第一步我把PWM开起来之后,有波形输出了,然后我发现我无论如何都关不掉这个波形,再后来才查到用PWMx_ENO寄存器来控制输出。
难道库函数多写一个暂停/开启函数,真的要花那么大精力么??

经过上述的配置,这时候 mian函数变成了:

void main(void)
{
    //u8	i;

    P_SW2 = 0x80;
    XOSCCR = 0xc0;                              //启动外部晶振
    while (!(XOSCCR & 1));                      //等待时钟稳定
    CLKDIV = 0x00;                              //时钟不分频
    CLKSEL = 0x01;                              //选择外部晶振
    P_SW2 = 0x00;

    pwm_proid = 333;
    PWMA_Duty.PWM1_Duty = 0;
    PWMA_Duty.PWM2_Duty = 0;
    PWMA_Duty.PWM3_Duty = 0;
    PWMA_Duty.PWM6_Duty = 0;
    PWMA_Duty.PWM7_Duty = 0;
    PWMA_Duty.PWM8_Duty = 0;
    
    GPIO_config();
    Timer_config();
    UART_config();
    PWM_config();
    EA = 1;

    ALL_OFF;								//所有PWM的GPIO全部输出无效电平   不过这句貌似没用,得要先把GPIO的PWM使能给关了才可以改变

    while (1)
    {
    
	}
}

这时候中断就已经开起来了,硬件PWM也是开启的,串口也是正常的收发的。

特殊功能

这个项目是一个工控的机器,主控那边把当前用户设定好的频率和占空比的数据通过串口发送过来STC芯片,经过校验核对无误之后,再把相应的波形输出出去,并给74HC244使能信号,让PWM信号发送出去。有6路PWM同步的需求,还有其中两路PWM错相的需求。

同步功能

这里原计划是使用PWMA比较/捕获中断或者是通过PWMB使能PWMA,但是说实在的这两个功能我是真的玩不明白,感觉芯片总是出现一些不该出现的状态,最后没办法任务紧急这里放弃研究了,有心人愿意钻研的可以好好研究一下这两个功能。STC把自家硬件PWM吹的天花乱坠,但是实际上呢,连个DEMO都不做,我真不知道用户要怎么去玩转这些功能。

简单的贴一下PWMB启动PWMA把:(在PWM_config();的末尾 被我注释掉了)

	// PWMB_MainModeSel(MMSn_UPDATE);		//配置PWMB为主模式 输出更新信号UEV
	// PWMA_SMCR_Source(SMCR_TSn_ITR2);		//触发源选择为PWMB产生的更新信号
	// PWMA_SMCR_SMS(SMCR_SMSA_TRIG);		//配置PWMA为触发模式

STC手册是这么写的:
在这里插入图片描述
实测下来,开PWMB之后,PWMA确实也能跟着起来,但是没有达到两个波形同步的效果。于是乎放弃这个方法。

改用PWMA产生比较/捕获中断,然后再在中断里面把PWMB开启,那么我通过在中断里面改变PWMB计数器的初值,也能精准控制两路PWM的错相间隔,通过补偿就能实现两路PWM同步。(理论可行,实际还是不行)

在PWM_config();的末尾:

	// PWMA_CC1IE_Enable();					//允许PWMA比较/捕获中断
	// PWMA_SR1 = 0;						//清楚所有中断标志

这样就把PWM比较/捕获中断打开了。

这里还有其他中断可以使能:
在这里插入图片描述
不过我已经试过了,无论是更新中断还是比较/捕获中断都无法实现我想要的效果。

另外这里出现了开篇的那个坑:Demo程序清中断标志和失能中断允许,是起不到效果的,原因就是没有切换到操作SFR寄存器。
这个才是正确的中断服务函数:

//========================================================================
// 函数: void PWMA_ISR(void) interrupt PWMA_VECTOR
// 描述: PWMA中断处理程序. 捕获数据通过 TIM1-> CCRnH / TIM1-> CCRnL 读取
// 参数: None
// 返回: none.
// 版本: V1.0, 2021-6-1
//========================================================================
void PWMA_ISR(void) interrupt PWMA_VECTOR
{
    EAXSFR();
    P32 = ~P32;
	PWMA_SR1 = 0;	//清除中断标志

    // pwm_cnt++;
    // if(pwm_cnt >=2)
    // {
    //     pwm_cnt=0;
        
    //     //PWMA_CC1IE_Disable();           //禁止比较/捕获中断
    // }

    EAXRAM();		/* MOVX A,@DPTR/MOVX @DPTR,A指令的操作对象为扩展RAM(XRAM) */
}

虽然中断入口地址,肯定是可以在手册上找到,这个函数呢,用户自己摸索之后也可以写出来,但是你作为原厂,为什么关于这些高级功能的内容连个demo都不做??真就是菜鸟不配用你家的芯片么?(反正我用过这一次以后再也不会选了)

再就是,所谓的论坛资源很丰富。上面这个函数就是我在论坛置顶帖子下载的《高级PWM相关程序》,里面原厂的工程梁工还在帖子下回帖加精了。。。。我想问一句,梁工,路人随手一弄的东西,你自己不测测的??没有EAXSFR();这句代码,这个中断无法清除中断标志,任凭你怎么改PWM的占空比和频率,进中断的时间始终一致,你们这么大的公司没一个人发现么??

按照我修改以后的代码,总算是可以随着PWM计数器计数值与设定占空比值匹配然后产生中断进中断服务函数,但是依旧是无法实现我的需求,那几天改的头发昏,写博的时候已经不知道当时是什么原因的。按道理来说,这个是一定可行的。
贴几张匹配中断的截图:
在这里插入图片描述
6通道就是P32的值,P32是根据PWM发生匹配的时候进终端,然后翻转一次。现在这么看起来,好像还是很同步的,然后放大看:
在这里插入图片描述
可以看出,中断滞后了424ns,当然这是正常的,毕竟有那么多条指令需要执行,到那时不太明白当时为什么使用这种方法无法使两路PWM完全同步。

后来这段就是我最后采用的办法了,通过这个办法也是成功实现了两路波形基本同步:(篇幅较长,中间扯得比较远)

首先是我串口接受到数据会把一个标志位置一:f_duty_change=1;
在主函数中

		if(f_duty_change==1)
        {
            Timer0_Stop();
            Timer0_InterruptDisable();

            SD=1;           //停止74HC244传输数据

            EAXSFR();		/* MOVX A,@DPTR/MOVX @DPTR,A指令的操作对象为扩展SFR(XSFR) */

            PWMA_ENO = 0;
            PWMB_ENO = 0;

            PWMA_CR1 &= ~0x01;		//0:禁止计数器
            PWMB_CR1 &= ~0x01;		//0:禁止计数器

            ALL_OFF;

            if(maikuan_row >= 11)
            {
                PWMA_Prescaler(1);
                PWMB_Prescaler(1);
            }	
            else
            {
                PWMA_Prescaler(0);
                PWMB_Prescaler(0);
            }

            if(hlvol == 2)                                                  //F模式
            {
                pwm_proid = maikuan_ayy_F[maikuan_row][maijian-3];			//maikuan:总共14组 对应14行	maijian: 3-15 对应0-12列
                PWMA_Duty.PWM1_Duty = duty_ayy[maikuan_row];
                PWMA_Duty.PWM2_Duty = duty_ayy[maikuan_row];
                PWMA_Duty.PWM3_Duty = duty_ayy[maikuan_row];

                PWMA_Duty.PWM6_Duty = duty_ayy[maikuan_row];
                PWMA_Duty.PWM7_Duty = duty_ayy[maikuan_row];
                PWMA_Duty.PWM8_Duty = duty_ayy[maikuan_row];
            }
            else                                                            //H/L模式
            {
                pwm_proid = maikuan_ayy_HL[maikuan_row][maijian-3];			//maikuan:总共14组 对应14行	maijian: 3-15 对应0-12列  
                if(maikuan >= 16)
                {
                    if(maijian == 3)         {j = (pwm_proid/100)*33; k = (pwm_proid/100)*29;  }        //不同maijian模式下  占空比也不同  maijian越大  占空比越低
                    else if(maijian == 4)    {j = (pwm_proid/100)*22; k = (pwm_proid/500)*97;  }
                    else if(maijian == 5)    {j = (pwm_proid/100)*17; k = (pwm_proid/500)*73;  }
                    else if(maijian == 6)    {j = (pwm_proid/100)*13; k = (pwm_proid/1000)*117;}
                    else if(maijian == 7)    {j = (pwm_proid/100)*11; k = (pwm_proid/500)*49;  }
                    else if(maijian == 8)    {j = (pwm_proid/500)*49; k = (pwm_proid/250)*21;  }
                    else if(maijian == 9)    {j = (pwm_proid/500)*43; k = (pwm_proid/500)*37;  }
                    else if(maijian == 10)   {j = (pwm_proid/1000)*77;k = (pwm_proid/200)*13;  }
                    else if(maijian == 11)   {j = (pwm_proid/1000)*69;k = (pwm_proid/1000)*59; }
                    else if(maijian == 12)   {j = (pwm_proid/1000)*63;k = (pwm_proid/500)*27;  }
                    else if(maijian == 13)   {j = (pwm_proid/1000)*57;k = (pwm_proid/1000)*49; }
                    else if(maijian == 14)   {j = (pwm_proid/1000)*53;k = (pwm_proid/200)*9;   }
                    else if(maijian == 15)   {j = (pwm_proid/2000)*99;k = (pwm_proid/500)*21;  }

                    PWMA_Duty.PWM3_Duty = (unsigned int)j;

                    PWMA_Duty.PWM6_Duty = (unsigned int)k;
                }
                else
                {
                    PWMA_Duty.PWM3_Duty = duty_ayy[maikuan_row];
                    PWMA_Duty.PWM6_Duty = duty_ayy[maikuan_row];
                }
                
                PWMA_Duty.PWM1_Duty = duty_ayy[maikuan_row];
                PWMA_Duty.PWM2_Duty = duty_ayy[maikuan_row];
                
                PWMA_Duty.PWM7_Duty = duty_ayy[maikuan_row];
                PWMA_Duty.PWM8_Duty = duty_ayy[maikuan_row];
            }

            pwm_proid_hig = (pwm_proid&0xff00)>>8;
            pwm_proid_low = (pwm_proid&0x00ff);
            
            PWMA_ARR = pwm_proid;
            PWMB_ARR = pwm_proid;

            PWMA_CCR1 = PWMA_Duty.PWM1_Duty;
            PWMA_CCR2 = PWMA_Duty.PWM2_Duty;
            PWMA_CCR3 = PWMA_Duty.PWM3_Duty;

            PWMB_CCR6 = PWMA_Duty.PWM6_Duty;
            PWMB_CCR7 = PWMA_Duty.PWM7_Duty;
            PWMB_CCR8 = PWMA_Duty.PWM8_Duty;

            EAXRAM();		/* MOVX A,@DPTR/MOVX @DPTR,A指令的操作对象为扩展RAM(XRAM) */

            f_duty_change=0;
            f_status_change=1;
            f_exactly_once=0;
            f_cnt_stop_out=1;
            t_cnt_stop_out=0;

            Timer0_InterruptEnable();
            Timer0_Run();
        }

这部分内容读者可能看不明白,因为是我项目内的一些特殊设置,贴出来是记录下类似波形的处理方法。

因为我是收到主控那边传过来的 占空比档位 总周期档位,但是对应到我实际出PWM的芯片上,是无法对应成相应的数值的,是需要自己去调整PWM的自动重装载寄存器(PWMA_ARR、PWMB_ARR)的值来调整总周期的,然后调整PWMA_CCR1、PWMA_CCR2等寄存器来调整占空比的。

这里就需要建表之后调表来快速匹配到相应的初值,建表过程比较痛苦,后来才发现一个比较快速的办法。

首先我这里抓取到了波形的实际数据:
在这里插入图片描述
首先我这里已经测量到了14个档位的有效电平时间和无效电平时间,每个档位有13种不同的频率,也就是14*13种组合。

首先我随便给一个总周期值,先把14档不同的占空比值对应的寄存器值得出来:

u16 duty_ayy[14] = {  69,  134,  260,  320,  390,  575,  1025,  1260,  1900,  2550,  3830,  2550,  3190,  3975};

然后通过excel的处理,得到一张占空比表。比如 第一档的占空比 时间是 2.16us 对应的寄存器初值是69,总周期是2.16+8.33=10.49us,占空比约等于20.6%,倒推出来的周期值就是69/0.206=334

第二档的第一种频率占空比为 4.15/(4.15+14.30) =0.2249 ,那么倒推出来第二档第一种频率的周期值应该为134/0.2249=595.82=595。以此类推。

我建的周期表为:

u16 maikuan_ayy_HL[14][13]=
{
    {  333 ,  395  ,  500  ,  570  ,  650  ,  715  ,  790  ,  870  ,  950  ,  1040 ,  1110 ,  1195 ,  1260 },//0
    {  600 ,  735  ,  875  ,  1020 ,  1160 ,  1290 ,  1430 ,  1580 ,  1740 ,  1870 ,  1995 ,  2140 ,  2280 },//1
    {  1100,  1380 ,  1640 ,  1910 ,  2185 ,  2440 ,  2700 ,  2985 ,  3240 ,  3530 ,  3790 ,  4050 ,  4330 },//2
    {  1360,  1680 ,  2030 ,  2360 ,  2695 ,  3020 ,  3335 ,  3700 ,  4010 ,  4330 ,  4690 ,  5000 ,  5320 },//3
    {  1620,  2000 ,  2420 ,  2800 ,  3190 ,  3600 ,  4000 ,  4400 ,  4800 ,  5200 ,  5600 ,  5980 ,  6350 },//4
    {  2380,  2970 ,  3570 ,  4130 ,  4750 ,  5320 ,  5900 ,  6500 ,  7045 ,  7620 ,  8270 ,  8840 ,  9420 },//5
    {  2072,  3082 ,  4092 ,  5102 ,  6100 ,  7290 ,  8136 ,  9170 ,  10141,  11175,  12177,  13180,  14182},//6
    {  2542,  3787 ,  5032 ,  6276 ,  7517 ,  8694 ,  10025,  11264,  12503,  13649,  14981,  16251,  17490},//7
    {  3822,  5707 ,  7592 ,  9476 ,  11311,  13193,  15138,  17020,  18870,  20721,  22666,  24548,  26430},//8
    {  5123,  7657 ,  10192,  12727,  15227,  17763,  20330,  22833,  25400,  27904,  30471,  32975,  35510},//9
    {  7682, 11497 ,  15312,  19126,  22940,  26762,  30584,  34406,  38228,  42050,  45872,  49694,  53516},//10
    {  5111,  7653 ,  10196,  12738,  15297,  17846,  20396,  22945,  25494,  28044,  30593,  33142,  35692},//11
    {  6391,  9574 ,  12753,  15938,  19154,  22347,  25540,  28733,  31926,  35119,  38152,  41504,  44538},//12
    {  7961, 11928 ,  15896,  19863,  23885,  27867,  31690,  35831,  39654,  43795,  47618,  51759,  55582} //13
};

当然这里实际值跟我理论计算并不一样,是因为我前面没想到这种倒推的办法,这是一点点通过调整填入自动重装载寄存器的初值来调的。

实际用好excel的话,这个表只需要半小时就建出来了。

在1ms一次的定时中断函数中:

void timer0_int (void) interrupt TIMER0_VECTOR          //1ms
{
    if(EN == 1)
    {
        if(f_duty_stop == 0)
        {
            f_duty_stop = 1;
            f_rece_done = 0;        //EN脚信号被拉高说明进入调节模式 当EN重新拉低时 等待UART接受完一次数据之后 再把pwm停止输出标志位清零(为了下一次直接输出设置的目标波形)
			
			EAXSFR();		/* MOVX A,@DPTR/MOVX @DPTR,A指令的操作对象为扩展SFR(XSFR) */

            SD = 1;

            PWMA_ENO = 0;
            PWMB_ENO = 0;

            PWMA_CR1 &= ~0x01;		//0:禁止计数器
            PWMB_CR1 &= ~0x01;		//0:禁止计数器

			EAXRAM();		/* MOVX A,@DPTR/MOVX @DPTR,A指令的操作对象为扩展RAM(XRAM) */

            ALL_OFF;

            f_current_change = 0;
            f_status_change  = 0;
        }
    }
    else
    {
        if(f_duty_stop == 1 && f_rece_done == 1)
        {
            f_duty_stop=0;
			f_duty_change=1;
        }
    }

    if(f_duty_change == 0 && f_duty_stop == 0)
    {
        if(f_cnt_stop_out==1)
        {
            t_cnt_stop_out++;
            if(t_cnt_stop_out>3)
            {
                t_cnt_stop_out=0;
                f_cnt_stop_out=0;
            }
        }
        else if(f_current_change == 1)
        {
            f_status_change=0;

            EAXSFR();		/* MOVX A,@DPTR/MOVX @DPTR,A指令的操作对象为扩展SFR(XSFR) */

            t_current_change++;
            if(t_current_change < 78)
            {
                if(f_exactly_once == 0)
                {
                    f_exactly_once=1;
                    PWMA_ENO |= 0x02;       //开启第一路

                    PWMA_CNTRH = pwm_proid_hig;
                    PWMA_CNTRL = pwm_proid_low;

                    PWMA_CR1 |= 0x01;		//1:使能计数器

                    SD = 0;

                    if(current == 1) {f_current_change=0;t_current_change=0;f_exactly_once=0;}
                }
            }
            else if(t_current_change >= 78 && t_current_change < 82)
            {
                if(f_exactly_once == 1)
                {
                    f_exactly_once=0;

                    SD = 1;

                    PWMA_ENO = 0;
                    PWMB_ENO = 0;

                    PWMA_CR1 &= ~0x01;		//0:禁止计数器
                    PWMB_CR1 &= ~0x01;		//0:禁止计数器

                    ALL_OFF;
                }
            }
            else if(t_current_change >= 82 && t_current_change<160)
            {
                if(f_exactly_once == 0)
                {
                    f_exactly_once=1;
                    PWMA_ENO |= 0x0A;       //第一二路同时开启
                    
                    PWMA_CNTRH = pwm_proid_hig;
                    PWMA_CNTRL = pwm_proid_low;

                    PWMA_CR1 |= 0x01;		//1:使能计数器

                    SD = 0;

                    if(current==2) {f_current_change=0;t_current_change=0;f_exactly_once=0;}
                }
            }
            else if(t_current_change >= 160 && t_current_change < 164)
            {
                if(f_exactly_once == 1)
                {
                    f_exactly_once=0;

                    SD = 1;

                    PWMA_ENO = 0;
                    PWMB_ENO = 0;

                    PWMA_CR1 &= ~0x01;		//0:禁止计数器
                    PWMB_CR1 &= ~0x01;		//0:禁止计数器

                    ALL_OFF;
                }
            }
            else if(t_current_change >= 164 && t_current_change<242)
            {
                if(f_exactly_once == 0)
                {
                    f_exactly_once=1;

                    PWMA_ENO |= 0x2A;       //开启 1 2 3 路

                    if(current == 3)
                    {
                        f_current_change=0;t_current_change=0;f_exactly_once=0;
                    }
                    else if(current >= 4)                               
                    {
                        PWMB_ENO |= 0x04;   //如果还有第四路  那么就同时开启三四路

                        if(current == 4)
                        {
                            f_current_change=0;t_current_change=0;f_exactly_once=0;
                        }
                    }

                    PWMB_CNTRH = pwm_proid_hig;
                    PWMB_CNTRL = pwm_proid_low;

                    pwm_proid = pwm_proid+6;
                    pwm_proid_hig = (pwm_proid&0xff00)>>8;
                    pwm_proid_low = (pwm_proid&0x00ff);

                    PWMA_CNTRH = pwm_proid_hig;
                    PWMA_CNTRL = pwm_proid_low;

                    PWMA_CR1 |= 0x01;		//1:使能计数器
                    PWMB_CR1 |= 0x01;		//1:使能计数器

                    SD = 0;
                }
            }
            else if(t_current_change >= 242 && t_current_change < 246)
            {
                if(f_exactly_once == 1)
                {
                    f_exactly_once=0;

                    SD = 1;

                    PWMA_ENO = 0;
                    PWMB_ENO = 0;

                    PWMA_CR1 &= ~0x01;		//0:禁止计数器
                    PWMB_CR1 &= ~0x01;		//0:禁止计数器

                    ALL_OFF;
                }
            }
            else if(t_current_change >= 246)
            {
                if(f_exactly_once == 0)
                {
                    f_exactly_once=1;

                    PWMA_ENO |= 0x2A;PWMB_ENO |= 0x14;                  //开启 1 2 3 4 5 路

                    if(current ==6)                                     //如果还有第六路  那么就同时开启五六路
                    {
                        PWMB_ENO |= 0x54;
                    }

                    PWMB_CNTRH = pwm_proid_hig;
                    PWMB_CNTRL = pwm_proid_low;

                    pwm_proid = pwm_proid+6;
                    pwm_proid_hig = (pwm_proid&0xff00)>>8;
                    pwm_proid_low = (pwm_proid&0x00ff);

                    PWMA_CNTRH = pwm_proid_hig;
                    PWMA_CNTRL = pwm_proid_low;

                    PWMA_CR1 |= 0x01;		//1:使能计数器
                    PWMB_CR1 |= 0x01;		//1:使能计数器
                    
                    SD = 0;

                    f_current_change=0;t_current_change=0;f_exactly_once=0;
                }
            }
            EAXRAM();		/* MOVX A,@DPTR/MOVX @DPTR,A指令的操作对象为扩展RAM(XRAM) */
        }
        else if(f_status_change == 1 && f_current_change == 0) // 
        {
            EAXSFR();		/* MOVX A,@DPTR/MOVX @DPTR,A指令的操作对象为扩展SFR(XSFR) */

            // PWMA_CC1IE_Enable();    //允许PWMA更新中断

            PWMB_CNTRH = pwm_proid_hig;
            PWMB_CNTRL = pwm_proid_low;

            pwm_proid = pwm_proid+6;
            pwm_proid_hig = (pwm_proid&0xff00)>>8;
            pwm_proid_low = (pwm_proid&0x00ff);

            PWMA_CNTRH = pwm_proid_hig;
            PWMA_CNTRL = pwm_proid_low;

            if(current == 1)    {PWMA_ENO |= 0x02;}
            else if(current==2) {PWMA_ENO |= 0x0A;}
            else if(current==3) {PWMA_ENO |= 0x2A;}
            else if(current==4) {PWMA_ENO |= 0x2A;PWMB_ENO |= 0x04;}
            else if(current==5) {PWMA_ENO |= 0x2A;PWMB_ENO |= 0x14;}
            else if(current==6) {PWMA_ENO |= 0x2A;PWMB_ENO |= 0x54;}

            PWMA_CR1 |= 0x01;		//1:使能计数器
            PWMB_CR1 |= 0x01;		//1:使能计数器
            
            EAXRAM();		/* MOVX A,@DPTR/MOVX @DPTR,A指令的操作对象为扩展RAM(XRAM) */

            SD=0;
            f_status_change = 0;
            pwm_cnt=0;
        }
    }

    if(f_over_flag==0)      //超时没有接收到串口数据  终止发PWM
    {
        t_over_time++;
        if(t_over_time>=5000)
        {
            t_over_time=0;
            f_over_flag=1;

            SD = 1;         //停止74HC244传输数据

            EAXSFR();		/* MOVX A,@DPTR/MOVX @DPTR,A指令的操作对象为扩展SFR(XSFR) */

            PWMA_ENO = 0;
            PWMB_ENO = 0;

            PWMA_CR1 &= ~0x01;		//0:禁止计数器
            PWMB_CR1 &= ~0x01;		//0:禁止计数器

            EAXRAM();		/* MOVX A,@DPTR/MOVX @DPTR,A指令的操作对象为扩展RAM(XRAM) */

            ALL_OFF;

            f_current_change = 0;
            f_status_change  = 0;
        }
    }
}

这部分代码就是个人纯记录用的了,不清楚产品实际功能的读者是看不明白的。

改变了出PWM路数之后,要实现这样的效果:
在这里插入图片描述
就是显出1路波形78ms,然后停5ms,再出12路波形78ms,再停5ms……最终出到设定的路数。上图是从5路改为6路的波形图。
我的实现方法是,首先判断是否为改变了路数的状态,如果是的话,首先是暂停所有路输出3ms,这个是通过f_cnt_stop_out这个标志位来计时3ms。然后进入出波的逻辑过程,首先定义一个时间轴变量t_current_change,在这个时间轴小于78的时候,开启第一路输出(注意f_exactly_once标志位控制只执行一次)(意思是出1路波形78ms),同时判断设定的PWM路数是否大于1,如果等于一那么就没有后面的逻辑了。然后进时间轴大于等于78小于82的时候,停止输出,也是通过f_exactly_once标志位控制只执行一次。然后时间轴大于等于82小于160(78ms)的时候开启1和判断第2路是否开启,以此类推。

如果是没有改变PWM路数,只改了其他参数,那么出波就是所有波形停止3ms之后再根据设置值同时出波:
对应的是这段代码

		else if(f_status_change == 1 && f_current_change == 0) // 
        {
            EAXSFR();		/* MOVX A,@DPTR/MOVX @DPTR,A指令的操作对象为扩展SFR(XSFR) */

            // PWMA_CC1IE_Enable();    //允许PWMA更新中断

            PWMB_CNTRH = pwm_proid_hig;
            PWMB_CNTRL = pwm_proid_low;

            pwm_proid = pwm_proid+6;
            pwm_proid_hig = (pwm_proid&0xff00)>>8;
            pwm_proid_low = (pwm_proid&0x00ff);

            PWMA_CNTRH = pwm_proid_hig;
            PWMA_CNTRL = pwm_proid_low;

            if(current == 1)    {PWMA_ENO |= 0x02;}
            else if(current==2) {PWMA_ENO |= 0x0A;}
            else if(current==3) {PWMA_ENO |= 0x2A;}
            else if(current==4) {PWMA_ENO |= 0x2A;PWMB_ENO |= 0x04;}
            else if(current==5) {PWMA_ENO |= 0x2A;PWMB_ENO |= 0x14;}
            else if(current==6) {PWMA_ENO |= 0x2A;PWMB_ENO |= 0x54;}

            PWMA_CR1 |= 0x01;		//1:使能计数器
            PWMB_CR1 |= 0x01;		//1:使能计数器
            
            EAXRAM();		/* MOVX A,@DPTR/MOVX @DPTR,A指令的操作对象为扩展RAM(XRAM) */

            SD=0;
            f_status_change = 0;
            pwm_cnt=0;
        }

这个就是我实现波形同步的方法,直接操作PWM的计数器,通过手动补偿实现两路波形同步,效果如图:
在这里插入图片描述
两路波形只差8ns,对于使用场景来说够了。

另外记录一下这里为什么这样填初值:

			PWMB_CNTRH = pwm_proid_hig;
            PWMB_CNTRL = pwm_proid_low;

            pwm_proid = pwm_proid+6;
            pwm_proid_hig = (pwm_proid&0xff00)>>8;
            pwm_proid_low = (pwm_proid&0x00ff);

            PWMA_CNTRH = pwm_proid_hig;
            PWMA_CNTRL = pwm_proid_low;

首先再主函数里面,我已经通过函数取到了要填入的初值pwm_proid_hig和pwm_proid_low,对于PWMB而言,这里直接赋值就好了。

但是对于PWMA而言,还需要补偿,为啥我是pwm_proid = pwm_proid+6; 通过先加总值然后移位再赋值呢。这里也是我一开始没考虑到的地方、
在这里插入图片描述
当时的现象是,我原本使用的是PWMA_CNTRL = pwm_proid_low+6;这样的写法,可以看到我注释掉了,这个写法我补偿6,波形滞后非常多,我把+6改为+3,滞后变的只有计时ns,可是一旦加大于4,滞后立马变为了us级。后来想到可能是溢出了,于是按照上图的方式改了滞后,波形恢复正常问题解决。

写到这,项目算是完成了,但是还有不完美的地方,比如改变状态之后,会出现这样的波形
在这里插入图片描述
我确实是没时间去深究出现的原因,这里直接丢给客户了,如果没有什么问题,这种bug也不打算修了,就这样把。

还有就是,负载需要阶梯波,其实对于我MCU而言,我给出这种波就行了:
在这里插入图片描述
至于负载端的阶梯波,实际上就是,我一路PWM控制一个MOS的G极,MOS的S极对地接一个电阻,各个MOS之间是并联的,导通的MOS越多,接入的电阻越多,并联的阻值越小,电流也就越大,负载端的波形就是一个阶梯形状的波形。

总结

这个项目说难不难,只是STC的弯弯绕绕太多了,如果换用ARM的芯片估计早就调试出来了,用STC这个硬是耗了一个星期时间。还是那句话,如果你准备用或是已经开始用STC了,那么建议你再考虑考虑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/650949.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Ubuntu16.04部署BEVformer 实时记录

一 配置依赖 a. Create a conda virtual environment and activate it. conda create -n open-mmlab python3.8 -y conda activate open-mmlabb. Install PyTorch and torchvision following the official instructions. pip install torch1.9.1cu111 torchvision0.10.1cu11…

【Linux】面试重点:死锁和生产消费模型原理

面试要点来了~ 文章目录 前言一、死锁的一系列问题二、生产者消费者模型原理总结 前言 上一篇的互斥量原理中我们讲解了锁的原理&#xff0c;我们知道每次线程申请锁的时候一旦申请成功这个线程自己就把锁带在自己身上了&#xff0c;这就保证了锁的原子性&#xff08;因为只有…

备忘录模式(二十二)

相信自己&#xff0c;请一定要相信自己 上一章简单介绍了中介者模式(二十一), 如果没有看过, 请观看上一章 一. 备忘录模式 引用 菜鸟教程里面备忘录模式介绍: https://www.runoob.com/design-pattern/memento-pattern.html 备忘录模式&#xff08;Memento Pattern&#xff…

基于Java单位人事管理系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a; ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精…

大面积无线WIFI覆盖 H3C WX3010E(AC+PoE三层交换机)+ H3C WA2620E、WA4320无线AP +华为USG6310S防火墙

一、适用场景&#xff1a; 1、跨复杂区域覆盖WIFI。支持多房间、多栋、多层复式楼、别墅、自建房的无线WIFI覆盖。 2、强大的漫游功能。楼上楼下移动使用WIFI时&#xff0c;需要支持WIFI的信号漫游&#xff0c;更换地理位置不掉线、不中断。 3、用户量或网络流量的负载均衡功…

IMX6ULL的官方SDK和官方BSP下载

买了块IMX6ULL的开发板&#xff0c;但是不想直接用开发板跟的程序&#xff0c;还有比如后面移植uboot和kernel的时候也想基于IMX6ULL官方的uboot和kernel做移植工作&#xff0c;所以自己先找一下怎么在官网下载这些东西。 1 官方SDK下载 百度搜索NXP官网&#xff0c;进去之后…

HotSpot虚拟机参数配置及优化

目录 一、JVM配置参数 二、GC回收日志分析 三、虚拟机性能监控和故障处理工具 1.命令工具 1)&#xff1a;基础工具 2)&#xff1a;性能监控和故障处理 2.可视化工具 四、JVM常出现问题 五、参考资料 一、JVM配置参数 HotSpot直到JDK9才提供统一的日志处理框架&#xff…

【k8s系列】使用MicroK8s 5分钟搭建k8s集群含踩坑经验

文章目录 MicrosK8s介绍版本选择准备三台虚拟机搭建MicroK8s环境安装Microk8s把当前用户加入Admin Group访问K8s启动和停止MicroK8s服务 搭建MicroK8s集群把Worker节点分别加入到MicroK8s集群在Master节点检查节点运行情况在Master节点打开存储插件在Master节点打开DNS插件查看…

低转速压榨,充分保留营养,用蓝宝原汁机每天轻享新鲜果汁

现在大家都特别重视健康&#xff0c;像是蔬菜、水果都是每天必需的&#xff0c;而且为了充分获取营养&#xff0c;很多人都会使用破壁机之类的工具&#xff0c;我觉得原汁机效果更好一些&#xff0c;它能够用慢速研磨技术来提取果汁&#xff0c;使用时不需要加水&#xff0c;能…

el-date-picker 结合dayjs 快速实现周选择、月选择,并设置控件的显示格式

目录 情况需求思路&#xff1a;使用el-date-picker 配置type属性&#xff0c;结合dayjsdayjs的安装以及常用api实现效果图 实现代码其他配置设置周选择控件显示一行为星期一 至 星期日 情况需求 在传递查询条件时&#xff0c;要求时间参数需要为 一周 或 一个月 思路&#xf…

系统移植 编译uboot和linux源码及驱动配置

写在前面&#xff1a;若是有些命令执行失败&#xff0c;前面添加sudo后再执行 目录 写在前面&#xff1a;若是有些命令执行失败&#xff0c;前面添加sudo后再执行 uboot源码获取和编译&#xff1a; Linux源码获取和编译 关于驱动配置 uboot源码获取和编译&#xff1a; 获…

驾驶安全、便捷,尽在车载Notification开发的掌握

Notification 概述 通知&#xff08;Notification&#xff09;是移动应用中常用的一种交互方式&#xff0c;用于向用户展示重要的信息、提醒事件或提供即时反馈等。通知可以以弹出窗口、图标或声音等形式呈现给用户。 以下是关于通知的一些基本概念和要点&#xff1a; 通知栏…

React基础教程(三):JSX语法

React基础教程(三)&#xff1a;JSX语法 1、JSX简介 全称&#xff1a;JavaScript XMLreact定义的一种类似于XML的JS扩展语法&#xff1a;JSXML本质是React.createElement(component, props, ...children)方法的语法糖作用&#xff1a;用来简化创建虚拟DOM&#xff08;注意&…

【数据库三】MySQL事务

MySQL事务 1.事务的概念2.事务的ACID特点3.知识点总结 1.事务的概念 事务是一种机制、一个操作序列&#xff0c;包含了一组数据库操作命令&#xff0c;并且把所有的命令作为一个整体&#xff0c;一起向系统提交或撤销操作请求&#xff0c;即这一组数据库命令要么都执行&#x…

JAVA0615_2

04JDK的下载和安装 05常用dos命令

golang-gin-mysql转gorm-struct--gen-model

背景:python-django项目用go-gin重构&#xff0c;数据库已存在&#xff0c;gin中使用gorm 所以需要工具将mysql数据库中的表结构转到orm的model中。 前提&#xff1a;因为国内访问github需要稳定的代理 Goproxy.cn 推荐这个 1.在项目路径中下载gen-model模块 go get -u git…

测试员怎么克服职业惯性获得成功?

长期从事质量控制的测试员&#xff0c;容易患上职业病——挑毛病、谈风险&#xff0c;踩刹车&#xff0c;大部分时候说的和做的都对。正所谓“手里拿个锤子&#xff0c;看什么都是钉子”。但事情成功者往往是那些有想法&#xff0c;有冲劲的乐观主义者&#xff0c;正所谓“悲观…

使用docker快速搭建redis哨兵模式

说明 本文主要参考&#xff1a; https://www.cnblogs.com/coderaniu/p/15352323.html https://developer.aliyun.com/article/892805 但是这两篇博客均缺失部分关键性细节&#xff0c;所以重新撰文。读者可以结合本文和上述文章一起阅读。 安装步骤 安装docker和docker-co…

【机器学习】——深度学习与神经网络

目录 引入 一、神经网络及其主要算法 1、前馈神经网络 2、感知器 3、三层前馈网络&#xff08;多层感知器MLP&#xff09; 4、反向传播算法 二、深度学习 1、自编码算法AutorEncoder 2、自组织编码深度网络 ①栈式AutorEncoder自动编码器 ②Sparse Coding稀疏编码 …

【Java基础学习打卡10】JDK下载与安装

目录 前言一、JDK下载1.JDK官网2.版本说明3.JDK 11下载 二、JDK安装1.JDK安装2.JDK安装目录介绍 总结 前言 本文主要介绍JDK 11 如何从官网下载&#xff0c;及如何在 Windows 11 系统安装&#xff0c;下载与安装很简单&#xff0c;主要是有少许的细节需要说明。 一、JDK下载 …