基于开源ATmega8 无感BLDC程序移植到ATmega328PB

基于开源ATmega8 无感BLDC程序移植到ATmega328PB


  • 🔖基于Atmel Studio 7.0开发环境。
  • 🥕开源原项目资源地址:https://svn.mikrokopter.de/websvn/listing.php?repname=BL-Ctrl&path=%2F&
  • 📍原理图和PCB资源 BL-Ctrl v2.0 in Eagle(MEGA168与本移植项目原理图差异较大):https://github.com/Janesak1977/BLCTRL20_HW
  • 🌿BL-Ctrl官网BL-Ctrl V3、BL-Ctrl V2.0、BL-Ctrl V1.2资料介绍:https://wiki.mikrokopter.de/en/BL-Ctrl?action=show&redirect=en%2FBrushlessCtrl
  • 📜BL-Ctrl历史版本查询:https://wiki.mikrokopter.de/Ctrl_History

ATmega8 单片机和ATmega328PB差异不大,大部分寄存器无需修改,少数寄存器存在差异,硬件资源上,ATmega328PB资源外设比ATmega8多,价格上差不多。虽然采用的是8位单片机,与当今主流单片机,在性能上无法比拟。不影响学习研究。

  • ✨同驱动类型,性价比更高的,国内开源公开资料的有:基于STC8和STC32单片机梁工的相关无刷电机驱动工程,可从STCAI论坛获取相关资料。原理实现方法相同,应该也算是借鉴过来的,后起之作。
  • 📍STC梁工开源的三相无刷直流电机驱动资料地址:https://www.stcaimcu.com/forum.php?mod=viewthread&tid=1822&highlight=%E6%97%A0%E5%88%B7%E7%94%B5%E6%9C%BA&page=1&extra=#pid11784
  • 🎉此项目开源,同类型驱动控制里面,在当时算得上最早被公开的了。里面的部分经典算法一直被同类型驱动上所采用。
  • 📜按照此开源资料,如果想移植到其它单片机上,按照此反电动势检测实现方法,硬件要求是需要有一个模拟比较器。和需要能产生3路PWM信号来驱动H桥,这最基本的电机转动的要求。如果还需要电流采集以及ADC调速,那就需要单片机要有ADC资源。
  • 🌿早期相关ATmega328P相关移植参考文章:https://www.amobbs.com/thread-4652868-1-1.html
  • 🌼移植后基于自制驱动板,驱动转动效果:
    🌼
  • 📌自制驱动板相关内容《自制无感无刷电机驱动板》
  • ✒自己亲手移植一遍工程,不是简单的照搬一次代码,起码对整个无感无刷电机运转实现,有一个认识和了解,同时硬件上学习了2个单片机的资源外设,进行熟悉了一遍。
  • 🌿ATmega8 原理图:https://wiki.mikrokopter.de/en/BL-Ctrl_V1.2
    在这里插入图片描述
  • 🌿通过上面的BL-Ctrl_V1.2版本原理图移植到ATmega328PB,个人使用参考的源码是:V0.42:https://svn.mikrokopter.de/websvn/listing.php?repname=BL-Ctrl&path=%2Ftags%2FV0.42%2F&#a4bfcc0886576e3118d94460220fa558a
    在这里插入图片描述
  • 🌿ATmega328PB引脚连接参考上面的图纸:BL-Ctrl_V1.2版本原理图。相关1.2版本程序:https://github.com/jankae/brushlessControl
  • 🔨代码移植编译平台:Atmel Studio 7.0

⛳移植难点和重点

✨整个工程,基本都是围绕着,电机转动时,反电动势检测过零点内容的实现。对于PWM发波,都是比较容易配置和实现的。
  • 🌟ATmega8 和ATmega328PB自带的模拟比较器功能又和ADC功能相关寄存器共生,不能同时启用,导致需要功能切换时,需要及时改变相关寄存器位的配置。
