Простой диспетчер с фиксированной очередью задач для PIC16

Меня посетила мысль, что если ты хочешь хорошо освоить какое-то семейство МК, надо собственноручно, на его ассемблере, написать для него какую-то управляющую систему. В идеале кооперативную ОС, по для начала можно и что-то попроще, типа местного динамического диспетчера. Я уже написал такой для MCS-51, но сейчас не о нем. Последнее время что-то внутри меня постоянно подталкивало изучить ПИКи. Наверное, чтобы можно было самому сравнить их с тем что я уже знаю (а знаю я уже MCS-51 немного, и AVR еще меньше), а не читать форумные баталии аля «AVR vs PIC». В процессе написания своей первой псевдо-ОС под ПИКи я немного разобрался с ними, и уже чувствую себя гораздо увереннее. Под катом результат работы — диспетчер с фиксированной очередью задач и службой на 8 (можно меньше) программных таймеров. Данный проект независимо (каждый со своей частотой) мигает 8 светодиодами на порту В. Проект занял 223 слова, что составляет 22% от доступного объема PIC16F84A. Критика приветствуется. В будущем планируется добавление динамической очереди задач для избавления от приставки «псевдо» перед ОС.


; Nedo_OS_v1.2
; Данная реализация диспетчера задач работает лишь на микроонтроллерах с обьемом памяти до 2 кб,
; так как дальнобойность команды goto соствляем 2 кб (одна страница)
; Для преодоления этого ограничения, необходим переход через промежуточную метку, для каждой задачи.
; Причем все промежуточные метки (своя для каждой задачи) должны находится в пределах 0 страницы (этих самых 2 кб)
; Начиная с версии 1.1 програмный таймер вынесен в обработчик Таймера0, что кардинально повышает точность,
; которая теперь не зависит от количества задач и количества команд в каждой задаче
; В версии 1.2 добавлены флаги запущеных таймеров, и декрементируются только те таймеры, флаги которых выставлены
; Таким образом мы можем выборочно запускать и останаваливать каждый таймер
; В данном проекте минимальная задержка - 1 ms, максимальная - 65536 ms, дискретность - 1 ms, точность 0,56%
; Тактовая частота МК - 4 МГц

	list p=pic16f84a
	__config	3FF1
	include	p16f84a.inc

Num_of_Task		equ	d'8'		; Количество задач, до 256 шт.
Num_of_Timers	equ	Num_of_Task	; Каждой задаче по таймеру, но всего их может быть не более 8 шт !!! Потому что в 1 байте 8 бит, а вводить еще 1 байт это перебор, слишком долго будем находится в обработчике прерывания Timer0

	cblock	0C                            ; Секция объявления переменных
w_temp
status_temp
Task_Counter					; Указатель на исполняемую задачу
Timer_Counter					; Счетчик таймеров
Active_Timers					; Флаги активных таймеров
Timer1							; Младший байт 1 таймера
Timer1_high						; Старший байт 1 таймера
Timer2
Timer2_high
Timer3
Timer3_high
Timer4
Timer4_high
Timer5
Timer5_high
Timer6
Timer6_high
Timer7
Timer7_high
Timer8
Timer8_high
	endc

Leds	equ	PORTB

push	macro
	movwf	w_temp
	swapf	STATUS,0
	movwf	status_temp
	endm

pop		macro
	swapf	status_temp,0
	movwf	STATUS
	swapf	w_temp,1
	swapf	w_temp,0
	endm

mov		macro	Timer,value		; Да, заново зададим таймер для этой задачи, в мс
	local	Inc_Low_Byte,Set_High_Byte	
	movlw	low(value)
	movwf	Timer
	movf	Timer					
	btfsc	STATUS,Z			; Младший байт равен 0 ?
	incf	Timer				; Да, инкрементируем его  
	movlw	high(value)+1		; Нет, оставляем как есть, а старший инкрементируем в любом случае, потому что в обработчике таймеров мы его декрементируем минимум 1 раз
	movwf	Timer+1
	endm

	org	0
	goto	Ini

	org	4						; Вектор прерывания
Timer0							; У нас только Timer0, поетому не делаем никакой идентификации источника прерывания
	push						; Сохраним контекст
	movlw	Timer1				; Загружаем указатель на младший байт 1 таймера
	movwf	FSR
	movlw	Num_of_Timers		; Загружаем количество програмных таймеров
	movwf	Timer_Counter
Next_Timer
	btfsc	Active_Timers,0		; Проверям 0 бит, он показывает, запущен ли этот таймер		
	decfsz	INDF				; Декремент младшего байта этого таймера
	goto	No_Dec_High_Byte	; Не 0, пропускам декремент старшего байта
	incf	FSR					; Младший байт 0, берем старший байт этого таймера						
	decfsz	INDF				; Декремент старшего байта этого таймера	
	goto	Yes_Dec_High_Byte	; Старший байт еще не 0, идем брать следующий таймер
	bcf		Active_Timers,0		; И сбросим флажок во флагах запущеных таймеров, чтобы в дальшнейшем пропускать этот таймер							
	goto	Yes_Dec_High_Byte	; Поставили флажок, идем проверять следующий	
No_Dec_High_Byte
	incf	FSR					; Пропускаем старший байт этого таймера
Yes_Dec_High_Byte
	incf	FSR					; Берем следующий таймер
	rrf		Active_Timers		; Сдвинем в 0 бит состояние флага старта следующего таймера, выдавив состояние текущего в С
	btfss	STATUS,C			; Проверим С на сброшеность
	bcf		Active_Timers,7		; Сброшен, сбросим вручную 7 бит запущеных таймеров, как будто сделали вращение без переноса
	btfsc	STATUS,C			; Проверим С на установленность
	bsf		Active_Timers,7		; Установлен, установим вручную 7 бит запущенных таймеров, как будто сделали вращение без переноса
	decfsz	Timer_Counter		; Все таймеры проверили ?
	goto	Next_Timer			; Нет, еще остались		
	movlw	d'37'				; Скорректируем системный таймер для периода переполнения 1 мс
	movwf	TMR0
	pop							; Восстановим контекст	
	bcf		INTCON,T0IF			; Сбросим флаг прерывания Таймера 0
	retfie

