2016年10月2日 星期日

作業系統之前的程式 for stm32f4discovery (14) - 打造 1ms timer 使用 systick

我並沒有什麼方法,只是對於一件事情很長時間很熱心地去考慮罷了。 - 牛頓

距離前一篇《作業系統之前的程式 for stm32f4discovery (13) - 打造 c++ 標準程式庫》有 8 個月了, 這期間我轉移目標, 改為研究編譯器, 在略有小成之後, 我又回到了 stm32f407, 補足我之前沒有搞懂的部份, 也因為購買了《Saleae Logic16 (16通道 100MHz 邏輯分析儀)》才能繼續下去。

其實我自己也為了編譯器設定不少計劃, 大概只完成 30%, 不過編譯器似乎還不夠吸引我, 我又回到和作業系統相關的主題。

終於完成這個了, 設定一個精準的 timer 是很重要的行為, 但為什麼到這麼後面才介紹, 因為需要很多東西。
  1. 邏輯分析儀
  2. 搞懂 stm32f4 clock source tree
  3. 設定 systick
fig 1 用 la 測量 pd12, pd13
我在《作業系統之前的程式 for stm32f4discovery (6) - systick》完成 3, 而最近買了《Saleae Logic16 (16通道 100MHz 邏輯分析儀)》, 只剩下最難的 2, 而在讀了《精通STM32F4(寄存器版)》、《精通STM32F4(库函数版)》之後, 我終於略懂 stm32f407 clock source tree, 還真不是普通的難。

總之在呼叫 my_timer.c L672 SystemInit() 之後:
sysclk (cpu 時脈): 168 MHz
AHB: 168 MHz
APB1: 42MHz
APB2: 84MHz
main PLL: 168 MHz

再來只要知道那個週邊掛在哪個 bus, 就知道要怎麼設定其 clock。ref: 作業系統之前的程式 for stm32f4discovery (3) - usart 最上面的幾張圖。

systick 是 sysclk/8 = 21MHz, 這表示 systick 1/21000000 s 會減小一個計數值, 如果把計數值設定為 21000, 在 21000/21000000 s 之後就會計數完畢, 也就是 1ms, 這樣就完成了1ms 的設定。my_timer.c L7 的 -1 是怎麼回事, 手冊說的, 計數值要減一。

systick 只要設定 3 個暫存器就可以完成我們需要的功能 (ref: DDI0337E_cortex_m3_r1p1_trm.pdf ):

  1. control
  2. reload
  3. current value

ref: my_timer.c 712, 713, 740

my_timer.c L708 而 control bit[2] 的 clksource, 在我的測試下, 不管是 0 或 1 都沒影響, 真是奇怪, 0 應該是使用 implementation defined external reference clock (168Mhz/8 = 21Mhz), 而 1 應該是用 168Mhz 當作 clksource。

程式邏輯是這樣: 點亮綠色 led, 1ms 後, 熄燈, 1ms 後, 亮燈, 人眼看不出來有閃爍, 不過從 la 的波型可以看到 1ms 上下一次。

fig 2 顯示 1ms 亮/滅一次。

fig 2 1ms systick