📑基于反电动势检测过零点有两种方式:
  • 🥕基于 ADC 采样的无感方波电机控制 ADC 采样检测过零点。

  • 🥕基于比较器检测的无感方波电机控制 比较器检测过零点。(本项目采用的方式)

    • 🍁比较器检测过零点电路部分:

在这里插入图片描述

  • 📐理论计算和推导部分:
    在这里插入图片描述

📙移植到ATmega328PB,相关寄存器变更

  • 🌿外部时钟频率原来的8MHz换成16MHz。
  • 🌿TCCR2对应ATmega328PB相关寄存器TCCR0ATCCR0B
  • 🌿SFIOR对应ATmega328PB相关寄存器ADCSRB
  • 🌿参考电压差异,ATmega8 内部参考电压:2.56v,而ATmega328PB内部参考电压:1.1V。(个人移植使用的是AVCC作为参考电压即5V)
  • ✨在移植程序中需要处理的个别寄存器位差异,需要调整的地方比较多,如果自己移植,哪里报错有问题改哪里。

🛠3路PWM控制上桥臂说明

  • 🌿3路PWM控制上桥臂的信号频率个人采用的是15.625KHz.
  • 🌿引脚分别是:PB1、PB2、PB3
  • 🌿定时器通道选择和使用:
PB1  OC1A(定时器1,通道A)	 Mode: Normal top=0xFF
PB2  OC1B(定时器1,通道B)	 Mode: Normal top=0xFF
PB3  OC2A(定时器2,通道A)	 Mode: Normal top=0xFF
  • 🔧3路控制PWM信号核心代码:
#define PWM_A_ON  {TCCR1A=(1<<WGM10);TCCR1B=(1<<WGM12)| (1<<CS12) |(1<<CS10);TCCR2A=(1<<COM2A1)|(1<<WGM21) | (1<<WGM20);TCCR2B=(1<<CS22) | (1<<CS21) | (1<<CS20);  DDRB = 0x08;}//PB3

#define PWM_B_ON  {	TCCR1A =  (1<<COM1B1) | (1<<WGM10);TCCR1B= (1<<WGM12)|(1<<CS12)|(1<<CS10);TCCR2A= (1<<WGM21) | (1<<WGM20) ;TCCR2B=(1<<CS22) | (1<<CS21) | (1<<CS20); DDRB = (1<<DDB2);}//PB2

#define PWM_C_ON  {TCCR1A = (1<<COM1A1)|(1<<WGM10);TCCR1B=(1<<WGM12) |  (1<<CS12)|(1<<CS10);TCCR2A =(1<<WGM21)|(1<<WGM20);TCCR2B=  (1<<CS22) | (1<<CS21) | (1<<CS20); DDRB = (1<<DDB1);}//PB1
  • 🔑双路同时输出方式:
#define PWM_C_ON  {TCCR1A = (1<<COM1A1)|(1<<WGM10);TCCR1B=(1<<CS12);TCCR1C=(1<<FOC1A)|(1<<FOC1B);TCCR2A= (1<<WGM21)|(1<<WGM20);TCCR2B =(1<<FOC2A)|(1<<CS21);DDRB = 0x0A;}//PB3 ->OC2A	;PB1 ->OC1A	clkI/O/8
#define PWM_B_ON  {TCCR1A = (1<<COM1B1)|(1<<WGM10);TCCR1B=(1<<CS12);TCCR1C=(1<<FOC1A)|(1<<FOC1B); TCCR2A=(1<<COM2A1)|(1<<WGM21)|(1<<WGM20);TCCR2B =(1<<FOC2A)|(1<<CS21);DDRB = 0x0C;}//PB3 ->OC2A ; PB2 ->OC1B
#define PWM_A_ON  {TCCR1A =(1<<WGM10);TCCR1B=(1<<CS12);TCCR1C=(1<<FOC1A)|(1<<FOC1B);TCCR2A= (1<<COM2A1)|(1<<WGM21)|(1<<WGM20);TCCR2B = (1<<FOC2A)|(1<<CS21);DDRB = 0x08;}//PB3 ->OC2A
#define PWM_OFF   {TCCR1B &=~((1<<CS10)|(1<<CS11)|(1<<CS12));TCCR2B &=~((1<<CS20)|(1<<CS21)|(1<<CS22));PORTB &= ~0x0E;}

