leekom

  • 2024-09-29
  • 回复了主题帖: 昊芯HXS320F28034数字信号处理器项目中电机相位控制与检测调试

    后续更新测量与FPGA监测方法

  • 2024-09-11
  • 发表了主题帖: 昊芯HXS320F28034数字信号处理器项目中电机相位控制与检测调试

    本帖最后由 leekom 于 2024-9-11 16:29 编辑 昊芯HXS320F28034数字信号处理器DSP,对电机使用差分编码器,也称为增量式编码器,其工作原理是将位移转换成周期性的电信号,再将这 个电信号转变成计数脉冲,用脉冲的个数表示位移的大小。 其QEP测速的实现方式如下: 采用GPIO MUX定义引脚:其引脚对应在码盘的边缘上开有相等角度的缝隙(分为透明和不透明部分),在开缝码盘两边分别安装光源及光敏元 件。当码盘随工作轴一起转动时,每转过一个缝隙就产生一次光线的明暗变化,从而获取差分编码器正交脉冲QEPA与QEPB、索引QEPI信号; 通过QDECCTL[XCR]选择,上升沿、下降沿或双边沿捕获计数方式,得到时钟信号xCLK,变化的信号经过整形放大,可以得到一定幅值和功率的 电脉冲输出信号。脉冲数就等于转过的缝隙数。将该脉冲信号送到计数器中去进行计数,从测得的数码数就能知道码盘转过的角度; 经解码QDU逻辑处理,得到方向的高低电平信号iDIR; 通过QDECCTL[QSRC]选择引脚模式:正交、方向、增或减计数,读取方向QDIR、时钟QCLK信号; 通过QEPCTL[PCRM]选择,位置计数控制PCCU模式:索引位置事件、最大位置锁存、单位超时锁存;使能QEPCTL[QPEN],捕获N线光电编码器 输出双边沿脉冲,使位置计数器产生计数,从而计算电机转速。 其A\B\C对应差分编码器,通过检测码盘上的缝隙引起的光线明暗变化,将其转换为电脉冲信号,并通过计数器计算脉冲数量来测量位移大小。 同时,通过两套光电转换装置产生的相位差信号来判断旋转方向,常用方法如下: (1)T法:一个脉冲周期fclk内,捕获脉冲计数,载入16位定时器QCTMR。单位事件发生时,更新载入QCPRDLAT,电机转速n=60*fclk/(4N*QCPRDLAT)。QCTMR上溢时,位置方向QDIR将变化,使捕获单元QEPSTS[COEF]发生错误标志,故仅适用于低速测量。 (2)M法:规定检测事件Tc内,捕获脉冲计数。单位事件发生时,锁存到32位QPOSILAT和QPOSSLAT,故电机转速n=60*QPOSSLAT/(4N*Tc),适用于高速测量。 由此设计QEP测速实例:EPWM配置1KHz PWM波,模拟1000线编码器旋转,EQEP采用M法与T法测量60rpm与6000rpm下旋转的转速,单位周期为 SysFreq*100,采用上升沿计数,捕获得到脉冲计数,实现转速测量,采用定时器中断实现三种不同功能切换,故硬件连接如下:GPIO0-GPIO20,GPIO1-GPIO21。基于以上分析,在CDK上开发QEP脉冲捕获输出,代码包括:EPWM与EQEP的GPIO引脚配置,EPWM 1KHz两路互差90°的PWM波输出配置,EQEP单位时间锁存计数捕获功能配置,定时器Timer0配置,计数捕获、M法与T法测速程序及其功能切换的定时器中断服务程序,主程序调用执行。 int main(void) { /*初始化系统控制*/ InitSysCtrl(); /*初始EPWM GPIO*/ InitEPwm1Gpio(); /*EQEP1引脚配置*/ InitEQep1Gpio(); /*初始化LED配置,用于指示实际转速是否到达给定转速*/ InitLED(); /*关中断*/ InitPieCtrl(); /*清中断*/ IER = 0x0000; IFR = 0x0000; /*初始化PIE中断向量表*/ InitPieVectTable(); /*定时器配置*/ Timer0_init(); EALLOW; /*将timer0_ISR入口地址赋给TINT0,执行M法、T法测速切换中断服务程序*/ PieVectTable.TINT0 = &timer0_ISR; EDIS; /*电机转速初始化*/ MotorSpeed_init(); EALLOW; /*禁用TBCLK同步,允许EPWM初始化配置*/ SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0; EDIS; /*配置epwm1,用于模拟编码器旋转*/ EPWM1_Config(); EALLOW; /*使能TBCLK同步,EPWM配置功能起作用*/ SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1; EDIS; /*EQEP脉冲捕获配置*/ EQEP_pulseCap(); /*使能打开相应的CPU IER中断*/ IER |= M_INT1; /*使能打开相应的PIE IER中断*/ PieCtrlRegs.PIEIER1.bit.INTx7 = 1; /*使能打开全局中断*/ EINT; while(1) {} return 0; } A\B\C三个相位如下图: CDK上开发QEP测速程序,其编译结果为: 编译通过后,就可以开始调试了,其中M法测速调试结果如下: T法测速调试结果如下: 所捕获的光电编码器QEP正交脉冲波形为    

  • 2024-08-28
  • 发表了主题帖: 【中科昊芯HXS320F28025C】昊芯HXS320F28034数字信号处理器CAN调试

    昊芯HXS320F28034数字信号处理器DSP,其CAN模块以Mailbox信箱MBOXn控制与传输数据,实现DSP之间的控制指令高效收发,可更有效助力于工程师实现高效的多DSP控制指令收发,广泛应用于电动汽车、风力发电、轨道交通、机器人等高复杂度控制领域。 本章节以HX2000系列芯片调试 USB_CAN通信收发实例讲解CAN网络通信功能。 HX2000系列CAN模块原理如下图,通过CANMC[CCR]使能进入初始化配置模式,等待CANMC[CCE]置高写入CANBTC位配置波特率;等待CANMC[CCE]拉低,完成初始化; int main(void) { InitSysCtrl();//系统时钟初始化 CAN_Init();//CAN的初始化参数配置 InitECanaGpio();//CAN的Gpio引脚配置 EALLOW; ECanaRegs.CANMIM.bit.MIM0=1;//打开接收掩码,接收到数据触发中断 ECanaRegs.CANMIL.bit.MIL0 = 0; // 选择EcanA中断0 ECanaRegs.CANGIM.bit.I0EN = 1; // 使能中断0 PieVectTable.ECAN0INTA = &eCanRxIsr; // CANA 0接收中断入口 EDIS; PieCtrlRegs.PIEIER9.bit.INTx5 = 1; // 使能ECAN1中断 IER |= M_INT9; // Enable CPU INT9 EINT; while(1){ CAN_Tx();//发送数据 } return 0; } 其中,CAN的初始化参数配置代码为: void CAN_Init() { volatile struct ECAN_REGS ECanaShadow; EALLOW; /* 配置RX与TX引脚*/ ECanaShadow.CANTIOC.all = P_ECanaRegs->CANTIOC.all; ECanaShadow.CANTIOC.bit.TXFUNC = 1; P_ECanaRegs->CANTIOC.all = ECanaShadow.CANTIOC.all; ECanaShadow.CANRIOC.all = P_ECanaRegs->CANRIOC.all; ECanaShadow.CANRIOC.bit.RXFUNC = 1; P_ECanaRegs->CANRIOC.all = ECanaShadow.CANRIOC.all; /* 清RMPn, GIFn 位 */ P_ECanaRegs->CANRMP.all = 0xFFFFFFFF; /* 清中断标志 */ P_ECanaRegs->CANGIF0.all = 0xFFFFFFFF; P_ECanaRegs->CANGIF1.all = 0xFFFFFFFF; /* 初始化配置,写入CAN波特率*/ ECanaShadow.CANMC.all = P_ECanaRegs->CANMC.all; ECanaShadow.CANMC.bit.CCR = 1 ; // Set CCR = 1 P_ECanaRegs->CANMC.all = ECanaShadow.CANMC.all; ECanaShadow.CANES.all = P_ECanaRegs->CANES.all; do { ECanaShadow.CANES.all = P_ECanaRegs->CANES.all; } while(ECanaShadow.CANES.bit.CCE != 1 ); // Wait for CCE bit to be set.. //Bit rate=SYSCLKOUT/2/{(BRP+1)*[(TSEG1+1)+(TSEG2+1)+1, //TSEG1≥3,TSEG2≥2 ECanaShadow.CANBTC.bit.BRPREG = 2; ECanaShadow.CANBTC.bit.TSEG2REG = 4; ECanaShadow.CANBTC.bit.TSEG1REG = 13; P_ECanaRegs->CANBTC.all = ECanaShadow.CANBTC.all; ECanaShadow.CANMC.all = P_ECanaRegs->CANMC.all; ECanaShadow.CANMC.bit.CCR = 0 ; // Set CCR = 0 P_ECanaRegs->CANMC.all = ECanaShadow.CANMC.all; ECanaShadow.CANES.all = P_ECanaRegs->CANES.all; do { ECanaShadow.CANES.all = P_ECanaRegs->CANES.all; } while(ECanaShadow.CANES.bit.CCE != 0 ); // Wait for CCE bit to be cleared.. /* 屏蔽所有信箱寄存器*/ // Required before writing the MSGIDs P_ECanaRegs->CANME.all = 0; /* 配置MBOX1的MSGID*/ P_ECanaMboxes->MBOX1.MSGID.all = 0x00040000; P_ECanaMboxes->MBOX0.MSGID.all = 0x00040000; /* 配置 MBOX1/MBOX0的传输字节为2 */ ECanaMboxes.MBOX1.MSGCTRL.bit.DLC = 0x2; ECanaMboxes.MBOX0.MSGCTRL.bit.DLC = 0x2; /* 配置 MBOX0 为接收,MBOX1为发送 */ ECanaRegs.CANMD.bit.MD0=1; ECanaRegs.CANMD.bit.MD1=0; /*使能 MBOX0 and MBOX1 */ ECanaRegs.CANME.bit.ME0=1; ECanaRegs.CANME.bit.ME1=1; //挂起接收邮箱,以触发接收中断 if(ECanaRegs.CANRMP.bit.RMP0==0) { ECanaRegs.CANRMP.bit.RMP0=1; } EDIS; return; } 通过Mailbox邮箱MBOXn.MSGCTRL的RTR位配置远程传输请求,TPL位配置MBOXn传输优先级,DLC位配置传输0~8个字节,传输过程如下: 1.CAN总线通过CAN传输芯片向CAN发送数据,使能CANRIOC[RXFUNC]打开接收线,接收来自GPIOMUX所定义CANRX接收引脚上的数据,装到缓冲区Receive Buffer; 2.通过CANMD[MDn]配置Mailbox邮箱MBOXn为接收,使能CANME[MEn]打开邮箱MBOXn,接收来自缓冲区中的数据; 全局中断标志的设置取决于CANGIM寄存器中GIL位的设置。如果设置了该位,则全局中断在CANGIF1寄存器中设置位;否则,在CANGIF0寄存器中设置。通过CANMIM[MIMn]配置接收掩码中断使能;通过CANMIL[MILn]配置,将MBOXn接收中断,映射到中断ECAN0INTA或ECAN1INTA;使能CANGIM[I0EN/I1EN]打开中断信号,MBOXn接收到数据时,将产生一个接收中断,使CANGIF0/1[MIV0/1]中相应接收邮箱MBOXn的邮箱号置位;通过PIE响应CPU执行接收中断程序: EEWORLDIMGTK0 (1)置位CANRMP[RMPn]挂起MBOXn邮箱,以防止瞬间接收到多组数据; (2)通过CANGIF0/1[MIV0/1],确认接收到数据邮箱为MBOXn,并读取接收的数据; 置位CANRMP[RMPn]清除邮箱MBOXn挂起状态,准备接收下一组数据。 CAN的发送程序代码为: void CAN_Tx(void) { //等待MBOX0接收成功,读取消息 while(ECanaRegs.CANRMP.bit.RMP0!=1){} ECanaMboxes.MBOX1.MDL.byte.BYTE0 = (receive_data&0xff); ECanaMboxes.MBOX1.MDL.byte.BYTE1 = ((receive_data>>8)&0xff); ECanaRegs.CANTRS.bit.TRS1 = 1;//发送MBOX1数据到MBOX0 } 作者:中科昊芯 https://www.bilibili.com/read/cv18742715/?spm_id_from=333.999.0.0 出处:bilibili EEWORLDIMGTK1 4.通过CANMD[MDn]配置另一Mailbox邮箱MBOXn为发送,使能CANME[MEn]打开发送邮箱,使能CANTRS[TRSn]启动发送邮箱,发送数据到缓冲区 Transmit Buffer; 5.使能CANTIOC[TXFUNC]打开发送线,通过GPIOMUX所定义CANTX发送引脚将缓冲区Transmit Buffer数据发送到CAN总线。 

  • 2024-08-14
  • 发表了主题帖: HXX320中pwm的占空比设置

           HXX320和STM32有不同,要配置可定义的PWM,其完成是内部分硬件方式。只需对相关的硬件设定,其DSP将协同处理。       首先,初始化完成后,确定相关的设定库。        TIM_TimeBaseStructure.TIM_Period设为TIMx的时钟,除非APB1的时钟分频数设置为1,否则通用定时器TIMx的时钟是APB1时钟的2倍,这时的TIMx时钟为72MHz,用这个TIMx时钟72MHz除以(PSC+1),得到定时器每隔多少秒涨一次,这里给PSC赋7199,计算得定时器每隔0.0001秒涨一次,即此时频率为10KHz,再把这个值乘以(ARR+1)得出PWM频率。        此例,假如ARR值为0,即0.0001*(0+1),则输出PWM频率为10KHz,再假如输出频率为100Hz的PWM,则将ARR寄存器设置为99即可。如果想调整PWM占空比精度,则只需降低PSC寄存器的值即可。设为100TIM_SetCompare2(20),就是20%占空比了。   相关代码下:   void TIM_Configuration(void)//TIM初始化函数 {   TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;//定时器初始化结构   TIM_OCInitTypeDef  TIM_OCInitStructure;//通道输出初始化结构      //TIM1初始化   TIM_DeInit(TIM2);   TIM_TimeBaseStructure.TIM_Period = 719;                  //周期719   TIM_TimeBaseStructure.TIM_Prescaler = 999;              //时钟分频     TIM_TimeBaseStructure.TIM_ClockDivision = 0;           //时钟分割点   TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//PWM模式   TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);   //基本初始化   // TIM_ITConfig(TIM2, TIM_IT_CC4, ENABLE);           //打开中断,中断需要这行代码      //TIM1通道初始化   TIM_OCStructInit(& TIM_OCInitStructure);                             //默认参数   TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;                    //工作状态,;//配置为PWM1模式   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;         //设定为输出,需要PWM输出才需要这行代码   TIM_OCInitStructure.TIM_Pulse = 800;                                 //占空长度   TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;                 //高电平   TIM_OC4Init(TIM2, &TIM_OCInitStructure);                         //使用P4脚   TIM_Cmd(TIM2, ENABLE);                                                   //启动TIM1 } 输出的信号周期为5ms,单个周期内高电平持续时间为0.7ms; 见图    

  • 2024-07-24
  • 发表了主题帖: 【中科昊芯】国产RISCV+DSP开发之准备

    最近申请到中科昊芯RISCV DSP测试样板,正好手头在做关于电机位置精准备控制相关项目。本想用RSIV片上系统与FPGA结合,做下高精控制,看来这颗片子可以一刀切。 先看看主要性能参数:      和STM相比,看到差别了,有一些增强型外设,这些也就是为了区分普通MCU方式,毕竟内嵌DSP,高速的高阶数学算法应该有极大提升。   相关资料网站    www.haawking.cn/280025        准备下资料,开始读文档了解,希望资料准确易懂。  

  • 2024-07-03
  • 回复了主题帖: 测评入围名单: 中科昊芯HXS320F28025C,基于RISC-V的DSP

    个人信息无误,确认可以完成测评分享计划

  • 2024-05-29
  • 回复了主题帖: 《Python编程快速上手》Python中 if-elif-else与switch-case的执行

  • 发表了主题帖: 《Python编程快速上手》Python中 if-elif-else与switch-case的执行

           在Python中,没有内置的switch-case语句,但你可以使用字典映射或者if-elif-else链来模拟switch-case的行为。        关于执行速度,通常来说,两者的性能差异非常小,不太可能成为程序执行的瓶颈。实际的性能差异更多地依赖于具体的使用场景、条件分支的数量、以及每个分支内的代码复杂度。       1、 IF-ELSE对于简单的条件判断,if-elif-else结构直观且易于理解。        Python解释器对这类基本结构做了优化,因此在大多数情况下性能是足够的。        value = 2        if value == 1:        print("Case 1")        elif value == 2:        print("Case 2")        elif value == 3:        print("Case 3")        else:        print("Default case")          2、 字典映射(模拟switch-case)使用字典映射可以提供一种更“Pythonic”的方式来处理多分支选择,这种方式在某些情况下可能更简洁,尤其是在处理大量分支时。          def case_1():       return "Case 1"       def case_2():       return "Case 2"       def case_3():       return "Case 3"      def default_case():      return "Default case" switch = {      1: case_1,      2: case_2,      3: case_3, } value = 2 result = switch.get(value, default_case)() print(result)             执行速度比较理论上,字典查找(模拟switch-case)的平均时间复杂度为O(1),而if-elif-else链的效率会随着条件数量的增加而稍微降低,尽管这种差异在实际应用中往往微乎其微,不足以成为决定使用哪种结构的主要因素。实际上,选择if-elif-else还是字典映射主要应基于代码的可读性、可维护性和具体需求。对于简单的逻辑和较少的分支,if-elif-else可能更直接;而对于复杂的多分支逻辑,尤其是当分支条件是固定值集合时,字典映射可能提供更清晰、更高效的实现方式。在绝大多数日常开发中,这两种方式的性能差异并不显著到需要特别考虑的程度。

  • 回复了主题帖: Python宏定义实现

  • 发表了主题帖: Python宏定义实现

             虽然Python本身不支持C/C++风格的预处理器宏定义,但可以通过一些第三方库或技巧来模拟宏的功能,尤其是在处理一些元编程或者代码生成的场景。以下是一些方法,可以用来实现类似“宏”的效果:      1. 使用functools.wraps和装饰器          装饰器可以用于在不修改原有函数定义的情况下,给函数添加额外的功能,这在某种程度上类似于宏的功能。           from functools import wraps        def log_function_call(func):        @wraps(func)        def wrapper(*args, **kwargs):         print(f"Calling function: {func.__name__}")         return func(*args, **kwargs)        return wrapper         @log_function_call       def example_function(x):       return x * x        print(example_function(5))    2. 使用元类(Metaclass)     元类允许你控制类的创建过程,从而在类定义时插入代码或修改行为,类似于在编译时期进行的宏替换或扩展。        class Meta(type):     def __new__(cls, name, bases, dct):         print(f"Creating class: {name}")         # 在这里可以修改dct来添加、删除或修改类的属性和方法         dct['created_at'] = 'now'         return super().__new__(cls, name, bases, dct)      class MyClass(metaclass=Meta):     pass      print(MyClass.created_at)    3. 通过字符串操作和exec模拟宏     虽然不推荐频繁使用,但在某些特定场景下,如动态生成代码,可以使用exec函数执行字符串形式的Python代码。   code = "PI = 3.14159" exec(code) print(PI)   通过以上三个方法可以灵活实现很多类似的需求。

  • 2024-05-11
  • 发表了日志: Python位运算

  • 发表了主题帖: Python位运算

    读到Python位运算这部分,将我理解与大家分享,Python中的位运算允许你在二进制层面操作数字,相较关系运算,理为接近某些底层编程、算法优化或是处理位级别数据时使用。 位运算符主要有以下几种: 按位与运算符 & 这个运算符对两个数字的每一位进行逻辑与操作。如果两个相应的位都是1,则结果位上就是1;否则为0。 a = 5 # 二进制表示为 0101 b = 3 # 二进制表示为 0011 c = a & b # 结果为 1,二进制表示为 0001 按位或运算符 | 对于两个数字的每一位,如果至少有一个位是1,那么结果位上就是1;如果都是0,则结果为0。 a = 5 # 二进制表示为 0101 b = 3 # 二进制表示为 0011 c = a | b # 结果为 7,二进制表示为 0111   按位异或运算符 ^ 当两个位不同时,结果位为1;如果相同,则为0。 a = 5 # 二进制表示为 0101 b = 3 # 二进制表示为 0011 c = a ^ b # 结果为 6,二进制表示为 0110 按位取反运算符 ~ 对数据的每个二进制位取反,即0变成1,1变成0。需要注意的是,这个运算符通常会得到一个负数,因为Python中整数是以补码形式存储的。 a = 5 # 二进制表示为 0101,取反后为 ...11111010,补码表示为 -6 b = ~a # 结果为 -6 左移运算符 << 将数字的二进制表示向左移动指定位数,右边空出来的位用0填充。 a = 5 # 二进制表示为 0101 b = a << 2 # 结果为 20,二进制表示为 10100 右移运算符 >> 将数字的二进制表示向右移动指定位数,左边空出来的位根据原数字的符号位决定,正数补0,负数补1(算术右移)。 a = 20 # 二进制表示为 10100 b = a >> 2 # 结果为 5,二进制表示为 0101   在某些需要基于机器层面的数椐处理时用到,以上为部分总结与理解,不足之处欢迎大佬指点。  

  • 2024-04-16
  • 回复了主题帖: 读书入围名单:《Python编程快速上手 让繁琐工作自动化 第2版》

    个人信息无误,确认可以完成阅读分享计划。

  • 回复了主题帖: 读书入围名单:《Python编程快速上手 让繁琐工作自动化 第2版》

    幸运入围,希望学习有惊喜,期待ing......

发布的博客

最近访客

< 1/1 >

统计信息

已有36人来访过

  • 芯积分:31
  • 好友:--
  • 主题:7
  • 回复:6

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言