my_timer.c
  1 #include "stm32.h"
  2 #include "stm32f4xx_usart.h"
  3 #include "stm32f4xx_rcc.h"
  4 #include "stm32f4xx_gpio.h"
  5 
  6 //#define INTERVAL (42000-1); // 2ms
  7 #define INTERVAL (21000-1); // 1ms
  8 
 96 void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState)
 97 { 
 98   /* Check the parameters */
 99   assert_param(IS_RCC_APB1_PERIPH(RCC_APB1Periph));
100   assert_param(IS_FUNCTIONAL_STATE(NewState));
101 
102   if (NewState != DISABLE)
103   {
104     RCC->APB1ENR |= RCC_APB1Periph;
105   }
106   else
107   {
108     RCC->APB1ENR &= ~RCC_APB1Periph;
109   }
110 }
111 
136 
137 void RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState)
138 {
139   /* Check the parameters */
140   assert_param(IS_RCC_AHB1_CLOCK_PERIPH(RCC_AHB1Periph));
141 
142   assert_param(IS_FUNCTIONAL_STATE(NewState));
143   if (NewState != DISABLE)
144   {
145     RCC->AHB1ENR |= RCC_AHB1Periph;
146   }
147   else
148   {
149     RCC->AHB1ENR &= ~RCC_AHB1Periph;
150   }
151 }
152 
296 void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
297 {
298   uint32_t pinpos = 0x00, pos = 0x00 , currentpin = 0x00;
299 
300   /* Check the parameters */
301   assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
302   assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));
303   assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));
304   assert_param(IS_GPIO_PUPD(GPIO_InitStruct->GPIO_PuPd));
305 
306   /* -------------------------Configure the port pins---------------- */
307   /*-- GPIO Mode Configuration --*/
308   for (pinpos = 0x00; pinpos < 0x10; pinpos++)
309   {
310     pos = ((uint32_t)0x01) << pinpos;
311     /* Get the port pins position */
312     currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
313 
314     if (currentpin == pos)
315     {
316       GPIOx->MODER  &= ~(GPIO_MODER_MODER0 << (pinpos * 2));
317       GPIOx->MODER |= (((uint32_t)GPIO_InitStruct->GPIO_Mode) << (pinpos * 2));
318 
319       if ((GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT) || (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_AF))
320       {
321         /* Check Speed mode parameters */
322         assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
323 
324         /* Speed mode configuration */
325         GPIOx->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR0 << (pinpos * 2));
326         GPIOx->OSPEEDR |= ((uint32_t)(GPIO_InitStruct->GPIO_Speed) << (pinpos * 2));
327 
328         /* Check Output mode parameters */
329         assert_param(IS_GPIO_OTYPE(GPIO_InitStruct->GPIO_OType));
330 
331         /* Output mode configuration*/
332         GPIOx->OTYPER  &= ~((GPIO_OTYPER_OT_0) << ((uint16_t)pinpos)) ;
333         GPIOx->OTYPER |= (uint16_t)(((uint16_t)GPIO_InitStruct->GPIO_OType) << ((uint16_t)pinpos));
334       }
335 
336       /* Pull-up Pull down resistor configuration*/
337       GPIOx->PUPDR &= ~(GPIO_PUPDR_PUPDR0 << ((uint16_t)pinpos * 2));
338       GPIOx->PUPDR |= (((uint32_t)GPIO_InitStruct->GPIO_PuPd) << (pinpos * 2));
339     }
340   }
341 }
428 
493 
494 static void SetSysClock(void)
495 {
496 /************************************************************************/
497 /*         PLL (clocked by HSE) used as System clock source             */
498 /************************************************************************/
499   __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
500   
501   /* Enable HSE */
502   RCC->CR |= ((uint32_t)RCC_CR_HSEON);
503  
504   /* Wait till HSE is ready and if Time out is reached exit */
505   do
506   {
507     HSEStatus = RCC->CR & RCC_CR_HSERDY;
508     StartUpCounter++;
509   } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
510 
511   if ((RCC->CR & RCC_CR_HSERDY) != RESET)
512   {
513     HSEStatus = (uint32_t)0x01;
514   }
515   else
516   {
517     HSEStatus = (uint32_t)0x00;
518   }
519 
520   if (HSEStatus == (uint32_t)0x01)
521   {
522     /* Enable high performance mode, System frequency up to 168 MHz */
523     RCC->APB1ENR |= RCC_APB1ENR_PWREN;
524     PWR->CR |= PWR_CR_PMODE;  
525 
526     /* HCLK = SYSCLK / 1*/
527     RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
528       
529     /* PCLK2 = HCLK / 2*/
530     RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
531     
532     /* PCLK1 = HCLK / 4*/
533     RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
534 
535     /* Configure the main PLL */
536     RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
537                    (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
538 
539     /* Enable the main PLL */
540     RCC->CR |= RCC_CR_PLLON;
541 
542     /* Wait till the main PLL is ready */
543     while((RCC->CR & RCC_CR_PLLRDY) == 0)
544     {
545     }
546    
547     /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
548     FLASH->ACR = FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
549 
550     /* Select the main PLL as system clock source */
551     RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
552     RCC->CFGR |= RCC_CFGR_SW_PLL;
553 
554     /* Wait till the main PLL is used as system clock source */
555     while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
556     {
557     }
558   }
559   else
560   { /* If HSE fails to start-up, the application will have wrong clock
561          configuration. User can add here some code to deal with this error */
562   }
563 
564 /************************************************************************/
565 /*                  I2S clock configuration                             */
566 /************************************************************************/
567   /* PLLI2S clock used as I2S clock source */
568   RCC->CFGR &= ~RCC_CFGR_I2SSRC;
569 
570   /* Configure PLLI2S */
571   RCC->PLLI2SCFGR = (PLLI2S_N << 6) | (PLLI2S_R << 28);
572 
573   /* Enable PLLI2S */
574   RCC->CR |= ((uint32_t)RCC_CR_PLLI2SON);
575 
576   /* Wait till PLLI2S is ready */
577   while((RCC->CR & RCC_CR_PLLI2SRDY) == 0)
578   {
579   }
580 }
581 
582 
583 void SystemInit(void)
584 {
585   /* FPU settings ------------------------------------------------------*/
586   #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
587     SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
588   #endif
589 
590   /* Reset the RCC clock configuration to the default reset state ------*/
591   /* Set HSION bit */
592   RCC->CR |= (uint32_t)0x00000001;
593 
594   /* Reset CFGR register */
595   RCC->CFGR = 0x00000000;
596 
597   /* Reset HSEON, CSSON and PLLON bits */
598   RCC->CR &= (uint32_t)0xFEF6FFFF;
599 
600   /* Reset PLLCFGR register */
601   RCC->PLLCFGR = 0x24003010;
602 
603   /* Reset HSEBYP bit */
604   RCC->CR &= (uint32_t)0xFFFBFFFF;
605 
606   /* Disable all interrupts */
607   RCC->CIR = 0x00000000;
608 
609 #ifdef DATA_IN_ExtSRAM
610   SystemInit_ExtMemCtl(); 
611 #endif /* DATA_IN_ExtSRAM */
612          
613   /* Configure the System clock source, PLL Multiplier and Divider factors, 
614      AHB/APBx prescalers and Flash settings ----------------------------------*/
615   SetSysClock();
616 
617   /* Configure the Vector Table location add offset address ------------------*/
618 #ifdef VECT_TAB_SRAM
619   SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
620 #else
621   SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
622 #endif
623 }
624 
625 void delay(u32 us)
626 {
627 }
628 
629 void udelay(u32 us)
630 {
631   delay(us*21); // 1 us
632 }
633 
634 void mdelay()
635 {
636 }
637 
638 
639 void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
640 {
641   /* Check the parameters */
642   //assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
643   //assert_param(IS_GPIO_PIN(GPIO_Pin));
644 
645   GPIOx->BSRRL = GPIO_Pin;
646 }
647 
648 
649 void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
650 {
651   /* Check the parameters */
652   //assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
653   //assert_param(IS_GPIO_PIN(GPIO_Pin));
654 
655   GPIOx->BSRRH = GPIO_Pin;
656 }
657 
658 
659 void Delay(uint32_t delay )
660 {
661   while(delay) delay--;
662 }
663 
664 /**
665  * @brief  Main program.
666  * @param  None
667  * @retval None
668  */
669 int main(void)
670 {
671   // set to 168M
672   SystemInit();
673 

703 
704   // systick 21M
705   // 1/21000000 會減 1
706   // int systick
707 
708   //SysTick->CTRL &= ~(1<<2);
709 
710 #if 1
712   SysTick->LOAD = INTERVAL;
713   SysTick->VAL = INTERVAL;

718 #endif
719   GPIO_InitTypeDef GPIO_InitStructure;
720   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
721 
722   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13| GPIO_Pin_14| GPIO_Pin_15;
723   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
724   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
725   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
726   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
727   GPIO_Init(GPIOD, &GPIO_InitStructure);
728 
729 
730   while(1)
731   {
732 
733     GPIO_SetBits(GPIOD, GPIO_Pin_12); // green led
734 
735     //Delay(0x3FFFFF);
736 
737 
738 #if 1
739     SysTick->VAL = INTERVAL;
740     SysTick->CTRL |= 1; // enable systick
741     while(1)
742     {
743       u32 tmp = SysTick->CTRL;
745       if ((tmp >> 16) & 1)
747       {
748         SysTick->CTRL = 0;
749         SysTick->VAL = 0;
750         break;
751       }
752     }
753 #endif
754     GPIO_ResetBits(GPIOD, GPIO_Pin_11 | GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
755     //Delay(0x3FFFFF);
756 
757 #if 1
758     SysTick->VAL = INTERVAL;
759     SysTick->CTRL |= 1; // enable systick
760     while(1)
761     {
762       u32 tmp = SysTick->CTRL;
763       //u32 tmp = (*(volatile u32*)0xe000e010);
764 
765       if ((tmp >> 16) & 1)
766       //if (SysTick->VAL == 0)
767       {
768         SysTick->CTRL = 0;
769         SysTick->VAL = 0;
770         break;
771       }
772     }
773 #endif
774 
783   }
784 }

soure code:
https://github.com/descent/stm32f4_prog/tree/master/my_timer

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。