双路输出模式下,PWM_B_ON PWM_A_ON ,寄存器TCCR2A可以都配置定时器2的通道 A 的比较输出模式(1<<COM2A1),或者两者配置其一。推荐这两项控制,仅配置PWM_C_ON,其它参数不变的情况下,测试明显电流要小一些。

📘3路下桥臂采用IO开关控制

#define  STEUER_A_L {PORTD &= ~0x30; PORTD |= 0x08;}//U- ->PD3
#define  STEUER_B_L {PORTD &= ~0x28; PORTD |= 0x10;}//V- ->PD4
#define  STEUER_C_L {PORTD &= ~0x18; PORTD |= 0x20;}//W- ->PD5
  • 🔖以下内容待写……

📙ADC检测

  • 🔖ADC采集电流,是通过硬件蛇形布线实现的。具体看开源资料中的PCB文件。
    在这里插入图片描述
  • 📑ADC采集代码实现,进行ADC功能前,需要关闭模拟比较器,采集完ADC数据后,需要打开模拟比较器。两个不能同时启用。
//############################################################################
//Strom Analogwerte lesen
unsigned int MessAD(unsigned char channel)
//############################################################################
{
    unsigned char sense;
    sense = ADMUX;   // Sense-Kanal merken
    channel |= IntRef;
    ADMUX  =  channel;  // 开启对应ADC通道
    //SFIOR  =  0x00;  // Analog Comperator aus
    ADCSRB &= ~(1<<ACME);//关闭模拟比较器
//    GTCCR &=~((1<<PSRSYNC)|(1<<PSRASY));//将定时器2,0,1,3,4分频系数reset
//    MCUCR  &=~(1<<PUD);//禁用上拉电阻
//  ADC is enabled
    PRR0&= ~(1<<PRADC);
    ADMUX  =  channel;  //
    /*
     * initialize ADC:
     * Prescaler = 16 -> 1MHz,
     * Enable + Start
     */
    ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADPS2);
//等待转换完成
    while(ADCSRA & (0x01 << ADIF));    /* check if ADC conversion complete */
//   while (ADCSRA & (1 << ADSC));// wait for ADC to finish
    ADCSRA = 0x00;

    ADMUX = sense;   // zurück auf den Sense-Kanal
//   SFIOR = 0x08;  // Analog Comperator ein

    ADCSRB |= (1<<ACME);//打开模拟比较器
// ADC is disabled to preserve power
//    PRR0|= 1<<PRADC;
    ADCSRB |= (1<<ACME);//当ACME为1,且ADEN为0时可以选择ADC相关引脚为负输入
    ADCSRA &= ~_BV(ADEN);
    /*
    Bit 3 – ACIE Analog Comparator Interrupt Enable
    Bit 6 – ACBG Analog Comparator Bandgap Select
    */
    ACSR |= _BV(ACIE);
    ACSR &= ~_BV(ACBG);//当该位被清除时,AIN0被应用于模拟比较器的正输入。
//    GTCCR &=~((1<<PSRSYNC)|(1<<PSRASY));//将定时器2,0,1,3,4分频系数reset
    MCUCR  &=~(1<<PUD);//禁用上拉电阻

    return (ADCW);
}

📗三相反电动势检测比较器实现。

  • 📄三相反电动势检测,对应电机转动过程就的:消磁事件,过零事件,换相事件。(下面参考图来源于网络)
    在这里插入图片描述

🧲电机启动实现方式

  • 📝强制启动,代码实现:
