با سلام، در این قسمت از آموزشها میریم سراغ مبحث وقفهها در میکروکنترلر CH32 وقفهها بخش مهمی از میکروکنترلرها هستند که در نوشتن یک برنامه کامل میتونن همهجوره به کمک ما بیان، اگر بخوایم یک توضیح مختصری در مورد وقفهها بگم به این شکل هست که باعث میشه CPU کاری که داره انجام میده رو ول کنه و کاری که در وقفه تعیین شده رو انجام بده، این وقفهها میتونن از هر قسمتی به CPU برسن، مثلاً گاهی واحد ADC تبدیلش تموم شده و به CPU میگه که من تبدیلم تموم شد بیا دیتا رو ببر؛ ولی اگر نبود CPU هی باید واحد ADC رو چک میکرد که آیا تبدیلت تموم شده یا نه؟ اگه اره دیتا رو میبره، اگه نه میره بعد دوباره میاد چک میکنه که این کار وقت CPU رو میگیره، اینجاست که وقفهها به کمکمون میان.
اولازهمه باید خدمتتون بگم که این واحد جزو پردازنده هست و نیاز به کلاک جداگونه ای ندارد، تنها کار ما با این پریفرال به این صورت هست که کجا و کدوم وقفه به چه علتی برسه به میکرو که جلوتر توضیح میدیم که به چه صورت این کارو انجام بدیم.
طبق گفتههای دیتاشیت این سری از میکرو ها که باهاشون کار میکنیم دو جور واحد وقفه دارن این میکرو ها برای سری CH32F2XX به نام NVIC و برای میکرو های CH32V2XX , CH32V3XX یکواحدی جایگزینکردن به نام PFIC
که برخی ویژگی هاش فرق دارن که در شکل زیر میتونید ببینید:
انواع واحد وقفه CH32F2XX
ولی خب نگران یک واحد جدید و ناشناخته نباشید و داخل توابع خودشون تبدیلش کردن به توابع استاندارد NVIC .
شما هیچ تابع آماده که بر پایه PFIC باشه پیدا نمیکنید، اگر حتماً اسرار به استفاده از آنها رو دارید باید با رجیستر مستقیم به واحد PFIC دستور بدید و رجیستری باهاش کار کنید.
خب بریم سراغ خود توابع،
تابع NVIC_SystemReset() هست که مشخصه برای ریستکردن میکرو ازش استفاده میکنید، پس از ران شدن این خط از برنامه میکرو به خونه صفر برمیگرده و میکرو از اول برنامه شروع به کار میکنه.
توابع ENABLE و Disable رو داریم که وقفه رو فعال یا غیرفعال میکنن.
توابعی که اول آنها GET دارند نشاندهنده وضعیت رجیسترهای داخلی واحد وقفه هستند برای مثال NVIC_GetActive()
وضعیت فعالبودن یا نبودن وقفه رو بهتون نشون میده، اگه یک برگرداند؛ یعنی وقفه فعال هست و اگه صفر برگرداند؛ یعنی وقفه غیرفعال هست.
تابع NVIC_Init رو درصورتیکه میخواهید از پارامترهای داخل استراکت اش استفاده کنید، ازش استفاده کنید وگرنه بدون این تابع هم وقفه میتونه راهاندازی بشه توسط NVIC_EnableIRQ ()، حالا این پارامترهای داخل استراکت چی هستن؟
این موارد عکس بالا هستن که تقریباً همش رو میشه با توابع دیگه هم تنظیم کرد. داخل نمونه کد بیشتر متوجه میشید.
با استفاده از توابع NVIC_SetPriority() میتونید به وقفهها اولویت بدید که در صورت رویدادن دو وقفه همزمان کدامیک باید اول اجرا بشه و کدوم یک باید در صف انتظار باقی بمونه.
این تابع NVIC_PriorityGroupConfig() برای گروهبندی اولویتها استفاده میشود که در عکس زیر توضیحاتش هست.
داخل کد نمونه طریقه استفاده از این توابع رو متوجه میشوید.
این کد یک وقفه روی GPIO ورودی هست، برای شروع یک GPIO رو ورودی میکنیم برای وقفه و یکی رو خروجی میکنیم تا بتونیم خروجی وقفه رو با یک LED نمایش بدیم، به این صورت: (فعالکردن کلاک GPIO مدنظر فراموش نشه).
در مرحله بعدی باید تنظیمات وقفه GPIO یا همون واحد EXTI رو انجام بدیم که بهصورت زیر هست:
در اینجا مقدار EXTI_Trigger مشخص میکنه که روی کدوم لبه سیگنالمون وقفه اتفاق بیفته که اینجا لبه پایینرونده انتخاب شده است، شما میتوانید لبه بالاروند و هر دو لبه رو نیز جایگزین کنید.
از EXTI_Line برای انتخاب اینکه توسط کدوم لاین وقفه روی بده استفاده میشه، در اینجا میتونید استفاده از هر لاین رو ببینید:
در مقدار Mode میتونید اینکه این رویداد یک وقفه باشه یا یک Event رو هم مشخص کنید که ما وقفه رو انتخاب میکنیم.
در مقدار CMD هم فعالبودن یا نبودن واحد EXTI رو مشخص میکنیم. سپس مقادیر را به تابع EXTI_Init پاس میدیم.
پس از این واحد نوبت خود واحد NVIC هست که بهصورت زیر تنظیمش میکنیم برای بالاترین اولویت برای EXTI_0:
خب بعدازاین کار دیگه تنظیمات اصلی ما تموم شده و فقط میمونه خود روتین وقفه که راههای زیادی داره که یکی از راه هاش اینکه بیایم و اسم تابع رو از فایل Startup برداریم و ازش استفاده کنیم به این صورت:
من خود اسم تابع رو کپی میکنم و میارم داخل main اصلی برنامه و یک void اولش میزارم به این صورت:
این تابع وقفه ما هست فقط دوتا نکته داره، اولین نکته که باید پرچم وقفه صفر بشه که پس از یک با اتفاقافتادن وقفه گیر نکنه و همیشه ادامه پیدا کنه که تابع EXTI_ClearFlag این کارو اینجا برای ما انجام میده، نکته دوم هم این است که باید تابع رو بالاتر در داخلmain تعریف کنید با ویژگی وقفه که خط زیر برای ما انجامش میده:
1 | void EXTI0_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); |
خب این کل کد ما برای وقفه بود، ازاینپس برای هر لبه پایینرونده روی پایه A0 پایه A1 تغییر وضعیت میده.
کد کامل مثال بالا:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | /********************************** (C) COPYRIGHT ******************************* * File Name : main.c * Author : WCH * Version : V1.0.0 * Date : 2021/06/06 * Description : Main program body. ********************************************************************************* * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. * Attention: This software (modified or not) and binary are used for * microcontroller manufactured by Nanjing Qinheng Microelectronics. *******************************************************************************/ /* *@Note USART Print debugging routine: USART1_Tx(PA9). This example demonstrates using USART1(PA9) as a print debug port output. */ #include "debug.h" /* Global typedef */ /* Global define */ void EXTI0_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); /* Global Variable */ char state =0; /********************************************************************* * @fn main * * @brief Main program. * * @return none */ int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); SystemCoreClockUpdate(); Delay_Init(); USART_Printf_Init(115200); printf("SystemClk:%d\r\n",SystemCoreClock); printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() ); printf("This is printf example\r\n"); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); /////////////////////GPIO A pin0 set as input pullup and pin 1 set as output GPIO_InitTypeDef GPAIP_CFG; GPAIP_CFG.GPIO_Mode = GPIO_Mode_IPU; GPAIP_CFG.GPIO_Pin = GPIO_Pin_0; GPIO_Init(GPIOA,&GPAIP_CFG); GPIO_InitTypeDef GPAPP_CFG; GPAPP_CFG.GPIO_Mode = GPIO_Mode_Out_PP; GPAPP_CFG.GPIO_Pin = GPIO_Pin_1; GPAPP_CFG.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init(GPIOA,&GPAPP_CFG); /////////////////////EXTI EXTI_InitTypeDef EXTI_CFG; EXTI_CFG.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_CFG.EXTI_Line = EXTI_Line0; //select GPIO Pin 0 EXTI_CFG.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_CFG.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_CFG); //////////////////////NVIC NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); NVIC_SetPriority(EXTI0_IRQn, 1); NVIC_EnableIRQ(EXTI0_IRQn); while(1) { } } void EXTI0_IRQHandler(void) { EXTI_ClearFlag(EXTI_Line0); state=!state; GPIO_WriteBit(GPIOA,GPIO_Pin_1,state); } |
در ادامه یک کد نمونه وقفه با تایمر رو هم ببینید، زیرا ما هم واحد وقفه رو توضیح دادیم هم واحد تایمر رو.
از تایمر میشه وقفههای زیادی گرفت در اینجا وقفه سرریز مثال زده شده:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | /********************************** (C) COPYRIGHT ******************************* * File Name : main.c * Author : WCH * Version : V1.0.0 * Date : 2021/06/06 * Description : Main program body. ********************************************************************************* * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. * Attention: This software (modified or not) and binary are used for * microcontroller manufactured by Nanjing Qinheng Microelectronics. *******************************************************************************/ /* *@Note USART Print debugging routine: USART1_Tx(PA9). This example demonstrates using USART1(PA9) as a print debug port output. */ #include "debug.h" /* Global typedef */ void TIM2_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); /* Global define */ /* Global Variable */ char State = 0; /********************************************************************* * @fn main * * @brief Main program. * * @return none */ int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); SystemCoreClockUpdate(); Delay_Init(); USART_Printf_Init(115200); printf("SystemClk:%d\r\n",SystemCoreClock); printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() ); printf("This is printf example\r\n"); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitTypeDef GPCFG; GPCFG.GPIO_Mode = GPIO_Mode_Out_PP; GPCFG.GPIO_Pin = GPIO_Pin_0; GPCFG.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPCFG); /////////////////////////////////////////////////////////////////////// TIM_TimeBaseInitTypeDef TIMBCFG; TIMBCFG.TIM_ClockDivision = TIM_CKD_DIV4; TIMBCFG.TIM_CounterMode = TIM_CounterMode_Up; TIMBCFG.TIM_Period = 500-1; TIMBCFG.TIM_Prescaler = 96-1; TIMBCFG.TIM_RepetitionCounter = 0; TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); TIM_TimeBaseInit(TIM2,&TIMBCFG); TIM_Cmd(TIM2,ENABLE); NVIC_EnableIRQ(TIM2_IRQn); while(1) { } } void TIM2_IRQHandler(void) { TIM_ClearFlag(TIM2,TIM_FLAG_Update); State=!State; GPIO_WriteBit(GPIOA,GPIO_Pin_0,State); } |
تا قسمت بعدی شما را به خدا میسپارم.
نویسنده شو !
سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.