9. Функции и подпрограммы



Автор: WildCat

Подпрограммы и функции используются, когда у вас есть несколько одинаковых фрагментов кода в различных частях программы. Допустим, у вас есть такая программа:
 

Dim ADCval as byte
Dim ADCchannel as byte
Const ADCreadaddress = &H66


'Read first ADC channel
ADCchannel=0
I2cstart
I2cwbyte ADCreadaddress
I2cwbyte ADCchannel
I2crbyte ADCval
Print "ADC voltage on channel: " ; ADCchannel; " is: " ; ADCval
'Read second ADC channel
ADCchannel=1
I2cstart
I2cwbyte ADCreadaddress
I2cwbyte ADCchannel
I2crbyte ADCval
Print "ADC voltage on channel: " ; ADCchannel; " is: " ; ADCval
Её можно записать так:

Dim ADCval as byte
Dim ADCchannel as byte

Const ADCreadaddress = &H66


Declare Sub ADCread(byval ADCchannel as byte, ADCval as byte)
'Read first ADC channel
ADCchannel=0
Call ADCread(AdDCchannel, ADCval)
Print "ADC voltage on channel: " ; ADCchannel; " is: " ; ADCval
'Read second ADC channel
ADCchannel=1
Call ADCread(AdDCchannel, ADCval)
Print "ADC voltage on channel: " ; ADCchannel; " is: " ; ADCval


Sub ADCread(byval ADCchannel as byte, ADCval as byte)
  I2cstart
  I2cwbyte ADCreadaddress
  I2cwbyte ADCchannel
  I2crbyte ADCval
End Sub
Во втором случае, подпрограмма - единственное место, где считывается значение АЦП. В основной программе дважды происходит вызов подпрограммы - для считывания значений 0 и 1 каналов АЦП.
Преимущество подпрограммы в том, что повторяющиеся участки кода используются в тексте программы только один раз. Если вдруг потребуется изменить код, это придётся сделать лишь один раз. Когда используется много повторяющихся фрагментов, вам придется изменять каждый из них. Однажды вы таки забудете исправить один из фрагментов и будете очень долго искать проблему.
Подпрограмма вызывается таким образом:

Call ADCread(ADCchannel, ADCval)

Подпограмма имеет название и может иметь дополнительные параметры. В данном случае это ADCchannel и ADCval. подпограмма использует параметр ADCchannel, чтобы считать значение с нужного канала. Считанное значение помещается в ADCval и возвращается подпрограммой по достижении её конца - End Sub. Поэтому параметры могут работать в обоих направлениях - от основной программы к подпрограмме и наоборот.

Можно указать подпрограмме, что данные ей параметры нужно оставить неизмененными. Для этого при объявлении используется команда Byval. Тогда BASCOM создаст в памяти копию этой переменной и будет работать с ней. Исходная переменная останется нетронутой, что бы не происходило с ней в подпрограмме. В вышеописанном примере с переменной ADCchannel не происходят изменения в подпрограмме, так как она специально описана в объявлении подпрограммы.
Обратный параметр - Byref - устанавливается по умолчанию. Его можно не указывать. В том случае все изменения, которые подпрограмма делает с переменной, останутся после её работы.
Как вы уже поняли, подпограммы, как и переменные, должны быть объявлены в программе заранее:

Declare Sub ADCread(byval ADCchannel, as byte, ADCval as byte)

Вот еще замечательный пример использования подпрограмм. Подключите ЖКИ к AT90S2313 по стандартной схеме компилятора и наблюдайте за работой программы (или можете сделать это в симуляторе).

$regfile = "2313def.dat"
$crystal = 4000000

Const Barmaxchar = 16
Dim Forcounter As Byte
Dim Logval As Byte

Declare Sub Bar(logvalue As Byte)

Deflcdchar 0 , 14 , 10 , 14 , 21 , 14 , 10 , 17 , 17        'jumping man-a
Deflcdchar 1 , 14 , 10 , 21 , 21 , 14 , 10 , 10 , 17        'jumping man-b
Deflcdchar 2 , 32 , 32 , 32 , 32 , 32 , 32 , 32 , 32        'empty box
Deflcdchar 3 , 16 , 16 , 16 , 16 , 16 , 16 , 16 , 0         '1/5 box
Deflcdchar 4 , 24 , 24 , 24 , 24 , 24 , 24 , 24 , 0         '2/5 box
Deflcdchar 5 , 28 , 28 , 28 , 28 , 28 , 28 , 28 , 0         '3/5 box
Deflcdchar 6 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 0         '4/5 box
Deflcdchar 7 , 31 , 31 , 31 , 31 , 31 , 31 , 31 , 0         'full box
Cls
Cursor Off
Do
  'Generate random numbers to display as bar
  For Forcounter = 1 To 40
  Logval = Rnd(40)
    Call Bar(logval)
    Waitms 500
  Next Logval
  'Count down to create diminishing bar
  For Logval = 40 To 1 Step -1
    Call Bar(logval)
    Waitms 250
  Next Logval
Loop
End

Sub Bar(logvalue As Byte)
  Local Lognumboxes As Byte
  Local Logbarremainder As Byte
  Local Logboxnumber As Byte
  Locate 1 , 1
  Lognumboxes = Logvalue / 5                                'calc number of boxes in bar
  Logbarremainder = 5 * Lognumboxes                         'and determine remainder
  Logbarremainder = Logvalue - Logbarremainder
  For Logboxnumber = 1 To Lognumboxes                       'number of boxes to lcd
    Lcd Chr(7)
  Next Logboxnumber
  Logbarremainder = 2 + Logbarremainder                     'make remainder point to
  Lcd Chr(logbarremainder)                                  'correct lcd char and output char to lcd
  Lognumboxes = Lognumboxes + 1                             'Fill remainder with spaces
  For Logboxnumber = Lognumboxes To Barmaxchar
    Lcd " "
  Next Logboxnumber
End Sub
В этом примере основная программа генерирует случайные числа для отображения в виде столбца. Затем она вызывает подпограмму Bar с параметром Logvalue. Подпрограмма, в свою очередь, рассчитывает количество полностью закрашеных символов, затем выбирает заранее описаный символ из 1-4 вертикальных черточек. Наконец, она заполняет оставшееся место на экране пробелами, чтобы убрать следы предыдущих показаний.

Функции

Функции подобны подпрограммам, но они возвращают хотя бы одно значение вызвавшей программе. Например:

Dim DDSWord as Long
Const DDSClock = 100000000 '100MHz
Declare Function MakeDDSWord(byref Frequency as Long) as Long

DDSWord = MakeDDSWord(Frequency)

Function MakeDDSWord(byref Frequency as Single) as Long
  MakeDDSWord = 2 ^ 32
  MakeDDSWord = MakeDDSWord / DDSClock
  MakeDDSWord = MakeDDSWord * Frequency
End Function
В строке DDSWord = MakeDDSWord(Frequency) функция MakeDDSWord вызывается с аргументом Frequency. Функкция должна вернуть хотя бы одно значение, но возможно (хотя и жутко нелогично для функций) изменение аргументов, данных функции. Чтобы разрешить это сделать, используйте параметры Byval и Byref, как и для подпрограмм.

Локальные переменные

В подпрограмме или функции можно объявить переменные, существующие только внутри неё. Они недоступны из основной программы. Для объявления такой переменной используйте оператор Local:

Local Temporaryval as Integer
Local Justfornow as Long

Все типы переменных, за исключением Bit, могут быть объявлены как локальные. Переменные типа Bit всегда глобальные.

 



DECADALAB ALFA