//############################################################################
//电机启动
char Anwerfen(unsigned char pwm)
//############################################################################
{
    unsigned long timer = 200, i;//timer = 300
    DISABLE_SENSE_INT;//关闭模拟比较器中断
    PWM = 16;
    SetPWM();//T1和T2定时计数器赋值,设置pwm占空比
    /*补充注释:
    开环顺序换向算法,注意换向时必须同步修改比较器端口及触发沿
    以便在反相感生电动势到达切换条件时,自动切换到闭环运转状态
    */
    Manuell();//换相操作
    Delay_ms(200);
    /*
        MinUpmPulse = SetDelay(300);
        while (!CheckDelay(MinUpmPulse))
        {
            FastADConvert();
            if (Strom > 120)
            {
                STEUER_OFF; // 因短路而关闭
                RotBlink(10);
                printf("ADC7 Strom STOP\r\n");
                return (0);
            }
        }
    	*/
    PWM = pwm;

    while (1)
    {
        /*
                for (i = 0; i < timer; i++)
                {
                    if (!UebertragungAbgeschlossen)  SendUart();
                    else DatenUebertragung();
                    //  Wait(100);  // warten 8/8
                    Wait(25);//328p 16/64
                }
                DebugAusgaben();
                FastADConvert();
                if (Strom > 60)
                {
                    STEUER_OFF; // Abschalten wegen Kurzschluss
                    RotBlink(10);
                    return (0);
                }
        */
        timer -= timer / 15 + 1;
        if (timer < 25)
        {
            if (TEST_MANUELL) timer = 25;
            else return (1);
        }
        Manuell();//BLDC换相
        Phase++;
        Phase %= 6;
        AdConvert();
        PWM = pwm;
        SetPWM();
        if (SENSE)
        {
            PORTD ^= GRUEN;
        }
    }
}

//############################################################################
/*
开环顺序换向算法,注意换向时必须同步修改比较器端口及触发沿
以便在反相感生电动势到达切换条件时,自动切换到闭环运转状态
*/
void Manuell(void)//BLDC换相
//############################################################################
{
    switch (Phase)
    {
    case 0:
        STEUER_A_H;//U+
        STEUER_B_L;//V-
        SENSE_C;//比较器选择,通道 ADC2(PC2)作为负输入端
        SENSE_RISING_INT;//模拟比较器输出的上升沿产生中断
        break;
    case 1:
        STEUER_A_H;
        STEUER_C_L;
        SENSE_B;//比较器选择,通道 ADC1(PC1)作为负输入端
        SENSE_FALLING_INT;
        break;
    case 2:
        STEUER_B_H;
        STEUER_C_L;
        SENSE_A;//比较器选择,通道 ADC1(PC0)作为负输入端
        SENSE_RISING_INT;
        break;
    case 3:
        STEUER_B_H;
        STEUER_A_L;
        SENSE_C;
        SENSE_FALLING_INT;
        break;
    case 4:
        STEUER_C_H;
        STEUER_A_L;
        SENSE_B;
        SENSE_RISING_INT;
        break;
    case 5:
        STEUER_C_H;
        STEUER_B_L;
        SENSE_A;
        SENSE_FALLING_INT;
        break;
    }
}

  • 🌾在STC 无感BLDC代码中也有类似的启动代码:
/******************* 强制电机启动函数 ***************************/
void StartMotor(void)
{
	u16 timer,i;
	PIE = 0; NIE = 0;	// 关比较器中断,	PIE=1: 允许比较器上升沿中断, PIE=0: 禁止上升沿中断.	NIE=1: 允许比较器下降沿中断, NIE=0: 禁止下降沿中断.

	PWM_Value  = D_START_PWM;	// 初始占空比, 根据电机特性设置
	PWMA_CCR1H = (u8)(PWM_Value/256);
	PWMA_CCR1L = (u8)(PWM_Value%256);
	PWMA_CCR2H = (u8)(PWM_Value/256);
	PWMA_CCR2L = (u8)(PWM_Value%256);
	PWMA_CCR3H = (u8)(PWM_Value/256);
	PWMA_CCR3L = (u8)(PWM_Value%256);
	step = 0;	StepMotor();	Delay_n_ms(30);	// 初始位置
	step = 1;	StepMotor();	Delay_n_ms(20);	// 初始位置
	timer = 232;	//200电机启动

	while(1)
	{
		for(i=0; i<timer; i++)	delay_us(18);  //20根据电机加速特性, 最高转速等等调整启动加速速度
		timer -= timer /16;
		if(++step >= 6)	step = 0;
		StepMotor();
		if(timer < 40)	return;
	}
}


