[#0x001F] finally总是会被执行

  finally总是会被执行,即使try中有break、continue、return这样的语句,如:

public class FinallyTest
{
	public static void main(String[] args)
	{
		int i;
		
		for (i = 0; ;i++)
		{
			try
			{
				if (i == 0)
					continue;
				else if (i == 1)
					break;
			}
			finally 
			{
				System.out.println("loop " + i + " ends");
			}
		}
		
		try
		{
			return;
		}
		finally
		{
			System.out.println("return ends");
		}
	}
}

//output
/*
	loop 0 ends
	loop 1 ends
	return ends
*/

 

  另外,从这个例子可以看出,不写catch,直接try-finally配对也是可以的。

(.venv) root@lujh:/xhs-toolkit# python test.py Traceback (most recent call last): File "/xhs-toolkit/test.py", line 12, in <module> driver = webdriver.Chrome(options=options) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/xhs-toolkit/.venv/lib/python3.12/site-packages/selenium/webdriver/chrome/webdriver.py", line 47, in __init__ super().__init__( File "/xhs-toolkit/.venv/lib/python3.12/site-packages/selenium/webdriver/chromium/webdriver.py", line 69, in __init__ super().__init__(command_executor=executor, options=options) File "/xhs-toolkit/.venv/lib/python3.12/site-packages/selenium/webdriver/remote/webdriver.py", line 257, in __init__ self.start_session(capabilities) File "/xhs-toolkit/.venv/lib/python3.12/site-packages/selenium/webdriver/remote/webdriver.py", line 356, in start_session response = self.execute(Command.NEW_SESSION, caps)["value"] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/xhs-toolkit/.venv/lib/python3.12/site-packages/selenium/webdriver/remote/webdriver.py", line 447, in execute self.error_handler.check_response(response) File "/xhs-toolkit/.venv/lib/python3.12/site-packages/selenium/webdriver/remote/errorhandler.py", line 232, in check_response raise exception_class(message, screen, stacktrace) selenium.common.exceptions.SessionNotCreatedException: Message: session not created from unknown error: no chrome binary at /usr/bin/google-chrome Stacktrace: #0 0x561016d4067a <unknown> #1 0x56101680ccd0 <unknown> #2 0x561016843947 <unknown> #3 0x561016841d58 <unknown> #4 0x561016891464 <unknown> #5 0x561016890b86 <unknown> #6 0x5610168829f3 <unknown> #7 0x56101684f534 <unknown> #8 0x561016850191 <unknown> #9 0x561016d07eef <unknown> #10 0x561016d0bf98 <unknown> #11 0x561016cef0f2 <unknown> #12 0x561016d0c73e <unknown> #13 0x561016cd60be <unknown> #14 0x561016d2ec68 <unknown> #15 0x561016d2ee78 <unknown> #16 0x561016d3f11c <unknown> #17 0x778646c94ac3 <unknown>
06-25
/* * linux/arch/arm/kernel/head.S * * Copyright (C) 1994-2002 Russell King * Copyright (c) 2003 ARM Limited * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Kernel startup code for all 32-bit CPUs */ #include <linux/linkage.h> #include <linux/init.h> #include <asm/assembler.h> #include <asm/cp15.h> #include <asm/domain.h> #include <asm/ptrace.h> #include <asm/asm-offsets.h> #include <asm/memory.h> #include <asm/thread_info.h> #include <asm/pgtable.h> #if defined(CONFIG_DEBUG_LL) && !defined(CONFIG_DEBUG_SEMIHOSTING) #include CONFIG_DEBUG_LL_INCLUDE #endif /* * swapper_pg_dir is the virtual address of the initial page table. * We place the page tables 16K below KERNEL_RAM_VADDR. Therefore, we must * make sure that KERNEL_RAM_VADDR is correctly set. Currently, we expect * the least significant 16 bits to be 0x8000, but we could probably * relax this restriction to KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4000. */ #define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET) #if (KERNEL_RAM_VADDR & 0xffff) != 0x8000 #error KERNEL_RAM_VADDR must start at 0xXXXX8000 #endif #ifdef CONFIG_ARM_LPAE /* LPAE requires an additional page for the PGD */ #define PG_DIR_SIZE 0x5000 #define PMD_ORDER 3 #else #define PG_DIR_SIZE 0x4000 #define PMD_ORDER 2 #endif .globl swapper_pg_dir .equ swapper_pg_dir, KERNEL_RAM_VADDR - PG_DIR_SIZE .macro pgtbl, rd, phys add \rd, \phys, #TEXT_OFFSET - PG_DIR_SIZE .endm /* * Kernel startup entry point. * --------------------------- * * This is normally called from the decompressor code. The requirements * are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0, * r1 = machine nr, r2 = atags or dtb pointer. * * This code is mostly position independent, so if you link the kernel at * 0xc0008000, you call this at __pa(0xc0008000). * * See linux/arch/arm/tools/mach-types for the complete list of machine * numbers for r1. * * We&#39;re trying to keep crap to a minimum; DO NOT add any machine specific * crap here - that&#39;s what the boot loader (or in extreme, well justified * circumstances, zImage) is for. */ .arm __HEAD ENTRY(stext) THUMB( adr r9, BSYM(1f) ) @ Kernel is always entered in ARM. THUMB( bx r9 ) @ If this is a Thumb-2 kernel, THUMB( .thumb ) @ switch to Thumb now. THUMB(1: ) #ifdef CONFIG_ARM_VIRT_EXT bl __hyp_stub_install #endif @ ensure svc mode and all interrupts masked safe_svcmode_maskall r9 mrc p15, 0, r9, c0, c0 @ get processor id bl __lookup_processor_type @ r5=procinfo r9=cpuid movs r10, r5 @ invalid processor (r5=0)? THUMB( it eq ) @ force fixup-able long branch encoding beq __error_p @ yes, error &#39;p&#39; #ifdef CONFIG_ARM_LPAE mrc p15, 0, r3, c0, c1, 4 @ read ID_MMFR0 and r3, r3, #0xf @ extract VMSA support cmp r3, #5 @ long-descriptor translation table format? THUMB( it lo ) @ force fixup-able long branch encoding blo __error_p @ only classic page table format #endif #ifndef CONFIG_XIP_KERNEL adr r3, 2f ldmia r3, {r4, r8} sub r4, r3, r4 @ (PHYS_OFFSET - PAGE_OFFSET) add r8, r8, r4 @ PHYS_OFFSET #else ldr r8, =PHYS_OFFSET @ always constant in this case #endif /* * r1 = machine no, r2 = atags or dtb, * r8 = phys_offset, r9 = cpuid, r10 = procinfo */ bl __vet_atags #ifdef CONFIG_SMP_ON_UP bl __fixup_smp #endif #ifdef CONFIG_ARM_PATCH_PHYS_VIRT bl __fixup_pv_table #endif bl __create_page_tables /* * The following calls CPU specific code in a position independent * manner. See arch/arm/mm/proc-*.S for details. r10 = base of * xxx_proc_info structure selected by __lookup_processor_type * above. On return, the CPU will be ready for the MMU to be * turned on, and r0 will hold the CPU control register value. */ ldr r13, =__mmap_switched @ address to jump to after @ mmu has been enabled adr lr, BSYM(1f) @ return (PIC) address mov r8, r4 @ set TTBR1 to swapper_pg_dir ARM( add pc, r10, #PROCINFO_INITFUNC ) THUMB( add r12, r10, #PROCINFO_INITFUNC ) THUMB( mov pc, r12 ) 1: b __enable_mmu ENDPROC(stext) .ltorg #ifndef CONFIG_XIP_KERNEL 2: .long . .long PAGE_OFFSET #endif /* * Setup the initial page tables. We only setup the barest * amount which are required to get the kernel running, which * generally means mapping in the kernel code. * * r8 = phys_offset, r9 = cpuid, r10 = procinfo * * Returns: * r0, r3, r5-r7 corrupted * r4 = physical page table address */ __create_page_tables: pgtbl r4, r8 @ page table address /* * Clear the swapper page table */ mov r0, r4 mov r3, #0 add r6, r0, #PG_DIR_SIZE 1: str r3, [r0], #4 str r3, [r0], #4 str r3, [r0], #4 str r3, [r0], #4 teq r0, r6 bne 1b #ifdef CONFIG_ARM_LPAE /* * Build the PGD table (first level) to point to the PMD table. A PGD * entry is 64-bit wide. */ mov r0, r4 add r3, r4, #0x1000 @ first PMD table address orr r3, r3, #3 @ PGD block type mov r6, #4 @ PTRS_PER_PGD mov r7, #1 << (55 - 32) @ L_PGD_SWAPPER 1: #ifdef CONFIG_CPU_ENDIAN_BE8 str r7, [r0], #4 @ set top PGD entry bits str r3, [r0], #4 @ set bottom PGD entry bits #else str r3, [r0], #4 @ set bottom PGD entry bits str r7, [r0], #4 @ set top PGD entry bits #endif add r3, r3, #0x1000 @ next PMD table subs r6, r6, #1 bne 1b add r4, r4, #0x1000 @ point to the PMD tables #ifdef CONFIG_CPU_ENDIAN_BE8 add r4, r4, #4 @ we only write the bottom word #endif #endif ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags /* * Create identity mapping to cater for __enable_mmu. * This identity mapping will be removed by paging_init(). */ adr r0, __turn_mmu_on_loc ldmia r0, {r3, r5, r6} sub r0, r0, r3 @ virt->phys offset add r5, r5, r0 @ phys __turn_mmu_on add r6, r6, r0 @ phys __turn_mmu_on_end mov r5, r5, lsr #SECTION_SHIFT mov r6, r6, lsr #SECTION_SHIFT 1: orr r3, r7, r5, lsl #SECTION_SHIFT @ flags + kernel base str r3, [r4, r5, lsl #PMD_ORDER] @ identity mapping cmp r5, r6 addlo r5, r5, #1 @ next section blo 1b /* * Map our RAM from the start to the end of the kernel .bss section. */ add r0, r4, #PAGE_OFFSET >> (SECTION_SHIFT - PMD_ORDER) ldr r6, =(_end - 1) orr r3, r8, r7 add r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER) 1: str r3, [r0], #1 << PMD_ORDER add r3, r3, #1 << SECTION_SHIFT cmp r0, r6 bls 1b #ifdef CONFIG_XIP_KERNEL /* * Map the kernel image separately as it is not located in RAM. */ #define XIP_START XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR) mov r3, pc mov r3, r3, lsr #SECTION_SHIFT orr r3, r7, r3, lsl #SECTION_SHIFT add r0, r4, #(XIP_START & 0xff000000) >> (SECTION_SHIFT - PMD_ORDER) str r3, [r0, #((XIP_START & 0x00f00000) >> SECTION_SHIFT) << PMD_ORDER]! ldr r6, =(_edata_loc - 1) add r0, r0, #1 << PMD_ORDER add r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER) 1: cmp r0, r6 add r3, r3, #1 << SECTION_SHIFT strls r3, [r0], #1 << PMD_ORDER bls 1b #endif /* * Then map boot params address in r2 if specified. * We map 2 sections in case the ATAGs/DTB crosses a section boundary. */ mov r0, r2, lsr #SECTION_SHIFT movs r0, r0, lsl #SECTION_SHIFT subne r3, r0, r8 addne r3, r3, #PAGE_OFFSET addne r3, r4, r3, lsr #(SECTION_SHIFT - PMD_ORDER) orrne r6, r7, r0 strne r6, [r3], #1 << PMD_ORDER addne r6, r6, #1 << SECTION_SHIFT strne r6, [r3] #if defined(CONFIG_ARM_LPAE) && defined(CONFIG_CPU_ENDIAN_BE8) sub r4, r4, #4 @ Fixup page table pointer @ for 64-bit descriptors #endif #ifdef CONFIG_DEBUG_LL #if !defined(CONFIG_DEBUG_ICEDCC) && !defined(CONFIG_DEBUG_SEMIHOSTING) /* * Map in IO space for serial debugging. * This allows debug messages to be output * via a serial console before paging_init. */ addruart r7, r3, r0 mov r3, r3, lsr #SECTION_SHIFT mov r3, r3, lsl #PMD_ORDER add r0, r4, r3 mov r3, r7, lsr #SECTION_SHIFT ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags orr r3, r7, r3, lsl #SECTION_SHIFT #ifdef CONFIG_ARM_LPAE mov r7, #1 << (54 - 32) @ XN #ifdef CONFIG_CPU_ENDIAN_BE8 str r7, [r0], #4 str r3, [r0], #4 #else str r3, [r0], #4 str r7, [r0], #4 #endif #else orr r3, r3, #PMD_SECT_XN str r3, [r0], #4 #endif #else /* CONFIG_DEBUG_ICEDCC || CONFIG_DEBUG_SEMIHOSTING */ /* we don&#39;t need any serial debugging mappings */ ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags #endif #if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS) /* * If we&#39;re using the NetWinder or CATS, we also need to map * in the 16550-type serial port for the debug messages */ add r0, r4, #0xff000000 >> (SECTION_SHIFT - PMD_ORDER) orr r3, r7, #0x7c000000 str r3, [r0] #endif #ifdef CONFIG_ARCH_RPC /* * Map in screen at 0x02000000 & SCREEN2_BASE * Similar reasons here - for debug. This is * only for Acorn RiscPC architectures. */ add r0, r4, #0x02000000 >> (SECTION_SHIFT - PMD_ORDER) orr r3, r7, #0x02000000 str r3, [r0] add r0, r4, #0xd8000000 >> (SECTION_SHIFT - PMD_ORDER) str r3, [r0] #endif #endif #ifdef CONFIG_ARM_LPAE sub r4, r4, #0x1000 @ point to the PGD table #endif mov pc, lr ENDPROC(__create_page_tables) .ltorg .align __turn_mmu_on_loc: .long . .long __turn_mmu_on .long __turn_mmu_on_end #if defined(CONFIG_SMP) __CPUINIT ENTRY(secondary_startup) /* * Common entry point for secondary CPUs. * * Ensure that we&#39;re in SVC mode, and IRQs are disabled. Lookup * the processor type - there is no need to check the machine type * as it has already been validated by the primary processor. */ #ifdef CONFIG_ARM_VIRT_EXT bl __hyp_stub_install_secondary #endif safe_svcmode_maskall r9 mrc p15, 0, r9, c0, c0 @ get processor id bl __lookup_processor_type movs r10, r5 @ invalid processor? moveq r0, #&#39;p&#39; @ yes, error &#39;p&#39; THUMB( it eq ) @ force fixup-able long branch encoding beq __error_p /* * Use the page tables supplied from __cpu_up. */ adr r4, __secondary_data ldmia r4, {r5, r7, r12} @ address to jump to after sub lr, r4, r5 @ mmu has been enabled ldr r4, [r7, lr] @ get secondary_data.pgdir add r7, r7, #4 ldr r8, [r7, lr] @ get secondary_data.swapper_pg_dir adr lr, BSYM(__enable_mmu) @ return address mov r13, r12 @ __secondary_switched address ARM( add pc, r10, #PROCINFO_INITFUNC ) @ initialise processor @ (return control reg) THUMB( add r12, r10, #PROCINFO_INITFUNC ) THUMB( mov pc, r12 ) ENDPROC(secondary_startup) /* * r6 = &secondary_data */ ENTRY(__secondary_switched) ldr sp, [r7, #4] @ get secondary_data.stack mov fp, #0 b secondary_start_kernel ENDPROC(__secondary_switched) .align .type __secondary_data, %object __secondary_data: .long . .long secondary_data .long __secondary_switched #endif /* defined(CONFIG_SMP) */ /* * Setup common bits before finally enabling the MMU. Essentially * this is just loading the page table pointer and domain access * registers. * * r0 = cp#15 control register * r1 = machine ID * r2 = atags or dtb pointer * r4 = page table pointer * r9 = processor ID * r13 = *virtual* address to jump to upon completion */ __enable_mmu: #if defined(CONFIG_ALIGNMENT_TRAP) && __LINUX_ARM_ARCH__ < 6 orr r0, r0, #CR_A #else bic r0, r0, #CR_A #endif #ifdef CONFIG_CPU_DCACHE_DISABLE bic r0, r0, #CR_C #endif #ifdef CONFIG_CPU_BPREDICT_DISABLE bic r0, r0, #CR_Z #endif #ifdef CONFIG_CPU_ICACHE_DISABLE bic r0, r0, #CR_I #endif #ifdef CONFIG_ARM_LPAE mov r5, #0 mcrr p15, 0, r4, r5, c2 @ load TTBR0 #else mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \ domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \ domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \ domain_val(DOMAIN_IO, DOMAIN_CLIENT)) mcr p15, 0, r5, c3, c0, 0 @ load domain access register mcr p15, 0, r4, c2, c0, 0 @ load page table pointer #endif b __turn_mmu_on ENDPROC(__enable_mmu) /* * Enable the MMU. This completely changes the structure of the visible * memory space. You will not be able to trace execution through this. * If you have an enquiry about this, *please* check the linux-arm-kernel * mailing list archives BEFORE sending another post to the list. * * r0 = cp#15 control register * r1 = machine ID * r2 = atags or dtb pointer * r9 = processor ID * r13 = *virtual* address to jump to upon completion * * other registers depend on the function called upon completion */ .align 5 .pushsection .idmap.text, "ax" ENTRY(__turn_mmu_on) mov r0, r0 instr_sync mcr p15, 0, r0, c1, c0, 0 @ write control reg mrc p15, 0, r3, c0, c0, 0 @ read id reg instr_sync mov r3, r3 mov r3, r13 mov pc, r3 __turn_mmu_on_end: ENDPROC(__turn_mmu_on) .popsection #ifdef CONFIG_SMP_ON_UP __INIT __fixup_smp: and r3, r9, #0x000f0000 @ architecture version teq r3, #0x000f0000 @ CPU ID supported? bne __fixup_smp_on_up @ no, assume UP bic r3, r9, #0x00ff0000 bic r3, r3, #0x0000000f @ mask 0xff00fff0 mov r4, #0x41000000 orr r4, r4, #0x0000b000 orr r4, r4, #0x00000020 @ val 0x4100b020 teq r3, r4 @ ARM 11MPCore? moveq pc, lr @ yes, assume SMP mrc p15, 0, r0, c0, c0, 5 @ read MPIDR and r0, r0, #0xc0000000 @ multiprocessing extensions and teq r0, #0x80000000 @ not part of a uniprocessor system? moveq pc, lr @ yes, assume SMP __fixup_smp_on_up: adr r0, 1f ldmia r0, {r3 - r5} sub r3, r0, r3 add r4, r4, r3 add r5, r5, r3 b __do_fixup_smp_on_up ENDPROC(__fixup_smp) .align 1: .word . .word __smpalt_begin .word __smpalt_end .pushsection .data .globl smp_on_up smp_on_up: ALT_SMP(.long 1) ALT_UP(.long 0) .popsection #endif .text __do_fixup_smp_on_up: cmp r4, r5 movhs pc, lr ldmia r4!, {r0, r6} ARM( str r6, [r0, r3] ) THUMB( add r0, r0, r3 ) #ifdef __ARMEB__ THUMB( mov r6, r6, ror #16 ) @ Convert word order for big-endian. #endif THUMB( strh r6, [r0], #2 ) @ For Thumb-2, store as two halfwords THUMB( mov r6, r6, lsr #16 ) @ to be robust against misaligned r3. THUMB( strh r6, [r0] ) b __do_fixup_smp_on_up ENDPROC(__do_fixup_smp_on_up) ENTRY(fixup_smp) stmfd sp!, {r4 - r6, lr} mov r4, r0 add r5, r0, r1 mov r3, #0 bl __do_fixup_smp_on_up ldmfd sp!, {r4 - r6, pc} ENDPROC(fixup_smp) #ifdef CONFIG_ARM_PATCH_PHYS_VIRT /* __fixup_pv_table - patch the stub instructions with the delta between * PHYS_OFFSET and PAGE_OFFSET, which is assumed to be 16MiB aligned and * can be expressed by an immediate shifter operand. The stub instruction * has a form of &#39;(add|sub) rd, rn, #imm&#39;. */ __HEAD __fixup_pv_table: adr r0, 1f ldmia r0, {r3-r5, r7} sub r3, r0, r3 @ PHYS_OFFSET - PAGE_OFFSET add r4, r4, r3 @ adjust table start address add r5, r5, r3 @ adjust table end address add r7, r7, r3 @ adjust __pv_phys_offset address str r8, [r7] @ save computed PHYS_OFFSET to __pv_phys_offset mov r6, r3, lsr #24 @ constant for add/sub instructions teq r3, r6, lsl #24 @ must be 16MiB aligned THUMB( it ne @ cross section branch ) bne __error str r6, [r7, #4] @ save to __pv_offset b __fixup_a_pv_table ENDPROC(__fixup_pv_table) .align 1: .long . .long __pv_table_begin .long __pv_table_end 2: .long __pv_phys_offset .text __fixup_a_pv_table: #ifdef CONFIG_THUMB2_KERNEL lsls r6, #24 beq 2f clz r7, r6 lsr r6, #24 lsl r6, r7 bic r6, #0x0080 lsrs r7, #1 orrcs r6, #0x0080 orr r6, r6, r7, lsl #12 orr r6, #0x4000 b 2f 1: add r7, r3 ldrh ip, [r7, #2] and ip, 0x8f00 orr ip, r6 @ mask in offset bits 31-24 strh ip, [r7, #2] 2: cmp r4, r5 ldrcc r7, [r4], #4 @ use branch for delay slot bcc 1b bx lr #else b 2f 1: ldr ip, [r7, r3] bic ip, ip, #0x000000ff orr ip, ip, r6 @ mask in offset bits 31-24 str ip, [r7, r3] 2: cmp r4, r5 ldrcc r7, [r4], #4 @ use branch for delay slot bcc 1b mov pc, lr #endif ENDPROC(__fixup_a_pv_table) ENTRY(fixup_pv_table) stmfd sp!, {r4 - r7, lr} ldr r2, 2f @ get address of __pv_phys_offset mov r3, #0 @ no offset mov r4, r0 @ r0 = table start add r5, r0, r1 @ r1 = table size ldr r6, [r2, #4] @ get __pv_offset bl __fixup_a_pv_table ldmfd sp!, {r4 - r7, pc} ENDPROC(fixup_pv_table) .align 2: .long __pv_phys_offset .data .globl __pv_phys_offset .type __pv_phys_offset, %object __pv_phys_offset: .long 0 .size __pv_phys_offset, . - __pv_phys_offset __pv_offset: .long 0 #endif #include "head-common.S" 解释以上代码
09-24
import can import time from canBase import CanPowerBase """ 80 00 16 00 10 00 06 07 在SDO中止传输帧中,数据部分的第4到第7字节组成的32位整数表示中止代码(Abort Code)。根据返回的数据: 第1字节: 0x80 (命令说明符) 第2字节: 0x00 第3字节: 0x16 第4字节: 0x00 第5字节: 0x10 第6字节: 0x00 第7字节: 0x06 第8字节: 0x07 其中,第5到第8字节(小端序)组成32位中止代码。由于CANopen协议规定多字节整数使用小端序,所以中止代码为0x07061000。 我们可以查阅CANopen的中止代码表: 0x06010000: 不支持的访问方式 0x06010001: 读一个只写对象 0x06010002: 写一个只读对象 0x06040041: 对象字典中不存在 0x06040042: 对象无法映射到PDO 0x06040043: 映射的PDO数量超出 0x06040047: 参数不兼容 0x06060000: 硬件错误 0x06070010: 数据类型不匹配(长度不同) 0x06070012: 数据长度超过 0x06090011: 子索引不存在 0x06090030: 值超出范围 0x08000000: 通用错误 """ class CanPower(): # 帧类型定义 (CANopen标准) NMT_CTRL = 0x000 # NMT控制帧 SYNC = 0x080 # SYNC帧 TIME_STAMP = 0x100 # 时间戳帧 PDO1_TX = 0x180 # TPDO1 (主站接收) PDO1_RX = 0x200 # RPDO1 (主站发送) PDO2_TX = 0x280 # TPDO2 PDO2_RX = 0x300 # RPDO2 SDO_TX = 0x580 # SDO传输 (从站->主站) SDO_RX = 0x600 # SDO接收 (主站->从站) NMT_GUARD = 0x700 # 节点保护帧 # 节点ID配置 NODE_ID = 0x05 # 从站节点ID PROTECTED_NODES = [0x01] # 节点1受保护 def __init__(self, channel=&#39;can0&#39;, bustype=&#39;socketcan&#39;, bitrate=500000): self.bus = None self.channel = channel # 通道 self.bustype = bustype # 类型 self.bitrate = bitrate # 波特率 # 节点ID配置 self.NODE_ID5 = 0x05 # 从站节点ID def sdo_write(self, node_id, index, subindex, value, data_length=None): """SDO写入对象字典(指定目标节点) 参数: node_id: 目标CANopen节点的ID (0-127) index: 对象字典的16位索引 (如0x2000) subindex: 对象字典的8位子索引 (如0x00) value: 要写入的值 data_length: 可选,指定写入的数据长度(字节数)。如果不指定,则根据value自动判断: value<=0xFF -> 1字节 value<=0xFFFF -> 2字节 value<=0xFFFFFFFF -> 4字节 """ if node_id in CanPower.PROTECTED_NODES: raise PermissionError(f"禁止修改受保护节点{node_id}") sdo_rx = 0x600 + node_id # SDO客户端->服务器地址 # 确定写入数据的长度 if data_length is None: if value <= 0xFF: data_length = 1 elif value <= 0xFFFF: data_length = 2 elif value <= 0xFFFFFFFF: data_length = 4 else: raise ValueError("值太大,不支持") else: # 检查传入的data_length是否合理 max_value = (1 << (data_length * 8)) - 1 if value > max_value: raise ValueError(f"值0x{value:X}超过{data_length}字节可表示的范围(0-0x{max_value:X})") # 命令字节: # data_length=1: 0x2F # data_length=2: 0x2B # data_length=4: 0x23 if data_length == 1: command_byte = 0x2F elif data_length == 2: command_byte = 0x2B elif data_length == 4: command_byte = 0x23 else: raise ValueError("data_length must be 1, 2, or 4") # 构建数据帧 data = [ command_byte, # 命令字节 index & 0xFF, # 索引低字节 (index >> 8) & 0xFF, # 索引高字节 subindex, # 子索引 ] # 填充数据(小端序) for i in range(data_length): data.append((value >> (i * 8)) & 0xFF) # 填充剩余部分为0 while len(data) < 8: data.append(0) msg = can.Message( arbitration_id=sdo_rx, data=data, is_extended_id=False ) self.bus.send(msg) # 等待确认(只接收目标节点的响应) start_time = time.time() while time.time() - start_time < 1.0: response = self.bus.recv(timeout=0.1) if response and response.arbitration_id == (0x580 + node_id): return response.data[0] == 0x60 return False # def sdo_write(self, node_id, index, subindex, value): # """SDO写入对象字典(指定目标节点) # # 参数: # node_id: 目标CANopen节点的ID (0-127) # index: 对象字典的16位索引 (如0x2000) # subindex: 对象字典的8位子索引 (如0x00) # value: 要写入的32位值 # # 返回值: # bool: 写入操作是否成功 done (收到节点确认) # """ # # 节点保护检查:防止修改受保护节点 # if node_id in CanPower.PROTECTED_NODES: # raise PermissionError(f"禁止修改受保护节点{node_id}") # # # 计算目标节点的SDO接收地址 (CAN ID) # sdo_rx = 0x600 + node_id # SDO客户端->服务器地址 # # """SDO数据帧结构解析""" # # CANopen SDO快速写入帧格式 (8字节) # data = [ # # 字节0: 命令字节 (0x23 = 快速写入4字节数据) # # 位7-5: 保留(0) # # 位4: 1=数据长度在命令中指定 # # 位3: 1=快速传输 (expedited) # # 位2: 1=包含数据 # # 位1-0: 11=发起请求 # 0x23, # 快速写入命令 (4字节) # # 字节1: 索引低字节 (LSB) # index & 0xFF, # # 字节2: 索引高字节 (MSB) # (index >> 8) & 0xFF, # # 字节3: 子索引 # subindex, # # 字节4-7: 32位值 (小端序) # value & 0xFF, # 最低有效字节 # (value >> 8) & 0xFF, # 次低有效字节 # (value >> 16) & 0xFF, # 次高有效字节 # (value >> 24) & 0xFF # 最高有效字节 # ] # # # 构造CAN帧 # msg = can.Message( # arbitration_id=sdo_rx, # CAN ID = 0x600 + node_id # data=data, # 8字节数据 # is_extended_id=False # 标准帧ID (11位) # ) # # # 发送SDO请求帧 # self.bus.send(msg) # # # 等待节点响应 (超时1秒) # response = self.bus.recv(timeout=1.0) # # # 验证响应有效性: # # 1. 响应存在 (非None) # # 2. 命令字节为0x60 (SDO写入成功 done确认) # print("response.data", response.data) # return response and response.data[0] == 0x60 def sdo_read(self, node_id, index, subindex=0): """SDO读取对象字典""" SDO_RX = CanPower.SDO_RX + node_id SDO_TX = CanPower.SDO_TX + node_id data = [ 0x40, # 读取命令 index & 0xFF, (index >> 8) & 0xFF, subindex, 0, 0, 0, 0 ] msg = can.Message( arbitration_id=SDO_RX, data=data, is_extended_id=False ) self.bus.send(msg) # 等待确认(只接收目标节点的响应) start_time = time.time() while time.time() - start_time < 1.0: response = self.bus.recv(timeout=0.1) if response and response.arbitration_id == SDO_TX: if response.data[0] == 0x43: # 成功 done响应 return response.data[4] | (response.data[5] << 8) | \ (response.data[6] << 16) | (response.data[7] << 24) return False def reset_node_configuration(self, node_id): """ 恢复节点的默认配置 步骤: 1. disabled 所有PDO映射 2. 重置PDO映射参数 3. 重启节点 """ # 1. disabled 所有PDO for pdo_type in ["RPDO", "TPDO"]: for instance in range(1, 5): # PDO1-4 if pdo_type == "RPDO": comm_index = 0x1400 + (instance - 1) else: comm_index = 0x1800 + (instance - 1) # 读取当前COB-ID并设置disabled 位 self.sdo_write(node_id, comm_index, 1, 0x80000000) # 2. 重置映射参数 for map_type in ["RPDO", "TPDO"]: for instance in range(1, 5): if map_type == "RPDO": map_index = 0x1600 + (instance - 1) else: map_index = 0x1A00 + (instance - 1) # 设置映射条目数为0 self.sdo_write(node_id, map_index, 0, 0) # 清除所有映射条目 for subindex in range(1, 9): self.sdo_write(node_id, map_index, subindex, 0) # 3. 发送NMT重启命令 # NMT命令帧:ID=0x00, 数据=[命令, 节点ID] nmt_msg = can.Message( arbitration_id=0x000, data=[0x81, node_id, 0, 0, 0, 0, 0, 0], # 0x81 = 复位节点 is_extended_id=False ) self.bus.send(nmt_msg) print(f"send node_id={node_id} reset 重启命令") # 等待节点重新上线 time.sleep(2) return True def verify_node_config(self, node_id): """验证节点配置是否恢复默认""" # 检查TPDO1映射条目数(应为0) map_index = 0x1A00 # TPDO1映射参数 num_entries = self.sdo_read(node_id, map_index, 0) if num_entries != 0: print(f"节点{node_id}配置未完全恢复! node rsset Failure Mapping Entry数={num_entries}") return False # 检查RPDO1状态 comm_index = 0x1400 cobid = self.sdo_read(node_id, comm_index, 1) if cobid & 0x80000000: # 检查disabled 位 print(f"节点{node_id} RPDO1仍被disabled ") return False return True def disable_pdo(self, node_id, pdo_type, instance=1): """disabled PDO映射""" if pdo_type == "RPDO": comm_index = 0x1400 + (instance - 1) else: # TPDO comm_index = 0x1800 + (instance - 1) # 设置COB-ID最高位为1 (disabled PDO) current_cobid = self.sdo_read(node_id, comm_index, 1) return self.sdo_write(node_id, comm_index, 1, current_cobid | 0x80000000) def configure_pdo_mapping(self, node_id, pdo_type, instance, mappings): """ 配置PDO映射 :param pdo_type: &#39;RPDO&#39; 或 &#39;TPDO&#39; :param instance: PDO实例 (1-4) :param mappings: 映射列表,格式: [(对象索引, 子索引, 数据长度(位))] """ assert pdo_type in [&#39;RPDO&#39; , &#39;TPDO&#39;] # 确定映射对象索引 if pdo_type == "RPDO": comm_index = 0x1400 + (instance - 1) map_index = 0x1600 + (instance - 1) else: # TPDO comm_index = 0x1800 + (instance - 1) map_index = 0x1A00 + (instance - 1) # disabled PDO if not self.disable_pdo(node_id, pdo_type, instance): print(f"disabled {pdo_type}{instance}失败 Failure") return False else: print(f"disabled {pdo_type}{instance} Success") # 设置映射条目数量 num_entries = len(mappings) if not self.sdo_write(node_id, map_index, 0, num_entries): print(f"设置 Seting Mapping Entry失败 Failure (Index 0x{map_index:04X})") return False # 配置每个映射条目 for i, mapping in enumerate(mappings, start=1): obj_index, subindex, bit_length = mapping # 构建映射值: 对象索引 + 子索引 + 数据长度 mapping_value = (obj_index << 16) | (subindex << 8) | (bit_length & 0xFF) if not self.sdo_write(node_id, map_index, i, mapping_value): print(f"Mapping Entry {i} 配置失败 Failure") return False # 重新启用PDO if pdo_type == "RPDO": cobid_base = 0x200 + node_id # NODE_ID else: cobid_base = 0x180 + node_id # NODE_ID # 计算当前实例的COB-ID cobid = cobid_base + (instance - 1) * 0x100 print(cobid) return self.sdo_write(node_id, comm_index, 1, cobid) def configure_all_mappings(self, node_id): """配置所有需要的PDO映射""" # 1. 配置RPDO1 (0x205) 映射 rpdo1_mappings = [ (0x2000, 0, 1), # bAuto (0x2001, 0, 1), # bWeldDisenable (0x2002, 0, 1), # bPowerEn (0x2003, 0, 1), # bPilotArcEn (0x2010, 0, 1), # bCurrentLockEn (0x2011, 0, 1), # bCurrentLockMode (0x2004, 0, 1), # bPulseEn ] if self.configure_pdo_mapping(node_id, "RPDO", 1, rpdo1_mappings): print("RPDO1 (0x205) 映射配置成功 done") else: print("RPDO1 (0x205) 映射配置失败 Failure") return False # 2. 配置RPDO2 (0x305) 映射 rpdo2_mappings = [ (0x2005, 0, 16), # iCurrent1 (0x2006, 0, 16), # iCurrent2 (0x2007, 0, 16), # iHZ1 (0x2008, 0, 16), # iDutyCycle1 ] if self.configure_pdo_mapping(node_id,"RPDO", 2, rpdo2_mappings): print("RPDO2 (0x305) 映射配置成功 done") else: print("RPDO2 (0x305) 映射配置失败 Failure") return False # 3. 配置TPDO1 (0x185) 映射 tpdo1_mappings = [ (0x2009, 0, 1), # bPowerAlarm (0x200A, 0, 1), # bPowerReady (0x200B, 0, 1), # bIGRO (0x200C, 0, 1), # bPilotArcOk (0x200D, 0, 1), # bPeakCurrentWorking (0x2012, 0, 8), # (0x200E, 0, 16), # iCurrentFeedback (0x200F, 0, 16), # iActVoltage ] if self.configure_pdo_mapping(node_id, "TPDO", 1, tpdo1_mappings): print("TPDO1 (0x185) 映射配置成功 done") else: print("TPDO1 (0x185) 映射配置失败 Failure") return False # 4. 重新启用所有PDO(推荐) self.enable_all_pdo(node_id) return True def enable_all_pdo(self, node_id): """重新启用所有PDO""" for pdo_type in ["RPDO", "TPDO"]: for instance in range(1, 5): # PDO1-4 if pdo_type == "RPDO": comm_index = 0x1400 + (instance - 1) cobid_base = 0x200 + node_id # RPDO基础COB-ID else: comm_index = 0x1800 + (instance - 1) cobid_base = 0x180 + node_id # TPDO基础COB-ID # 计算当前实例的COB-ID cobid = cobid_base + (instance - 1) * 0x100 # 清除禁用位(最高位) enabled_cobid = cobid & 0x7FFFFFFF # 重新启用PDO self.sdo_write(node_id, comm_index, 1, enabled_cobid) print(f"reboot node_id 启用节点{node_id}的所有PDO") def check_node_state(self, node_id): """检查节点是否处于操作状态""" # 读取节点状态 (索引0x2100) state = self.sdo_read(node_id, 0x2100, 0) if state is False: print(f"无法读取节点{node_id}状态") print(f"0x2100 not exisit") return False # CANopen节点状态定义: # 0: 初始化 # 1: 未连接 # 2: 预操作 # 3: 停止 # 4: 操作 # 5: 预操作+启动 if state != 4: # 不是操作状态 print(f"节点{node_id}处于非操作状态(状态码:{state}),尝试设置为操作状态") # 发送NMT命令设置节点为操作状态 nmt_msg = can.Message( arbitration_id=0x000, data=[0x01, node_id, 0, 0, 0, 0, 0, 0], # 0x01 = 启动节点 is_extended_id=False ) self.bus.send(nmt_msg) # 等待状态切换 time.sleep(0.5) new_state = self.sdo_read(node_id, 0x2100, 0) if new_state == 4: print(f"节点{node_id}已成功设置为操作状态 Success") return True else: print(f"无法设置节点{node_id}为操作状态 Failure") return False return True def set_nmt_state(self, node_id, state): """设置节点NMT状态 state: 0x01 (启动), 0x02 (停止), 0x80 (预操作), 0x81 (复位节点), 0x82 (复位通信) """ msg = can.Message( arbitration_id=0x000, data=[state, node_id], is_extended_id=False ) self.bus.send(msg) def connect(self, node_id = 0x05): # node_id = 0x05 self.bus = can.interface.Bus( channel=self.channel, bustype=self.bustype, bitrate=self.bitrate) # 配置站点5的信号 if not self.configure_all_mappings(node_id): print(" node id 0x05 Set Error ! reset......") self.reset_node_configuration(node_id) # 发送NMT命令设置节点为操作状态 nmt_msg = can.Message( arbitration_id=0x000, data=[0x01, node_id], # 0x01 = 启动节点 is_extended_id=False ) self.bus.send(nmt_msg) return self.bus # self.bus.shutdown() # def ctyle(self): if __name__ == "__main__": power = CanPower() power.connect() 如何在映射的时候就设置ycnc等,让节点0x185 一直发送数据(最短时间)
09-11
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值