STM32F103C8T6 CAN通信详解
之前在stm32f107上面整过can,这次换了一个芯片,是STM32F103C8T6,48引脚封装的,把之前的107的程序移植过来的时候
不好使,无奈得重新配置,这次清楚的stm32的时钟和can的波特率学习了一遍,
先介绍板子硬件资源:
HSE时钟:8MHz;
MCU : STM32F103C8T6
CAN:一路;(注意:没有端口映射,使用PA11(can接收),PA12(can发送));
一、时钟配置
首先看看系统初始化时的时钟配置(使用的HSE时钟,只讲解从HSE时钟源到CAN时钟线路上的配置)
先看初始化代码中部分:
1 ; Reset handler 2 Reset_Handler PROC 3 EXPORT Reset_Handler [WEAK] 4 IMPORT __main 5 IMPORT SystemInit 6 LDR R0, =SystemInit 7 BLX R0 8 LDR R0, =__main 9 BX R0 10 ENDP
清楚的看到,在进入main函数之前,系统显示进入 SystemInit() 函数,进到这里看看;
1 void SystemInit (void) 2 { 3 /* Reset the RCC clock configuration to the default reset state(for debug purpose) */ 4 /* Set HSION bit */ 5 RCC->CR |= (uint32_t)0x00000001; 6 7 /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */ 8 #ifndef STM32F10X_CL 9 RCC->CFGR &= (uint32_t)0xF8FF0000; 10 #else 11 RCC->CFGR &= (uint32_t)0xF0FF0000; 12 #endif /* STM32F10X_CL */ 13 14 /* Reset HSEON, CSSON and PLLON bits */ 15 RCC->CR &= (uint32_t)0xFEF6FFFF; 16 17 /* Reset HSEBYP bit */ 18 RCC->CR &= (uint32_t)0xFFFBFFFF; 19 20 /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */ 21 RCC->CFGR &= (uint32_t)0xFF80FFFF; 22 23 #ifdef STM32F10X_CL 24 /* Reset PLL2ON and PLL3ON bits */ 25 RCC->CR &= (uint32_t)0xEBFFFFFF; 26 27 /* Disable all interrupts and clear pending bits */ 28 RCC->CIR = 0x00FF0000; 29 30 /* Reset CFGR2 register */ 31 RCC->CFGR2 = 0x00000000; 32 #elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL) 33 /* Disable all interrupts and clear pending bits */ 34 RCC->CIR = 0x009F0000; 35 36 /* Reset CFGR2 register */ 37 RCC->CFGR2 = 0x00000000; 38 #else 39 /* Disable all interrupts and clear pending bits */ 40 RCC->CIR = 0x009F0000; 41 #endif /* STM32F10X_CL */ 42 43 #if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL) 44 #ifdef DATA_IN_ExtSRAM 45 SystemInit_ExtMemCtl(); 46 #endif /* DATA_IN_ExtSRAM */ 47 #endif 48 49 /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */ 50 /* Configure the Flash Latency cycles and enable prefetch buffer */ 51 SetSysClock(); 52 53 #ifdef VECT_TAB_SRAM 54 SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */ 55 #else 56 SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */ 57 #endif 58 }
面前一系列的RCC寄存器的初始化,还有一些条件编译选项,那些都是无关紧要的,对寄存器的初始化,
还有就是根据mcu的型号选择不同的编译; 到最后那里调用了 SetSysClock() 函数,
我们在进入到这个函数里看看,代码:
1 static void SetSysClock(void) 2 { 3 #ifdef SYSCLK_FREQ_HSE 4 SetSysClockToHSE(); 5 #elif defined SYSCLK_FREQ_24MHz 6 SetSysClockTo24(); 7 #elif defined SYSCLK_FREQ_36MHz 8 SetSysClockTo36(); 9 #elif defined SYSCLK_FREQ_48MHz 10 SetSysClockTo48(); 11 #elif defined SYSCLK_FREQ_56MHz 12 SetSysClockTo56(); 13 #elif defined SYSCLK_FREQ_72MHz 14 SetSysClockTo72(); 15 #endif 16 17 /* If none of the define above is enabled, the HSI is used as System clock 18 source (default after reset) */ 19 }
又是一些条件编译,没事,因为之前之前宏定义的是
#define SYSCLK_FREQ_72MHz 72000000
所以程序进入到 SetSysClockTo72 函数中,看看这个函数里面:
1 /** 2 * @brief Sets System clock frequency to 72MHz and configure HCLK, PCLK2 3 * and PCLK1 prescalers. 4 * @note This function should be used only after reset. 5 * @param None 6 * @retval None 7 */ 8 static void SetSysClockTo72(void) 9 { 10 __IO uint32_t StartUpCounter = 0, HSEStatus = 0; 11 12 /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/ 13 /* Enable HSE */ 14 RCC->CR |= ((uint32_t)RCC_CR_HSEON); 15 16 /* Wait till HSE is ready and if Time out is reached exit */ 17 do 18 { 19 HSEStatus = RCC->CR & RCC_CR_HSERDY; 20 StartUpCounter++; //HSE_STARTUP_TIMEOUT重拾计数,系统便不适用PLL,而使用内部8MHz晶振 21 } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); 22 23 if ((RCC->CR & RCC_CR_HSERDY) != RESET) 24 { 25 HSEStatus = (uint32_t)0x01; 26 } 27 else 28 { 29 HSEStatus = (uint32_t)0x00; 30 } 31 32 if (HSEStatus == (uint32_t)0x01) 33 { 34 /* Enable Prefetch Buffer */ 35 FLASH->ACR |= FLASH_ACR_PRFTBE; 36 37 /* Flash 2 wait state */ 38 FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY); 39 FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2; 40 41 42 /* HCLK = SYSCLK */ 43 RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; 44 45 /* PCLK2 = HCLK */ 46 RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; 47 48 /* PCLK1 = HCLK */ 49 RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; //APB1分频系数:2 craigtao 2014-4-4 50 51 #ifdef STM32F10X_CL 52 /* Configure PLLs ------------------------------------------------------*/ 53 /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */ 54 /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */ 55 56 RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL | 57 RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC); 58 RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 | 59 RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5); 60 61 /* Enable PLL2 */ 62 RCC->CR |= RCC_CR_PLL2ON; 63 /* Wait till PLL2 is ready */ 64 while((RCC->CR & RCC_CR_PLL2RDY) == 0) 65 { 66 } 67 68 69 /* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */ 70 RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL); 71 RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | 72 RCC_CFGR_PLLMULL9); 73 #else 74 /* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */ 75 RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | 76 RCC_CFGR_PLLMULL)); 77 RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9); //PLL倍频系数:9 craigtao 2014-4-4 78 #endif /* STM32F10X_CL */ 79 80 /* Enable PLL */ 81 RCC->CR |= RCC_CR_PLLON; 82 83 /* Wait till PLL is ready */ 84 while((RCC->CR & RCC_CR_PLLRDY) == 0) 85 { 86 } 87 88 /* Select PLL as system clock source */ 89 RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); 90 RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; 91 92 /* Wait till PLL is used as system clock source */ 93 while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) 94 { 95 } 96 } 97 else 98 { /* If HSE fails to start-up, the application will have wrong clock 99 configuration. User can add here some code to deal with this error */ 100 } 101 StartUpCounter = 0x11223344; 102 HSEStatus = 0x22334455; 103 }
又是一些条件编译,不用在乎,结合mcu手册,关键的两行代码:
/* PCLK1 = HCLK */ RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; //APB1分频系数:2 craigtao 2014-4-4 RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9); //PLL倍频系数:9 craigtao 2014-4-4
程序注释写得很明白,can使用的时钟是AHB低速APB1上的时钟,这里配置分频系数是 2 ; 时钟源是PLL提供
在往上一级时钟就是PLL时钟,PLL时钟倍频系数是 9 ;作为APB1的输入时钟源
HSE也就是外部接入的 8MHz 的晶振,作为PLL的输入时钟源,
总结一下时钟的流向:
HSE (8 MHz) -------> PLL倍频 (9 倍 = 72 MHz) ---------> APB1分频 (1/2 倍 = 36MHz) ------> can工作时钟 = 36 MHz
二、can波特率配置
上面已经讲了,can工作的时钟是 : 36 MHz,清楚了这个以后,结合mcu手册,进行can波特率的设置,就从代码的角度讲解,看can波特率设置代码段:
CAN_InitStructure.CAN_SJW=CAN_SJW_1tq; CAN_InitStructure.CAN_BS1=CAN_BS1_3tq;
CAN_InitStructure.CAN_BS2=CAN_BS2_2tq;
CAN_InitStructure.CAN_Prescaler=60;
专用词汇名称在这里就不多讲解了,现在给出个公式: 8 (晶振) x 9 (PLL倍频) / 2 (APB1分频) / 60 / (1 + 3 + 2) = 0.1 = 100 (K)
得到can的通信波特率 100 K;
就套用这个公司,关键是得到can的时钟源,也就是第一里讲的内容,
(以上只是个人的理解,没有很详细的写出来,希望大家多多批评)
2014年4月4日