void StepMotor(void) // 换相序列函数
{
	switch(step)
	{
	case 0:  // AB  PWM1, PWM2_L=1
			PWMA_ENO = 0x00;	PWM1_L=0;	PWM3_L=0;
			Delay_500ns();
			PWMA_ENO = 0x01;		// 打开A相的高端PWM
			PWM2_L = 1;				// 打开B相的低端
			CMPEXCFG = 0;			// 比较器选择C相反电动势,  CMP+输入选择,0->P3.7(EMFC),1->P5.0(EMFA),2->P5.1(EMFB),3->ADCIN
			PIE = 0; NIE = 1;		// 比较器下降沿中断,	PIE=1: 允许比较器上升沿中断, PIE=0: 禁止上升沿中断.	NIE=1: 允许比较器下降沿中断, NIE=0: 禁止下降沿中断.
			break;
	case 1:  // AC  PWM1, PWM3_L=1
			PWMA_ENO = 0x01;	PWM1_L=0;	PWM2_L=0;	// 打开A相的高端PWM
			Delay_500ns();
			PWM3_L = 1;				// 打开C相的低端
			CMPEXCFG = 2;			// 比较器选择B相反电动势,  CMP+输入选择,0->P3.7(EMFC),1->P5.0(EMFA),2->P5.1(EMFB),3->ADCIN
			PIE = 1; NIE = 0;		// 比较器上升沿中断,	PIE=1: 允许比较器上升沿中断, PIE=0: 禁止上升沿中断.	NIE=1: 允许比较器下降沿中断, NIE=0: 禁止下降沿中断.
			break;
	case 2:  // BC  PWM2, PWM3_L=1
			PWMA_ENO = 0x00;	PWM1_L=0;	PWM2_L=0;
			Delay_500ns();
			PWMA_ENO = 0x04;		// 打开B相的高端PWM
			PWM3_L = 1;				// 打开C相的低端
			CMPEXCFG = 1;			// 比较器选择A相反电动势,  CMP+输入选择,0->P3.7(EMFC),1->P5.0(EMFA),2->P5.1(EMFB),3->ADCIN
			PIE = 0; NIE = 1;		// 比较器下降沿中断,	PIE=1: 允许比较器上升沿中断, PIE=0: 禁止上升沿中断.	NIE=1: 允许比较器下降沿中断, NIE=0: 禁止下降沿中断.
			break;
	case 3:  // BA  PWM2, PWM1_L=1
			PWMA_ENO = 0x04;	PWM2_L=0;	PWM3_L=0;	// 打开B相的高端PWM
			Delay_500ns();
			PWM1_L = 1;				// 打开C相的低端
			CMPEXCFG = 0;			// 比较器选择C相反电动势,  CMP+输入选择,0->P3.7(EMFC),1->P5.0(EMFA),2->P5.1(EMFB),3->ADCIN
			PIE = 1; NIE = 0;		// 比较器上升沿中断,	PIE=1: 允许比较器上升沿中断, PIE=0: 禁止上升沿中断.	NIE=1: 允许比较器下降沿中断, NIE=0: 禁止下降沿中断.
			break;
	case 4:  // CA  PWM3, PWM1_L=1
			PWMA_ENO = 0x00;	PWM2_L=0;	PWM3_L=0;
			Delay_500ns();
			PWMA_ENO = 0x10;		// 打开C相的高端PWM
			PWM1_L = 1;				// 打开A相的低端
			CMPEXCFG = 2;			// 比较器选择B相反电动势,  CMP+输入选择,0->P3.7(EMFC),1->P5.0(EMFA),2->P5.1(EMFB),3->ADCIN
			PIE = 0; NIE = 1;		// 比较器下降沿中断,	PIE=1: 允许比较器上升沿中断, PIE=0: 禁止上升沿中断.	NIE=1: 允许比较器下降沿中断, NIE=0: 禁止下降沿中断.
			break;
	case 5:  // CB  PWM3, PWM2_L=1
			PWMA_ENO = 0x10;	PWM1_L=0;	PWM3_L=0;	// 打开C相的高端PWM
			Delay_500ns();
			PWM2_L = 1;				// 打开B相的低端
			CMPEXCFG = 1;			// 比较器选择A相反电动势,  CMP+输入选择,0->P3.7(EMFC),1->P5.0(EMFA),2->P5.1(EMFB),3->ADCIN
			PIE = 1; NIE = 0;		// 比较器上升沿中断,	PIE=1: 允许比较器上升沿中断, PIE=0: 禁止上升沿中断.	NIE=1: 允许比较器下降沿中断, NIE=0: 禁止下降沿中断.
			break;

	default:
			break;
	}

	if(B_start)	// 启动时禁止下降沿和上升沿中断
	{
		CMPIF = 0;		//清除比较器中断标志
		PIE = 0; NIE = 0;		// 比较器上升沿中断,	PIE=1: 允许比较器上升沿中断, PIE=0: 禁止上升沿中断.	NIE=1: 允许比较器下降沿中断, NIE=0: 禁止下降沿中断.
	}
}