Ini								; Предварительная загрузка начальных значений	
	mov		Timer1,d'500'
	mov		Timer2,d'1000'
	mov		Timer3,d'2000'
	mov		Timer4,d'4000'
	mov		Timer5,d'8000'
	mov		Timer6,d'16000'
	mov		Timer7,d'32000'
	mov		Timer8,d'64000'
	movlw	b'11111111'			; Запускаем таймеры
	movwf	Active_Timers
	bsf		STATUS,RP0			; Банк 1
        movlw	b'00000000'
	movwf	TRISB				; Порт В на вывод
	movlw	b'00000001'			; Тактирвание Таймера 0 от внутреннего генератора, пределитель 1 к 4, что дает 1,024 мс на частоте 4 Мгц
	movwf	OPTION_REG
	bcf		STATUS,RP0			; Банк 0		
	movlw	Num_of_Task-1h		; Определение числа задач
	movwf	Task_Counter
	clrf	Leds				; Погасим все светодиоды
	movlw	b'10100000'			; Разрешаем прерывание по переполнению Таймера 0 и прерывания вообще
	movwf	INTCON

Main
	incf	Task_Counter		; Следующая задача
	movlw	Num_of_Task			; Загружаем общее количство задач
	subwf	Task_Counter,w 
	btfss	STATUS,Z			; Все задачи выполнены ?
	goto	Task_Table			; Нет, идем выполнять  
	clrf	Task_Counter		; Да, сбросим счетчик задач
Task_Table
	movfw	Task_Counter		; Загрузим номер задачи,
	addwf	PCL					; и перейдем на нее
	goto	Task_1
	goto	Task_2
	goto	Task_3
	goto	Task_4
	goto	Task_5
	goto	Task_6
	goto	Task_7
	goto	Task_8

Task_1
	btfsc	Active_Timers,0		; Проверяем отработал ли наш таймер	
	goto	Main				; Нет, идем пробовать следующую задачу
	mov		Timer1,d'500'		; Да, заново зададим таймер для этой задачи, в мс
	movlw	b'00000001'			; Делаем полезную работу
	xorwf	Leds
	bsf		Active_Timers,0		; Запускаем наш таймер
	goto	Main

Task_2
	btfsc	Active_Timers,1		; Проверяем отработал ли наш таймер	
	goto	Main				; Нет, идем пробовать следующую задачу
	mov		Timer2,d'1000'		; Да, заново зададим таймер для этой задачи, в мс
	movlw	b'00000010'			; Делаем полезную работу
	xorwf	Leds
	bsf		Active_Timers,1		; Запускаем наш таймер
	goto	Main

Task_3
	btfsc	Active_Timers,2		; Проверяем отработал ли наш таймер	
	goto	Main				; Нет, идем пробовать следующую задачу
	mov		Timer3,d'2000'		; Да, заново зададим таймер для этой задачи, в мс
	movlw	b'00000100'			; Делаем полезную работу
	xorwf	Leds
	bsf		Active_Timers,2		; Запускаем наш таймер
	goto	Main

Task_4
	btfsc	Active_Timers,3		; Проверяем отработал ли наш таймер	
	goto	Main				; Нет, идем пробовать следующую задачу
	mov		Timer4,d'4000'		; Да, заново зададим таймер для этой задачи, в мс
	movlw	b'00001000'			; Делаем полезную работу
	xorwf	Leds
	bsf		Active_Timers,3		; Запускаем наш таймер
	goto	Main

Task_5
	btfsc	Active_Timers,4		; Проверяем отработал ли наш таймер	
	goto	Main				; Нет, идем пробовать следующую задачу
	mov		Timer5,d'8000'		; Да, заново зададим таймер для этой задачи, в мс
	movlw	b'00010000'			; Делаем полезную работу
	xorwf	Leds
	bsf		Active_Timers,4		; Запускаем наш таймер
	goto	Main

Task_6
	btfsc	Active_Timers,5		; Проверяем отработал ли наш таймер	
	goto	Main				; Нет, идем пробовать следующую задачу
	mov		Timer6,d'16000'		; Да, заново зададим таймер для этой задачи, в мс
	movlw	b'00100000'			; Делаем полезную работу
	xorwf	Leds
	bsf		Active_Timers,5		; Запускаем наш таймер
	goto	Main

Task_7
	btfsc	Active_Timers,6		; Проверяем отработал ли наш таймер	
	goto	Main				; Нет, идем пробовать следующую задачу
	mov		Timer7,d'32000'		; Да, заново зададим таймер для этой задачи, в мс
	movlw	b'01000000'			; Делаем полезную работу
	xorwf	Leds
	bsf		Active_Timers,6		; Запускаем наш таймер
	goto	Main

Task_8
	btfsc	Active_Timers,7		; Проверяем отработал ли наш таймер	
	goto	Main				; Нет, идем пробовать следующую задачу
	mov		Timer8,d'64000'		; Да, заново зададим таймер для этой задачи, в мс
	movlw	b'10000000'			; Делаем полезную работу
	xorwf	Leds
	bsf		Active_Timers,7		; Запускаем наш таймер
	goto	Main

	end
  • +2
  • 02 июля 2016, 21:59
  • Gnusmas

Комментарии (0)

RSS свернуть / развернуть
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.