Мы знаем, что сейчас в офисах, торговых центрах и многих других местах требуется авторизация людей, которые входят/выходят из этих мест. Часто для этих целей используется радиочастотная идентификация (RFID — Radio Frequency Identification). В частности, радиочастотная идентификация используется в торговых центрах для защиты от воров – все товары в них снабжены специальными радиочастотными метками и если кто то пытается вынести из торгового центра товар с такой меткой, то сразу включается сигнализация. Радиочастотные метки представляют собой очень маленькие устройства – их размер сравним с песчинками. Системы радиочастотной идентификации просты в разработке и дешевы в производстве, что и обусловило их широкое распространение по всему миру.
В этой статье мы рассмотрим машину для голосования которая подсчитывает только авторизованные голоса. Делать мы это будем с помощью радиометок RFID. Рассматриваемая нами схема и программа к ней будет разрешать голосовать только авторизованным пользователям. Простую машину для голосования (без авторизации) мы рассмотрели в этой статье на нашем сайте.
Необходимые компоненты
Аппаратное обеспечение
- Микроконтроллер ATmega32 (купить на AliExpress).
- Программатор AVR-ISP (купить на AliExpress), USBASP (купить на AliExpress) или другой подобный.
- Жидкокристаллический дисплей JHD_162ALCD (16x2LCD) (купить на AliExpress).
- Конденсатор 100 мкФ (соединенный по питанию) (купить на AliExpress).
- Кнопка (5 шт.).
- Резистор 10 кОм (5 шт.) (купить на AliExpress).
- Конденсатор 100 нФ (5 шт.) (купить на AliExpress).
- Светодиод (2 шт.) (купить на AliExpress).
- EM-18 (модуль чтения RFID меток) (купить на AliExpress).
- Источник питания с напряжением 5 Вольт.
Реклама: ООО «АЛИБАБА.КОМ (РУ)» ИНН: 7703380158
Программное обеспечение
- Atmel Studio версии 6.1 (или выше).
- Progisp или flash magic (необязательно).
Работа схемы
Схема устройства приведена на следующем рисунке.
В представленной схеме мы использовали только 2 контакта управления ЖК дисплея для лучшего понимания работы схемы. Бит контраста и READ/WRITE используются нечасто, поэтому они могут быть замкнуты на землю. Это обеспечивает ЖК дисплею максимальную контрастность и переводит его в режим чтения. Теперь нам всего лишь нужно контролировать контакты ENABLE и RS чтобы передавать на ЖК дисплей символы и данные. Также на нашем сайте вы можете прочитать более подробную статью о подключении ЖК дисплея к микроконтроллеру ATmega32.
В схеме необходимо сделать следующие соединения с ЖК дисплеем:
PIN1 или VSS — земля
PIN2 или VDD или VCC — +5v питание
PIN3 или VEE — земля (обеспечивает максимальный контраст ЖК дисплею)
PIN4 или RS (Register Selection) – контакт PD6 микроконтроллера
PIN5 или RW (Read/Write) — земля (переводит ЖК дисплей в режим чтения что упрощает взаимодействие с ним для начинающих)
PIN6 или E (Enable) — контакт PD5 микроконтроллера
PIN7 или D0 — контакт PA0 микроконтроллера
PIN8 или D1 — контакт PA1 микроконтроллера
PIN9 или D2 — контакт PA2 микроконтроллера
PIN10 или D3 — контакт PA3 микроконтроллера
PIN11 или D4 — контакт PA4 микроконтроллера
PIN12 или D5 — контакт PA5 микроконтроллера
PIN13 или D6 — контакт PA6 микроконтроллера
PIN14 или D7 — контакт PA7 микроконтроллера
В схеме мы использовали 8-битную связь (D0-D7) ЖК дисплея с микроконтроллером, хотя можно было ограничиться и 4-битной – но в этом случае код программы стал бы немного сложнее.
Таким образом, мы использовали 10 контактов ЖК дисплея, 8 из которых будут использоваться для передачи данных и 2 для управления.
Чтобы двигаться дальше нам необходимо остановиться на принципах работы последовательного порта микроконтроллера потому что модуль чтения радиочастотных меток будет посылать данные микроконтроллеру именно по этому порту. Можно задействовать и другой режим связи модуля чтения радиочастотных меток с микроконтроллером (RFID), но в целях упрощения мы в данной статье остановимся на их связи через порт RS232. Таким образом, контакт RS232 модуля RFID необходимо соединить с контактом RXD микроконтроллера ATmega32.
Данные, передаваемые модулем RFID, выглядят следующим образом:
Чтобы осуществить успешный их прием и обработку, необходимо выполнить следующие действия:
- Контакт RXD микроконтроллера должен быть доступен (активирован).
- Поскольку мы используем последовательную передачу данных, то мы должны знать момент окончания приема всего байта данных. То есть мы должны приостановить работу программы до тех пор, пока мы не примем весь байт. Это достигается при помощи использования соответствующего прерывания.
- Модуль RFID передает данные микроконтроллеру в 8 битном режиме, поэтому одновременно будут передаваться два символа.
- Биты четности не используются, но однако один стоповый бит посылается модулем RFID.
Регистры, используемые для управления последовательным портом микроконтроллера, представлены на следующем рисунке.
Кратко поясним данный рисунок.
RED (красный, RXEN): этот бит представляет особенности принимаемых данных. Он должен быть установлен для данных, которые передаются от модуля RFID к микроконтроллеру. Он также производит активацию (запускает в работу) контакт RXD микроконтроллера.
BROWN (коричневый, RXCIE): этот бит должен быть установлен для обработки прерывания после окончания успешного приема данных. Активируя этот бит мы знаем что произвели успешный прием 8 бит.
PINK (розовый, URSEL): этот бит должен быть установлен перед активацией других битов UCSRC. После его установки, другие биты UCSRC, URSEL должны быть деактивированы или установлены в 0.
YELLOW (желтый, UCSZ0,UCSZ1,UCSZ2): эти три бита используются для выбора номеров битов данных, которые мы передаем или принимаем при односторонней передаче. Поскольку данные от модуля RFID передаются в 8 битном формате мы должны установить биты UCSZ0 и UCSZ1 в единицу, а бит UCSZ2 – в ноль.
ORANGE (оранжевый, UMSEL): этот бит определяет используется ли асинхронный режим передачи между устройствами (используются различные часы) или синхронный (используются одни и те же часы). Поскольку модуль RFID и микроконтроллер ATmega32 используют разные часы, то этот бит должен быть установлен в ноль.
GREEN (зеленый, UPM1, UPM0): эти два бита корректируются на основе бита четности, который мы используем в процессе связи.
Поскольку модуль RFID передает данные без использования четности, то биты UPM1, UPM0 должны быть установлены в ноль либо не трогать их вообще поскольку по умолчанию все биты во всех регистрах устанавливаются в ноль при инициализации микроконтроллера.
BLUE (синий, USBS): этот бит используется для выбора числа стоповых битов которые мы используем в процессе связи.
Поскольку модуль RFID передает данные с одним стоповым битом, мы должны установить бит USBS в ноль.
После установки всех рассмотренных битов мы должны установить техническую (в бодах) скорость передачи по последовательному порту. Модуль RFID передает данные по этому порту со скоростью 9600 бод/с, поэтому такую же скорость мы должны выставить и у последовательного порта микроконтроллера при помощи установки соответствующего значения UBRRH.
Значение байта UBRRH должно выбираться из необходимой скорости передачи данных и используемой частоты работы микроконтроллера. Исходя из нижеприведенной таблицы мы видим, что нам необходимо записать в этот байт значение ‘6’.
В схеме присутствуют 5 кнопок, четыре – для подсчета голосов кандидатов и пятая для сброса числа голосов кандидатов в ноль. Конденсаторы на схеме предназначены для устранения эффектов биений, возникающих при нажатии кнопок. Если их убрать, то микроконтроллер в некоторых случаях будет фиксировать более одного нажатия кнопки, хотя кнопка была нажата всего один раз.
Резисторы, подсоединенные к контактам микроконтроллера, служат для ограничения тока во время нажатий на кнопки. Во время нажатия кнопки на соответствующий контакт микроконтроллера подается «земля» и таким образом микроконтроллер распознает что кнопка была нажата и выполняет в связи с этим определенное действие: увеличивает число голосов кандидата или сбрасывает все голоса (в зависимости от того какая кнопка была нажата).
При нажатии кнопки, соответствующей какому-нибудь человеку, микроконтроллер распознает это и увеличивает соответствующую переменную в своей памяти, затем передает значение этой переменной на ЖК дисплей 16×2.
Исходный код программы на языке С (Си) с пояснениями
Программа для рассматриваемой схемы представлена следующим фрагментом кода на языке С (Си). Комментарии к коду программу поясняют принцип работы отдельных команд.
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 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
#include <avr/io.h> // заголовок чтобы разрешить контроль данных на контактах #define F_CPU 1000000 // задание тактовой частоты микроконтроллера #include <util/delay.h> // заголовок чтобы задействовать функции задержки в программе #define E 5 // задействуем 5-й контакт PORTD (“enable”), поскольку он соединен с контактом “enable” ЖК дисплея #define RS 6 // задействуем выбор регистра (“registerselection”) на 6-м контакте PORTD, поскольку он соединен с контактом RS ЖК дисплея void send_a_command(unsigned char command); void send_a_character(unsigned char character); void send_a_string(char *string_of_characters); int main(void) { DDRA = 0xFF; // установка porta в режим вывода данных DDRD = 0b11111110; _delay_ms(50);// задержка 50ms DDRB = 0b11110000;// установка нескольких контактов portB в режим ввода данных UCSRB |=(1<<RXEN)|(1<<RXCIE); // разрешаем прием прерываний и прием данных UCSRC |=(1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1); // изменение других битов первоначальной установкой URSEL, установка для режима 8 битной связи UCSRC &=~(1<<UMSEL); // используем асинхронный режим передачи UBRRH &=~(1<<URSEL); UBRRL=6;// установка скорости передачи в бодах (9600) int16_t VOTEA = 0;// число голосов от персоны 1 char A [4];// отображаемый символ голосования персоны 1 на ЖК дисплее int16_t VOTEB = 0; ;// число голосов от персоны 2 char B [4];// отображаемый символ голосования персоны 2 на ЖК дисплее int16_t VOTEC = 0; ;// число голосов от персоны 3 char C [4];// отображаемый символ голосования персоны 3 на ЖК дисплее int16_t VOTED = 0; ;// число голосов от персоны 4 char D [4];/ / отображаемый символ голосования персоны 4 на ЖК дисплее /* после выгрузки программы в микроконтроллер нужно взять карты, которые необходимо будет авторизовать и получить нужные идентификаторы тэгов. Это осуществляется при помощи помещения тэга рядом с модулем RFID и в этом случае идентификатор этого тэга должен высветиться на экране ЖК дисплея. После получения этих идентификаторов программу необходимо обновить путем замены нижеперечисленных идентификаторов на новые идентификаторы. char ADMIT [5][4]={{(0x97),(0xa1),(0x90),(0x92)},{(0x97),(0xa1),(0x90),(0x93)},{(0x97),(0xa1),(0x90),(0x94)},{(0x97),(0xa1),(0x90),(0x95)},{(0x97),(0xa1),(0x90),(0x96)}}; В представленном примере мы авторизовали только пять карт, но в общем случае можно авторизовать любое число карт. К примеру, рассмотрим случай, когда программа, первоначально загруженная в микроконтроллер, должна авторизовать несколько карт. Помещая поочередно каждую из этих карт вблизи модуля RFID мы получим идентификатор каждой из них в формате xxxxxxxx (например, 907a4F87). Если у нас 7 тэгов (карт), то мы получим 7 восьмибитных идентификаторов. */ // теперь для 7 карт имеем //char ADMIT[7][4]={{(0x90),(0x7a),(0x4F),(0x87)},[},{},{},{},{},{}} // Теперь, когда все идентификаторы записаны, эти тэги (карты) будут рассматриваться как авторизованные Int16_t COUNTA = 0;// предварительная очистка памяти, которая будет использоваться для хранения идентификаторов, передаваемых RFID модулем char SHOWA [4];// предварительная очистка памяти, которая будет использоваться для показа идентификаторов, передаваемых RFID модулем int i=0; int vote =0; int k =0; send_a_command(0x01); // очистить экран 0x01 = 00000001 _delay_ms(50); send_a_command(0x38);// сообщаем ЖК дисплею что мы будем использовать 8-битный режим команд/данных _delay_ms(50); send_a_command(0b00001111);// включаем экран и мигание курсора на ЖК дисплее char MEM[4];// выделение памяти для хранения всех идентификаторов тегов send_a_string ("RFID NUMBER");//передаем строку send_a_command(0x80 + 0x40 + 0);//перемещаем курсор на вторую линию while(1) { while(!(UCSRA&(1<<RXC)));// ждем пока не примем первые 8 бит данных { } COUNTA=UDR;//UDR хранит первые 8 бит принятых данных и преобразует их в целое число (integer) MEM[0]=COUNTA;// первые два символа обновляются в памяти itoa(COUNTA,SHOWA,16); //command for putting variable number in LCD(variable number, in which character to replace, which base is variable(ten here as we are counting number in base10)) send_a_string(SHOWA); //telling the display to show character(replaced by variable number) of second person after positioning the courser on LCD while(!(UCSRA&(1<<RXC)));// ждем пока не примем вторые 8 бит данных { } COUNTA=UDR; itoa(COUNTA,SHOWA,16); send_a_string(SHOWA); MEM[1]=COUNTA; // третий и четвертый символы обновляются в памяти while(!(UCSRA&(1<<RXC))); // ждем пока не примем третьи 8 бит данных { } COUNTA=UDR; itoa(COUNTA,SHOWA,16); send_a_string(SHOWA); MEM[2]=COUNTA;// пятый и шестой символы обновляются в памяти while(!(UCSRA&(1<<RXC))); // ждем пока не примем четвертые 8 бит данных { } COUNTA=UDR; itoa(COUNTA,SHOWA,16); send_a_string(SHOWA); MEM[3]=COUNTA;// седьмой и восьмой символы обновляются в памяти send_a_string(" "); send_a_command(0x80 + 0x40 + 0); UCSRB &=~(1<<RXEN);// отключаем (disabling) прием данных до тех пор пока не проверим идентификаторы for (i=0;i<5;i++) { if ((MEM[0]==ADMIT[i][0])&(MEM[1]==ADMIT[i][1])&(MEM[2]==ADMIT[i][2])&(MEM[3]==ADMIT[i][3])) { // проверка авторизации при помощи сравнения двух символов в данный момент времени с символами, хранящимися в памяти PORTB|=(1<<PINB4);// если авторизация успешна, то включить светодиод vote=1;// если авторизация успешна, то установить переменную VOTE } } if (vote==0)// авторизация не удалась если VOTE не установлена { UCSRB |=(1<<RXEN);// старт приема данных от RFID модуля } while (vote==1)//выполнять этот цикл до тех пор пока авторизация успешна { send_a_command(0x80 + 0);//передвинуть курсор на нулевую позицию 1-й линии send_a_string ("VOTE NOW ");//отображение строки if (bit_is_clear(PINB,0)) //когда кнопка 1 нажата { VOTEA++; // инкрементирование числа голосов от персоны 1 на единицу vote=0;//letting while loop go after voting } if (bit_is_clear(PINB,1 ))// когда кнопка 2 нажата { VOTEB++;// инкрементирование числа голосов от персоны 2 на единицу vote=0; } if (bit_is_clear(PINB,2))// когда кнопка 3 нажата { VOTEC++;// инкрементирование числа голосов от персоны 3 на единицу vote=0; } if (bit_is_clear(PINB,3)) // когда кнопка 4 нажата { VOTED++;// инкрементирование числа голосов от персоны 4 на единицу vote=0; } if (vote==0)// очищаем после того как голосование окончено { send_a_command(0x80 + 0);// передвинуть курсор на нулевую позицию 1-й линии send_a_string ("THANK U FOR VOTE");//отображаем строку for (k=0;k<10;k++) { _delay_ms(220); } PORTB&=~(1<<PINB4);// выключаем светодиод, свидетельствующий о разрешении голосования send_a_command(0x01); send_a_command(0x80 + 0); //отображаем голоса все четырех персон send_a_string ("A="); send_a_command(0x80 + 2); itoa(VOTEA,A,10); send_a_string(A); send_a_command(0x80 + 8); send_a_string ("B="); send_a_command(0x80 + 10); itoa(VOTEB,B,10); send_a_string(B); send_a_command(0x80 + 0x40 + 0); send_a_string ("C="); send_a_command(0x80 + 0x40 + 2); itoa(VOTEC,C,10); send_a_string(C); send_a_command(0x80 + 0x40 + 8); send_a_string ("D="); send_a_command(0x80 + 0x40 + 10); itoa(VOTED,D,10); send_a_string(D); send_a_command(0x80 + 0x40 + 16); for (k=0;k<25;k++) { _delay_ms(220); } UCSRB |=(1<<RXEN);// разрешаем прием данных после голосования send_a_command(0x01); send_a_command(0x80 + 0);// передвигаем курсор на нулевую позицию send_a_string ("RFID NUMBER");//посылаем строку send_a_command(0x80 + 0x40 + 0); } } void send_a_command(unsigned char command) { PORTA = command; PORTD &= ~ (1<<RS); // устанавливаем RS в 0 чтобы сообщить ЖК дисплею что мы будем передавать команду PORTD |= 1<<E; // сообщаем ЖК дисплею что мы будем передавать данные _delay_ms(50); PORTD &= ~1<<E;// сообщаем ЖК дисплею что мы закончили передачу данных PORTA= 0; } void send_a_character(unsigned char character) { PORTA= character; PORTD |= 1<<RS;// сообщаем ЖК дисплею что мы будем передавать данные (не команду) PORTD |= 1<<E;// сообщаем ЖК дисплею чтобы он начал прием команды/данных _delay_ms(50); PORTD &= ~1<<E;// сообщаем ЖК дисплею что мы закончили передачу данных/команды PORTA = 0; } void send_a_string(char *string_of_characters) { while(*string_of_characters > 0) { send_a_character(*string_of_characters++); } } |
Текст программы без комментариев
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 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
/* RFID Based Electronic Voting Machine */ #include <avr/io.h> #define F_CPU 1000000 #include <util/delay.h> #include <stdlib.h> #define enable 5 #define registerselection 6 void send_a_command(unsigned char command); void send_a_character(unsigned char character); void send_a_string(char *string_of_characters); int main(void) { DDRA = 0xFF; DDRB = 0b11110000; DDRD = 0b11111110; _delay_ms(50); UCSRB |=(1<<RXEN)|(1<<RXCIE); UCSRC |=(1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1); UCSRC &=~(1<<UMSEL); UBRRH &=~(1<<URSEL); UBRRL=6; char ADMIT [5][4]={{(0x97),(0xa1),(0x90),(0x92)},{(0x97),(0xa1),(0x90),(0x93)},{(0x97),(0xa1),(0x90),(0x94)},{(0x97),(0xa1),(0x90),(0x95)},{(0x97),(0xa1),(0x90),(0x96)}}; int16_t COUNTA = 0; char SHOWA [4]; int i=0; int vote =0; int k =0; int16_t VOTEA = 0; char A [4]; int16_t VOTEB = 0; char B [4]; int16_t VOTEC = 0; char C [4]; int16_t VOTED = 0; char D [4]; send_a_command(0x01); //Clear Screen 0x01 = 00000001 _delay_ms(50); send_a_command(0x38); _delay_ms(50); send_a_command(0b00001111); _delay_ms(50); char MEM[4]; send_a_string ("RFID NUMBER"); send_a_command(0x80 + 0x40 + 0); while(1) { while(!(UCSRA&(1<<RXC))); { } COUNTA=UDR; MEM[0]=COUNTA; itoa(COUNTA,SHOWA,16); send_a_string(SHOWA); while(!(UCSRA&(1<<RXC))); { } COUNTA=UDR; itoa(COUNTA,SHOWA,16); send_a_string(SHOWA); MEM[1]=COUNTA; while(!(UCSRA&(1<<RXC))); { } COUNTA=UDR; itoa(COUNTA,SHOWA,16); send_a_string(SHOWA); MEM[2]=COUNTA; while(!(UCSRA&(1<<RXC))); { } COUNTA=UDR; itoa(COUNTA,SHOWA,16); send_a_string(SHOWA); MEM[3]=COUNTA; send_a_string(" "); send_a_command(0x80 + 0x40 + 0); UCSRB &=~(1<<RXEN); for (i=0;i<5;i++) { if ((MEM[0]==ADMIT[i][0])&(MEM[1]==ADMIT[i][1])&(MEM[2]==ADMIT[i][2])&(MEM[3]==ADMIT[i][3])) { PORTB|=(1<<PINB4); vote=1; } } if (vote==0) { UCSRB |=(1<<RXEN); } while (vote==1) { send_a_command(0x80 + 0); send_a_string ("VOTE NOW "); if (bit_is_clear(PINB,0)) { VOTEA++; vote=0; } if (bit_is_clear(PINB,1)) { VOTEB++; vote=0; } if (bit_is_clear(PINB,2)) { VOTEC++; vote=0; } if (bit_is_clear(PINB,3)) { VOTED++; vote=0; } if (vote==0) { send_a_command(0x80 + 0); send_a_string ("THANK U FOR VOTE"); for (k=0;k<10;k++) { _delay_ms(220); } PORTB&=~(1<<PINB4); send_a_command(0x01); send_a_command(0x80 + 0); send_a_string ("A="); send_a_command(0x80 + 2); itoa(VOTEA,A,10); send_a_string(A); send_a_command(0x80 + 8); send_a_string ("B="); send_a_command(0x80 + 10); itoa(VOTEB,B,10); send_a_string(B); send_a_command(0x80 + 0x40 + 0); send_a_string ("C="); send_a_command(0x80 + 0x40 + 2); itoa(VOTEC,C,10); send_a_string(C); send_a_command(0x80 + 0x40 + 8); send_a_string ("D="); send_a_command(0x80 + 0x40 + 10); itoa(VOTED,D,10); send_a_string(D); send_a_command(0x80 + 0x40 + 16); for (k=0;k<25;k++) { _delay_ms(220); } UCSRB |=(1<<RXEN); send_a_command(0x01); send_a_command(0x80 + 0); send_a_string ("RFID NUMBER"); send_a_command(0x80 + 0x40 + 0); } } } } void send_a_command(unsigned char command) { PORTA = command; PORTD &= ~ (1<<registerselection); PORTD |= 1<<enable; _delay_ms(20); PORTD &= ~1<<enable; PORTA = 0; } void send_a_character(unsigned char character) { PORTA = character; PORTD |= 1<<registerselection; PORTD |= 1<<enable; _delay_ms(20); PORTD &= ~1<<enable; PORTA = 0; } void send_a_string(char *string_of_characters) { while(*string_of_characters > 0) { send_a_character(*string_of_characters++); } } |