📓比较器中断代码

✨用于确定下一刻,应该换哪一相;以及设定下一次比较器触发电平模式。

//############################################################################
//
SIGNAL(TIMER2_COMPB_vect)	//SIG_OVERFLOW2 定时器2溢出
//############################################################################
{
}

//############################################################################
//SIG_COMPARATOR模拟比较器
// + Wird durch den Analogkomperator ausgelöst
// + Dadurch wird das Kommutieren erzeugt
SIGNAL(ANALOG_COMP_vect)
//############################################################################
{
    unsigned char sense = 0;
    do
    {
        if (SENSE_H) sense = 1;
        else sense = 0;
        switch (Phase)
        {
        case 0:
            STEUER_A_H;
            if (sense)
            {
                STEUER_C_L;
                if (ZeitZumAdWandeln) AdConvert();
                SENSE_FALLING_INT;
                SENSE_B;
                Phase++;
                CntKommutierungen++;
            }
            else
            {
                STEUER_B_L;
            }
            break;
        case 1:
            STEUER_C_L;
            if (!sense)
            {
                STEUER_B_H;
                if (ZeitZumAdWandeln) AdConvert();
                SENSE_A;
                SENSE_RISING_INT;
                Phase++;
                CntKommutierungen++;
            }
            else
            {
                STEUER_A_H;
            }

            break;
        case 2:
            STEUER_B_H;
            if (sense)
            {
                STEUER_A_L;
                if (ZeitZumAdWandeln) AdConvert();
                SENSE_C;
                SENSE_FALLING_INT;
                Phase++;
                CntKommutierungen++;
            }
            else
            {
                STEUER_C_L;
            }

            break;
        case 3:
            STEUER_A_L;
            if (!sense)
            {
                STEUER_C_H;
                if (ZeitZumAdWandeln) AdConvert();
                SENSE_B;
                SENSE_RISING_INT;
                Phase++;
                CntKommutierungen++;
            }
            else
            {
                STEUER_B_H;
            }


            break;
        case 4:
            STEUER_C_H;
            if (sense)
            {
                STEUER_B_L;
                if (ZeitZumAdWandeln) AdConvert();
                SENSE_A;
                SENSE_FALLING_INT;
                Phase++;
                CntKommutierungen++;
            }
            else
            {
                STEUER_A_L;
            }

            break;
        case 5:
            STEUER_B_L;
            if (!sense)
            {
                STEUER_A_H;
                if (ZeitZumAdWandeln) AdConvert();
                SENSE_C;
                SENSE_RISING_INT;
                Phase = 0;
                CntKommutierungen++;
            }
            else
            {
                STEUER_C_H;
            }
            break;
        }
    }
    while ((SENSE_L && sense) || (SENSE_H && !sense));
    ZeitZumAdWandeln = 0;
}

