KEIL 5.38 ARM-CM3/4汇编设计学习笔记7 – STM32 CAN学习与测试2

KEIL 5.38的ARM-CM3/4 ARM汇编设计学习笔记7 – STM32的CAN学习与测试2 – 双机通讯的发送测试

  • 一、前文回顾
  • 二、 双机通讯的发送测试
  • 2.1 一个注意事项
  • 2.2 测试环境
  • 2.3 测试代码
  • 三、测试结果
  • 四、结论和下一步工作
  • 一、前文回顾

    根据《KEIL 5.38的ARM-CM3/4 ARM汇编设计学习笔记6 – STM32的CAN学习与测试1》的讨论,我实现了单机的回环模式的CAN测试。后来在测试中又有下面的积累。

    1. 在回环模式下,不会有ACK检定,也不会有ACK不应答的报警。 由于CANTX实际上并不把数据发给CAN收发器,只是在芯片里面转一圈就到了CANRX。根据手册的说明,这个过程中是不会有ACK检定。所以不会在CAN_ESR中有ACK ERROR(LEC为3)的指示。参考手册《RM0090 Reference manual – STM32F405/415, STM32F407/417, STM32F427/437 and STM32F429/439 advanced Arm®-based 32-bit MCUs》1082页的说明。

    1. 可以通过CAN_MCR的第4位NART位和CAN_TSR的ABRQx位来中止当前的发送请求。 如果在DEBUG模式中不小心产生了Transmission错误,比如关掉了回环模式并直接去发送数据。在示波器上会看到数据不停的发送,并且相关的CAN_ESR中有错误指示。这个时候复位CAN_TIxR的TXRQ位是复不了位的。必须通过置位CAN_TSR的ABRQx位来中止掉当前的发送。究其原因,是因为默认的情况下,CAN_MCR的第4位NART位是0,意思是开启自动重新发送。所以一旦检测到错误,CAN外设就会一直尝试发送。有人认为这个最好还是关了。但是笔者目前没有偏好。

    二、 双机通讯的发送测试

    2.1 一个注意事项

    说起这个测试,笔者犯了一个小失误。那就是在debug之前要记得检查一下板子上的BOOT0和BOOT1口的电平是否是正确的。参考手册的第69页的表2。

    这里,如果要从Flash启动,BOOT0端一定要确认接地,悬空可能会导致歧义。DEBUG的时候也许一切正常,一旦脱机使用,就发现程序不能用了。所以要用跳线帽把BOOT0都和GND连好。双机或者多机通讯的时候板子比较多,很有可能会出现这种失误。我的接线如下所示。

    2.2 测试环境

    测试用到的开发板、相关的软件和设备如下表所示。

    名称 型号
    开发板 HX32F4开发板2块
    示波器 鼎阳SDS3104X HD
    IDE Keil 5

    2.3 测试代码

    和上一期的代码很相似,只是我们这次不用进入回环模式了。所有的代码如下所示

    	get peripherials.s
    rRCC	rn 	r8
    rGPIOB	rn 	r9
    rCAN1	rn	r10
    
    	import SystemCoreClockUpdate
    	
    	area text, code	
    	align 4
    	preserve8 
    init	proc
    	push {r4 - r11, lr}
    	
    	; Enable the clocks. 
    	; APB1 for CAN1, AHB1 for GPIOB
    	ldr rRCC, =RCC_BaseAddr
    	ldr  r4, [rRCC, #RCC_AHB1ENR]
    	orr  r4, #RCC_AHB1ENR_GPIOBEN
    	str  r4, [rRCC, #RCC_AHB1ENR]
    	ldr  r4, [rRCC, #RCC_APB1ENR]
    	orr  r4, #RCC_APB1ENR_CAN1EN
    	str  r4, [rRCC, #RCC_APB1ENR]
    	
    	; Configure the pins for CAN1
    	; CAN_TX: PB8, AF9
    	; CAN_RX: PB9, AF9
    	ldr  rGPIOB, =GPIOB_BaseAddr
    	ldr  r4, [rGPIOB, #GPIOx_MODER]
    	bfc  r4, #16, #4
    	orr  r4, #(GPIOx_MODER_AFIO:shl:16):or:(GPIOx_MODER_AFIO:shl:18)
    	str  r4, [rGPIOB, #GPIOx_MODER]
    	ldr  r4, [rGPIOB, #GPIOx_OSPEEDR]
    	bfc  r4, #16, #4
    	orr  r4, #(GPIOx_OSPEEDR_VERYHIGH:shl:16):or:(GPIOx_OSPEEDR_VERYHIGH:shl:18)
    	str  r4, [rGPIOB, #GPIOx_OSPEEDR]
    	ldr  r4, [rGPIOB, #GPIOx_PUPDR]
    	bfc  r4, #16, #4
    	orr  r4, #(GPIOx_PUPDR_PU:shl:16):or:(GPIOx_PUPDR_PU:shl:18)
    	str  r4, [rGPIOB, #GPIOx_PUPDR]
    	ldr  r4, [rGPIOB, #GPIOx_AFRH]
    	bfc  r4, #0, #4
    	orr  r4, #(9:shl:0) :or: (9:shl:4)
    	str	 r4, [rGPIOB, #GPIOx_AFRH]
    	
    	; Setup the CAN1
    	ldr rCAN1, =CAN1_BaseAddr
    	
    	; Trans to Initialisation Mode
    	ldr  r4, [rCAN1, #CAN_MCR]
    	bic  r4, #CAN_MCR_SLEEP
    	orr	 r4, #CAN_MCR_INRQ
    	str	 r4, [rCAN1, #CAN_MCR]
    	
    	; Set the Baudrate
    	; CAN1 is at APB1. 
    	push {r0}
    	ldr  r4, =SystemCoreClockUpdate
    	blx  r4
    	lsr  r4, r0, #2
    	; Now f_apb1 is r4.
    	pop  {r1}
    	
    	; TS1 and TS2 are 3, BRP + 1 is r4.
    	; Enable the loop-back mode
    	udiv r4, r4, r1
    	mov  r5, #9
    	udiv r4, r4 ,r5
    	sub  r4, #1
    	mov  r5, #3
    	ldr  r6, [rCAN1, #CAN_BTR]
    	bfc  r6, #16, #6
    	bfi	 r6, r4, #0, #10
    	bfi	 r6, r5, #16, #4
    	bfi	 r6, r5, #20, #3
    	str  r6, [rCAN1, #CAN_BTR]
    	
    	; Set the filter
    	ldr  r4, [rCAN1, #CAN_FM1R]
    	orr	 r4, #1:shl:0
    	str  r4, [rCAN1, #CAN_FM1R]
    	
    	; The filtered STID is 11, data frame
    	; Enable the filter F0R1
    	mov  r4, #11:shl:21
    	str  r4, [rCAN1, #CAN_F0R1]
    	ldr  r4, [rCAN1, #CAN_FA1R]
    	orr  r4, #1:shl:0
    	str  r4, [rCAN1, #CAN_FA1R]
    	ldr  r4, [rCAN1, #CAN_FMR]
    	bic  r4, #1:shl:0
    	str  r4, [rCAN1, #CAN_FMR]
    	
    	; Switch CAN1 Peripherial from Initialisation to Normal State
    	ldr  r4, [rCAN1, #CAN_MCR]
    	bic  r4, #1:shl:0
    	str  r4, [rCAN1, #CAN_MCR]
    	
    	
    	; Make a test
    	ldr  r4, =Test_String
    	ldr  r4, [r4]
    	str  r4, [rCAN1, #CAN_TDL0R]
    	mov  r4, #4
    	ldr  r5, [rCAN1, #CAN_TDT0R]
    	bfi  r5, r4, #0, #3
    	str  r5, [rCAN1, #CAN_TDT0R]
    	ldr  r4, [rCAN1, #CAN_TI0R]
    	
    	mov  r4, #11:shl:21
    	str  r4, [rCAN1, #CAN_TI0R]
    	
    	
    	pop	 {r4 - r11, lr}
    	bx	 lr
    	
    Test_String	dcb	"Andy"	
    	endp
    	align 4	
    can1 dcd	init
    	export can1	
    		
    	end
    

    关于这段代码就没有什么好讲的了。因为每一部分的功能都在上一期讲到了。

    有人可能会认为,我应该把尽可能多的常数定义成符号(注意,C语言里面的宏和汇编中的符号不是一回事。符号是在编译和链接过程中都有效的;而宏只要过了预编译就不存在了)。比如,用把1:shl:0定义成CAN_MCR_INRQ。这样会显得专业一点。我非常同意您的看法。我会在后面的版本中改正。

    编译,链接。分别下载两块开发板。只不过在第二块开发板上点debug进去。

    三、测试结果

    还是那个熟悉的调试界面。还是点击TI0R的TXRQ,我们通过0号邮箱发送一个数据帧出去。

    暂时先不去看接收那边。用示波器直接看波形分析。


    根据CAN的协议,ACK那个位必须是总线系统中其他的设备来填。不可以你自己发包自己填ACK。那么从这个波形分析中可以看到一个完整的数据帧了。

    关于上一期说到的那个这个示波器的协议解析选件的BUG,在没有ACK应答的时候确实会出现不好确认ACK ERROR的问题。但是在ACK应答正常的情况下,SDS3104X HD是不出这个BUG的。

    四、结论和下一步工作

    通过上面的讨论可以看出,每一块开发板都可以通过CAN总线在这样一个多主系统中发出邮件。下一步需要做一套收发程序,或者通过两个电脑开DEBUG的方法测试接收的情况。

    物联沃分享整理
    物联沃-IOTWORD物联网 » KEIL 5.38 ARM-CM3/4汇编设计学习笔记7 – STM32 CAN学习与测试2

    发表评论