سیسوگ در قسمت هفدهم آموزش میکروکنترلر STM8 تایمر 2 را به شما معرفی کرد. در این قسمت از مجموعه مقالات آموزش میکروکنترلر STM8 قصد دارد تایمر 4 را آموزش دهد. با سیسوگ همراه باشید.
تایمر 4 :
در میکروکنترلر، تایمر 4 نسبت به دیگر تایمرها از اهمیت بالاتری برخوردار است. این مقاله، نحوه اسکن و نمایش اطلاعات روی سون سگمنت با استفاده از وقفه را نشان می دهد، در حالیکه حلقه اصلی می تواند اطلاعات را نمایش دهد.
اتصالات سخت افزاری
نمونه کد تایمر 4
main.c
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 | #include "STM8S.h" unsigned int value = 0x00; unsigned char n = 0x00; const unsigned char num[0x0A] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F}; void GPIO_setup(void); void clock_setup(void); void TIM4_setup(void); void main(void) { int j=0; GPIO_setup(); clock_setup(); TIM4_setup(); while(TRUE) { value++; for(j=0;j<0x3FFF;j++); }; } void GPIO_setup(void) { GPIO_DeInit(GPIOC); GPIO_DeInit(GPIOD); GPIO_DeInit(GPIOB); GPIO_Init(GPIOD, ((GPIO_Pin_TypeDef)(GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5)), GPIO_MODE_OUT_PP_LOW_FAST); GPIO_Init(GPIOD, GPIO_PIN_1| GPIO_PIN_6, GPIO_MODE_OUT_PP_LOW_FAST); GPIO_Init(GPIOC, GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 , GPIO_MODE_OUT_PP_LOW_FAST); GPIO_Init(GPIOB, GPIO_PIN_4 | GPIO_PIN_5, GPIO_MODE_OUT_PP_LOW_FAST); } void clock_setup(void) { CLK_DeInit(); CLK_HSECmd(DISABLE); CLK_LSICmd(DISABLE); CLK_HSICmd(ENABLE); while(CLK_GetFlagStatus(CLK_FLAG_HSIRDY) == FALSE); CLK_ClockSwitchCmd(ENABLE); CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV8); CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1); CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSI, DISABLE, CLK_CURRENTCLOCKSTATE_ENABLE); CLK_PeripheralClockConfig(CLK_PERIPHERAL_SPI, DISABLE); CLK_PeripheralClockConfig(CLK_PERIPHERAL_I2C, DISABLE); CLK_PeripheralClockConfig(CLK_PERIPHERAL_ADC, DISABLE); CLK_PeripheralClockConfig(CLK_PERIPHERAL_AWU, DISABLE); CLK_PeripheralClockConfig(CLK_PERIPHERAL_UART1, DISABLE); CLK_PeripheralClockConfig(CLK_PERIPHERAL_TIMER1, DISABLE); CLK_PeripheralClockConfig(CLK_PERIPHERAL_TIMER2, DISABLE); CLK_PeripheralClockConfig(CLK_PERIPHERAL_TIMER4, ENABLE); } void TIM4_setup(void) { TIM4_DeInit(); TIM4_TimeBaseInit(TIM4_PRESCALER_32, 128); TIM4_ITConfig(TIM4_IT_UPDATE, ENABLE); TIM4_Cmd(ENABLE); enableInterrupts(); } |
(stm8s_it.h (top part only
1 2 3 4 5 6 | #ifndef __STM8S_IT_H #define __STM8S_IT_H @far @interrupt void TIM4_UPD_IRQHandler(void); /* Includes ------------------------------------------------------------------*/ #include "stm8s.h" |
stm8s_it.c
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 | #include "stm8s.h" #include "stm8s_it.h" extern unsigned int value; extern unsigned char n; extern unsigned char seg; extern const unsigned char num[10]; #define SEG_A 1 #define SEG_B 2 #define SEG_C 4 #define SEG_D 8 #define SEG_E 16 #define SEG_F 32 #define SEG_G 64 #define SegNum_0 (SEG_A|SEG_B|SEG_C|SEG_D|SEG_E|SEG_F) #define SegNum_1 (SEG_B|SEG_C) #define SegNum_2 (SEG_A|SEG_B|SEG_D|SEG_E|SEG_G) #define SegNum_3 (SEG_A|SEG_B|SEG_C|SEG_D|SEG_G) #define SegNum_4 (SEG_B|SEG_C|SEG_F|SEG_G) #define SegNum_5 (SEG_A|SEG_C|SEG_D|SEG_F|SEG_G) #define SegNum_6 (SEG_A|SEG_C|SEG_D|SEG_E|SEG_F|SEG_G) #define SegNum_7 (SEG_A|SEG_B|SEG_C) #define SegNum_8 (SEG_A|SEG_B|SEG_C|SEG_D|SEG_E|SEG_F|SEG_G) #define SegNum_9 (SEG_A|SEG_B|SEG_C|SEG_D|SEG_F|SEG_G) void SegWriteByte(char Data) { int i; char Pin[] = {GPIO_PIN_1,GPIO_PIN_7,GPIO_PIN_6,GPIO_PIN_5,GPIO_PIN_4,GPIO_PIN_3,GPIO_PIN_6}; GPIO_TypeDef *GPIOx[]= {GPIOD, GPIOC, GPIOC, GPIOC, GPIOC, GPIOC, GPIOD}; for( i=0;i<7;i++) { if(Data&1) { GPIOx[i]->ODR |=Pin[i]; } else { GPIOx[i]->ODR &=~Pin[i]; } Data>>=1; } GPIO_WriteHigh(GPIOB, GPIO_PIN_4); } void TIM4_UPD_IRQHandler(void) { static char index = 0; GPIOD->ODR &=~((GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5)); switch(index++) { case 0: n = (value / 1000); SegWriteByte(num[n]); GPIOD->ODR |=(GPIO_PIN_5); break; case 1: n = ((value / 100) % 10); SegWriteByte(num[n]); GPIOD->ODR |=(GPIO_PIN_4); break; case 2: n = ((value / 10) % 10); SegWriteByte(num[n]); GPIOD->ODR |=(GPIO_PIN_3); break; case 3: n = (value % 10); SegWriteByte(num[n]); GPIOD->ODR |=(GPIO_PIN_2); break; } if(index>3) index = 0; TIM4_ClearFlag(TIM4_FLAG_UPDATE); } |
stm8_interrupt_vector.c
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 | /* BASIC INTERRUPT VECTOR TABLE FOR STM8 devices * Copyright (c) 2007 STMicroelectronics */ #include "stm8s_it.h" typedef void @far (*interrupt_handler_t)(void); struct interrupt_vector { unsigned char interrupt_instruction; interrupt_handler_t interrupt_handler; }; @far @interrupt void NonHandledInterrupt (void) { /* in order to detect unexpected events during development, it is recommended to set a breakpoint on the following instruction */ return; } extern void _stext(); /* startup routine */ //@far @interrupt void TIM4_UPD_IRQHandler(void) ; struct interrupt_vector const _vectab[] = { {0x82, (interrupt_handler_t)_stext}, /* reset */ {0x82, NonHandledInterrupt}, /* trap */ {0x82, NonHandledInterrupt}, /* irq0 */ {0x82, NonHandledInterrupt}, /* irq1 */ {0x82, NonHandledInterrupt}, /* irq2 */ {0x82, NonHandledInterrupt}, /* irq3 */ {0x82, NonHandledInterrupt}, /* irq4 */ {0x82, NonHandledInterrupt}, /* irq5 */ {0x82, NonHandledInterrupt}, /* irq6 */ {0x82, NonHandledInterrupt}, /* irq7 */ {0x82, NonHandledInterrupt}, /* irq8 */ {0x82, NonHandledInterrupt}, /* irq9 */ {0x82, NonHandledInterrupt}, /* irq10 */ {0x82, NonHandledInterrupt}, /* irq11 */ {0x82, NonHandledInterrupt}, /* irq12 */ {0x82, NonHandledInterrupt}, /* irq13 */ {0x82, NonHandledInterrupt}, /* irq14 */ {0x82, NonHandledInterrupt}, /* irq15 */ {0x82, NonHandledInterrupt}, /* irq16 */ {0x82, NonHandledInterrupt}, /* irq17 */ {0x82, NonHandledInterrupt}, /* irq18 */ {0x82, NonHandledInterrupt}, /* irq19 */ {0x82, NonHandledInterrupt}, /* irq20 */ {0x82, NonHandledInterrupt}, /* irq21 */ {0x82, NonHandledInterrupt}, /* irq22 */ {0x82, (interrupt_handler_t)TIM4_UPD_IRQHandler}, /* irq23 */ {0x82, NonHandledInterrupt}, /* irq24 */ {0x82, NonHandledInterrupt}, /* irq25 */ {0x82, NonHandledInterrupt}, /* irq26 */ {0x82, NonHandledInterrupt}, /* irq27 */ {0x82, NonHandledInterrupt}, /* irq28 */ {0x82, NonHandledInterrupt}, /* irq29 */ }; |
توضیحات
کلاک داخلی و CPU هر دو روی 2 مگاهرتز کار می کنند.
1 2 3 4 5 | CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV8); CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1); .... …. CLK_PeripheralClockConfig(CLK_PERIPHERAL_TIMER4, ENABLE); |
تایمر 4 یک تایمر پایه است و بهتر است آن را برای کارهای ساده استفاده کنیم. ما آن را با مقدار پیش فرض 32 و بارگیری شمارنده روی 128 تنظیم کردیم. این مقادیر باعث می شود تایمر 4 سرریز کند و وقفه ای 2ms ای ایجاد کند. که این مدت زمان برای نمایش اطلاعات بر روی یک سون سگمنت کافی است. در اینجا 4 سون سگمنت در طول 8ms به روز می شوند ولی چشم شما تمامی اطلاعات را در یک لحظ می بیند، در واقع در اینجا به مغز انسان حقه میزنیم. در نهایت، باید وقفه های که مورد نیاز هستند را فعال کنیم.
1 2 3 4 5 6 7 8 9 | void TIM4_setup(void) { TIM4_DeInit(); TIM4_TimeBaseInit(TIM4_PRESCALER_32, 128); TIM4_ITConfig(TIM4_IT_UPDATE, ENABLE); TIM4_Cmd(ENABLE); enableInterrupts(); } |
اولین مثال از وقفه ها در مجموعه آموزش میکروکنترلر STM8 را به یاد می آورید؟ همان طور که در پست های قبل دیدیم ما باید به کامپایلر بگوییم که از کدام وقفه استفاده می کنیم. با توجه به دیتاشیت مشاهده می شود که آپدیت و سرریز تایمر 4 در IRQ4 قرار دارد. بنابراین باید این تغییرات را در فایل stm8_interrupt_vector.c انجام دهیم:
1 | {0x82, (interrupt_handler_t)TIM4_UPD_IRQHandler}, /* irq23 */ |
به یاد داشته باشید که چون در این کد از وقفه استفاده می کنیم فایل های هدر و سورس وقفه را اضافه کنید. درون ISR، اسکن هر سون سگمنت را انجام می دهیم. هربار که وقفه سرریز کند سون سگمنت تغییر می کند. در پایان ISR یک شماره افزایش می یابد تا در هنگام سرریز جدید صفحه نمایش بعدی را انتخاب کند. در داخل Switch-Case، سون سگمنت ها را فعال و درباره مقداری را که باید نشان دهند تصمیم می گیریم. سرانجام پرچم سرریز/به روز رسانی پاک می شود.
سیسوگ در قسمت نوزدهم از مجموعه آموزش میکروکنترلر STM8 تایمر 2 (PWM) را مورد بررسی قرار میدهد. با سیسوگ همراه باشید.
لینکهای دانلود
مطالب مرتبط
- قسمت اول − میکروکنترلر STM8 چیست و از کجا آمده است؟
- قسمت دوم − معرفی بردهای Discovery میکروکنترلر STM8
- قسمت سوم − کامپایلر و پروگرامر
- قسمت چهارم − STM8CubeMX
- قسمت پنجم − آماده سازی ابزارهای نرمافزاری برای STM8
- قسمت ششم − چگونه برنامه خود را روی STM8 آپلود کنیم؟
- قسمت هفتم− LED چشمکزن
- قسمت هشتم − کلاک سیستم (CLK)
- قسمت نهم − وقفه خارجی (EXTI)
- قسمت دهم − Beeper
- قسمت یازدهم − LCD کاراکتری
- قسمت دوازدهم − مبدل آنالوگ به دیجیتال (ADC)
- قسمت سیزدهم − تایمر نگهبان آنالوگ (AWD)
- قسمت چهادهم − تایمر نگهبان (IWDG)
- قسمت پانزدهم − تایمر نگهبان محدوده ای (WWDG)
- قسمت شانزدهم− اصول اولیه تایمرها
- قسمت هفدهم− تایمر 2
- قسمت نوزدهم −PWM
- قسمت بیستم − PWM تایمر 1
- قسمت بیست و یکم − بررسی اجمالی ارتباطات
- قسمت بیست و دوم − رابط سریال (UART)
- قسمت بیست و سوم − رابط کاربری سریال (SPI)
- قسمت بیست و چهارم − رابط I2C
- دوره رایگان آموزش میکروکنترلر STM8 – سیسوگ
سلام وقت بخیر
حرف extern قبل تعریف متغییر ها به چه معناست؟
سلام دوست عزیز
به این معنی است که این متغییر جای دیگری (در فایل یا کتابحانه دیگری ) تعریف شده است و صرفا اینجا قصد استفاده از آن را داریم.
سلام چرا stm8s_it توی peripheral لایبرری نیست؟ stm8s_itc هست ولی it نیست. البته جای دیگه من این فایلو پیدا کردم ولی سوال این هست که اگه قرار هست از پریفرال ها استفاده کنیم چرا کامل نیستند..
سلام بسیار عالی
امکانش هست خود فایل کامل پروژه رو هم بصورت rar پایان آموزش بزارید !!؟
سلام دوست عزیز
پیشنهاد خوبی هست – انشالله در اموزش های بعدی لحاظ میکنیم
متشکرم برای حسن توجه شما
سلام ممنون از مطالب خوبتون و تشکر از زحماتتون
من این برنامه رو اجرا کردم و یه ایرادی تو کد وجود داره،پایه B4 مربوط به حرف g سگمنته اما تو خط 33 و 34 فایل stm8-it.c پایه d6 رو دیفاین کرده در حالیکه به جایی وصل نیست
سلام – ممنون برای نکته بینی که دارید – بله درسته – عکس رو اصلاح می کنیم
بعد از خوندن مطلب به این فکر افتادم که الان بنده دقیقا باید چیکار کنم ؟
چرا تو فایل main تابع دستورات عملکرد وقته وجود نداره ؟
چرا باید تو فایل “stm8s_it.c” اون کد هارو وارد کنیم درصورتی که اصولا باید توی main باشند . اصلا یه فایل به همین اسم به پیشفرض تو لایبرری ها هست با مقادیر کاملا متفاوت .
توی تلگرام بهتون پیام دادم گفتم که این اموزشات مربوط به stm8 مخصوصا مخصوصا وقفه خارجه و داخلی ، شدیدا نیاز به باز بینی دارند. چون ممکنه یک تازه کار مثل من بیاد هیچی حالیش نشه .
سلام – البته قبول دارم مقداری توضیحات اجمالی بود ولی اینطور هم نبود که مخاطب چیزی برداشت نکنه 😐
اما این که وقفه ها رو توی یه فایل جدا میذاریم تنها برای مدیریت بهتر است اگر نه به سادگی میتونید همونطور که گفتید توی مین برنامه هم تعریفشون کنید.
در مورد مباحث اینتراپت – مجددا نگاه میکنم امیداورم که بتونیم بهترش کنیم.
متشکرم