🔬移植初版工程源码

链接:https://pan.baidu.com/s/1kCNJCOLu4gqsP1KXwm9d3Q?pwd=6z15 
提取码:6z15

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Keli5烧写STM32程序时出现ST-LINK USB communication error错误(USB 通信错误)

1错误原图 2错误原因 前提驱动安装正确 原因1 usb接触不良&#xff08;极少出现&#xff09; 解决方法 更换USB线 还不行连下载器一起更换 原因2&#xff08;出现概率比较大&#xff09; 下载器的固件出现问题或下载器固件版本与Keli5的版本不匹配 解决方法 在Keli5的…

【python】python tkinter 计算器GUI版本(模仿windows计算器 源码)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

创建带有公共头部的Electron窗口

创建带有公共头部的Electron窗口 创建一个公共头部的html文件 1.我们在项目根目录创建一个名为app-header的文件夹 2.在app-header创建一个文件名为header.html的文件 结构如下&#xff1a; 基本结构和脚本如下 <body> <div class"header"><div c…

基于STM32+NBIOT(BC26)设计的物联网观赏鱼缸

文章目录 一、前言1.1 项目介绍【1】开发背景【2】项目实现的功能【3】项目模块组成 1.2 设计思路 二、(硬件控制端)硬件选型2.1 STM32开发板2.2 PCB板2.3 USB下载线2.4 NBIOT模块2.5 杜邦线&#xff08;2排&#xff09;2.6 稳压模块2.7 电源插头2.8 水温检测传感器2.9 水质检测…

Python 中别再用 ‘+‘ 拼接字符串了!

当我开始学习 Python 时&#xff0c;使用加号来连接字符串非常直观和容易&#xff0c;就像许多其他编程语言&#xff08;比如Java&#xff09;一样。 然而&#xff0c;很快我意识到许多开发者似乎更喜欢使用.join()方法而不是。 在本文中&#xff0c;我将介绍这两种方法之间的…

计算机网络(1

网络初识 目录 网络初识一. 网络分类1. 局域网LAN(Local Area Network):2. 广域网WAN(Wide Area Network): 二. 组建网络的基础设备1. 路由器2. 交换机 三. 标识符 协议 (protocol)一. 协议分层1. 分层的好处2. OSI七层分层3. TCP/IP五层模型(或四层) 模型(1. 物理层(可不算)(2…

从零开始:手把手教你使用Python实现PDF到Excel的转换

来百 在日常工作和学习中&#xff0c;我们经常会遇到需要将PDF文件中的数据提取到Excel表格中的情况。可能是为了进行数据分析、报告生成或者其他目的。虽然手动复制粘贴是一种方法&#xff0c;但对于大量的数据来说&#xff0c;这种方式显然效率太低。幸运的是&#xff0c;Py…

摸鱼大数据——Hive基础理论知识——Hive基础架构

1、Hive和MapReduce的关系 1- 用户在Hive上编写数据分析的SQL语句&#xff0c;然后再通过Hive将SQL语句翻译成MapReduce程序代码&#xff0c;最后提交到Yarn集群上进行运行 2- 大家可以将Hive理解成有道词典&#xff0c;帮助你翻译英文 2、Hive架构 用户接口: 包括 CLI、JDBC/…

ISCC——AI

得到一个T4.pyc 回编译一下 得到下面代码 import base64def encrypt_and_compare(user_input, offset_str, target_base64):if len(user_input) ! 24:return Please enter a string with a length of 24encrypted Nonefor i, char in enumerate(user_input):offset int(off…

自然资源-各级国土空间总体规划的审查要点及流程总结

自然资源-各级国土空间总体规划的审查要点及流程总结 国土空间规划是对一定区域国土空间开发保护在空间和时间上作出的安排&#xff0c;包括总体规划、详细规划和相关专项规划。 国土空间规划管理是国土空间规划中重要的一环。中共中央、国务院发布《关于建立国土空间规划体系…

C++ Primer Plus第十八章复习题

1、使用用大括号括起的初始化列表语法重写下述代码。重写后的代码不应使用数组ar。 class z200 { private:int j;char ch;double z; public:Z200(int jv,char chv&#xff0c;zv) : j(jv), ch (chv), z(zv){} };double x 8.8; std::string s "what a bracing effect ! …

添砖Java(十一)——常见类的使用Object,Math,System,BigDeciaml,包装类

目录 object&#xff1a; toString&#xff1a; equals: ​编辑 Math&#xff1a;​编辑 System: BigDecimal: 基本数据的包装类&#xff1a;​编辑 object&#xff1a; 我们知道&#xff0c;所有的类都是间接或直接继承了object类。然后object里面有几个用得很多的方法…

使用printf的两种方法,解决printf不能使用的问题

使用printf的两种方法&#xff0c;解决printf不能使用的问题 一、微库法 我们使用printf前要加上重定向fputc //重定义fputc函数 int fputc(int ch, FILE *f) { while((USART1->SR&0X40)0);//循环发送,直到发送完毕 USART1->DR (uint8_t) ch; return…

【JVM实践与应用】

JVM实践与应用 1.类加载器(加载、连接、初始化)1.1 类加载要完成的功能1.2 加载类的方式1.3 类加载器1.4 双亲委派模型1.5自定义ClassLoader1.6 破坏双亲委派模型2.1 类连接主要验证内容2.2 类连接中的解析2.3 类的初始化3.1 类的初始化时机3.2 类的初始化机制和顺序3.2 类的卸…

RedHat9 | DNS剖析-配置辅助DNS服务器

一、实验环境 1、辅助域名DNS服务器 DNS通过划分为若干个区域进行管理&#xff0c;每一个区域由1台或多台DNS服务器负责解析&#xff0c;如果仅仅采用1台DNS服务器&#xff0c;在DNS服务器出现故障后&#xff0c;用户将无法完成解析。 辅助DNS服务器的优点 容灾备份&#x…

Star-CCM+中滞环的实现—场平均监视

前言 前文有介绍过通过更新事件来实现滞环,但是更新事件的方法比较繁琐,应用起来比较困难,本文将介绍一种简单的方法来实现滞环功能。即通过场平均监视来实现,具体方法如下: 一 创建场平均监视 首先在检测量下创建“场平均监视”,具体操作步骤如下图所示: 二 创建质量…

企业客户信息反馈|基于SprinBoot+vue的企业客户信息反馈平台(源码+数据库+文档)

企业客户信息反馈平台 目录 基于SprinBootvue的企业客户信息反馈平台 一、前言 二、系统设计 三、系统功能设计 1平台功能模块 2后台登录 5.2.1管理员功能 5.2.2客户功能 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&am…

如何使用DotNet-MetaData识别.NET恶意软件源码文件元数据

关于DotNet-MetaData DotNet-MetaData是一款针对.NET恶意软件的安全分析工具&#xff0c;该工具专为蓝队研究人员设计&#xff0c;可以帮助广大研究人员轻松识别.NET恶意软件二进制源代码文件中的元数据。 工具架构 当前版本的DotNet-MetaData主要由以下两个部分组成&#xf…

matlab 使用Otsu方法计算图像全局阈值

目录 一、概述1、算法概述2、主要函数3、参考文献二、代码实现三、结果展示四、参考链接本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、概述