240 lines
9.3 KiB
Markdown
240 lines
9.3 KiB
Markdown
---
|
||
title: "Простой частотомер на AVR - курсовая работа"
|
||
categories: ["mcu", "archive"]
|
||
date: 2015-08-16T00:00:00+03:00
|
||
draft: true
|
||
featured_image: miniature.jpg
|
||
---
|
||
|
||
|
||
Итак, пришло время время делать курсовые работы. Мне попалась тема "Частотомер с передачей данных по последовательному порту и динамической индикацией". Значит, будем использовать таймер, внешние прерывания и динамическую индикацию. Использовал семисегментный индикатор с четырьмя разрядами и общим анодом. Микроконтроллер выберем Atmega16. Именно с ним я не чувствовал дефицита ножек. Приступим.
|
||
|
||
<!--more-->
|
||
|
||
> :warning: Этот текст – древний стыд. Не нужно принимать его всерьёз. Сейчас бы такой бред не сделал.
|
||
|
||
Работа заключается в следующем: нужно спроектировать схему устройства, развести печатку, а также изготовить корпус для готового устройства. В КОМПАС 3D, разумеется.
|
||
|
||
Для начала нам нужно определиться как именно считать частоту. Я решил использовать таймер, тактированный часовым кварцем и засекать количество внешних прерываний за секунду. Прерывания использовал по нарастающему фронту. Возможно, я не прав, но, во благо, на железе собирать ничего не требуется.
|
||
|
||
Делаем всё в протеусе. Схема получилась такая:
|
||
|
||
![Схема|786](freq_sch.png)
|
||
|
||
Пишем программу. Микроконтроллер должен считать импульсы в секунду и выводить их на семисегментный индикатор. А также отдавать данные по UART.
|
||
|
||
main.h:
|
||
```c
|
||
#ifndef __MAIN_H_
|
||
#define __MAIN_H_
|
||
|
||
#include <stdint.h>
|
||
#include <stdbool.h>
|
||
|
||
typedef uint8_t byte;
|
||
|
||
byte buf[16];
|
||
|
||
uint32_t freq;
|
||
uint32_t freq_max;
|
||
uint32_t measure_buf;
|
||
|
||
// порт для сегментов
|
||
#define SEGMENTS_DDR DDRA
|
||
#define SEGMENTS_PORT PORTA
|
||
|
||
|
||
// порт для разрядов
|
||
#define DIGITS_DDR DDRC
|
||
#define DIGITS_PORT PORTC
|
||
|
||
|
||
#define SWITCH_TIME 45 // время между переключениями разрядов; чем меньше, тем меньше мерцает
|
||
|
||
#define INPUT 0x00
|
||
#define OUTPUT 0xFF
|
||
|
||
|
||
#define SYMBOLS_SIZE 11 // количество символов в таблице
|
||
byte symbols[SYMBOLS_SIZE] = //состояния пинов для символов
|
||
{
|
||
0b00111111, // 0
|
||
0b00000110, // 1
|
||
0b01011011, // 2
|
||
0b01001111, // 3
|
||
0b01100110, // 4
|
||
0b01101101, // 5
|
||
0b01111101, // 6
|
||
0b00000111, // 7
|
||
0b01111111, // 8
|
||
0b01101111, // 9
|
||
0b10000000 // .
|
||
};
|
||
|
||
int main(void);
|
||
|
||
void switchDigit(byte digit); // показать нужный разряд, остальные погасить; значения от 0 до 3
|
||
void showDigit(byte number, bool dot); // вывести символ на разряд, с точкой или без; значения от 0 до SYMBOLS_SIZE
|
||
void initTimer(); // инициализация таймера (для нас - часового)
|
||
void initInterrupts(); //инициализация прерываний
|
||
void initUART(); //инициализация последовательного порта
|
||
void sendByte(byte b); //отправка байта по UART
|
||
void sendString(byte *str); //отправка строки по UART
|
||
|
||
#endif //__MAIN_H_
|
||
```
|
||
|
||
main.c:
|
||
```c
|
||
#include "main.h"
|
||
#include <avr/io.h>
|
||
#include <avr/interrupt.h>
|
||
#include <util/delay.h>
|
||
#include <stdio.h>
|
||
|
||
|
||
int main(void) {
|
||
SEGMENTS_DDR = OUTPUT; // настраиваем порты ввода-вывода
|
||
DIGITS_DDR = OUTPUT;
|
||
DIGITS_PORT = 0xFF;
|
||
|
||
initTimer();
|
||
initInterrupts();
|
||
initUART();
|
||
|
||
byte i;
|
||
uint32_t num;
|
||
|
||
sprintf(buf, "? Stabilization...\r\n");
|
||
sendString(buf);
|
||
_delay_ms(100); // ожидаем стабилизации таймера
|
||
sprintf(buf, "? Init finished!\r\n");
|
||
sendString(buf);
|
||
|
||
while (1) {
|
||
|
||
num = freq > 9999 ? 9999 : freq;
|
||
|
||
for (i = 0; i < 4; ++i) { // перебираем все разряды, разбираем частоту на цифры
|
||
switchDigit(i);
|
||
switch (i) {
|
||
case 0:
|
||
showDigit((byte) ((num / 1000) % 10), false);
|
||
break;
|
||
case 1:
|
||
showDigit((byte) ((num / 100) % 10), false);
|
||
break;
|
||
case 2:
|
||
showDigit((byte) ((num / 10) % 10), false);
|
||
break;
|
||
case 3:
|
||
showDigit(num % 10, false);
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
_delay_ms(SWITCH_TIME);
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
ISR(TIMER2_OVF_vect) { // прерывание таймера при переполнении
|
||
freq = measure_buf;
|
||
if( freq > freq_max) freq_max = freq;
|
||
measure_buf = 0;
|
||
|
||
|
||
sprintf(buf, "> Freq: %dHz, ", freq); // отправляем данные
|
||
sendString(buf);
|
||
sprintf(buf, "Max: %dHz\r\n", freq_max);
|
||
sendString(buf);
|
||
|
||
|
||
}
|
||
|
||
ISR(INT0_vect) { //внешнее прерывание
|
||
measure_buf++;
|
||
}
|
||
|
||
void showDigit(byte digit, bool dot) {
|
||
SEGMENTS_PORT = ~symbols[digit > SYMBOLS_SIZE - 1 ? 10 : digit] | (dot ? symbols[10] : 0);
|
||
}
|
||
|
||
void switchDigit(byte number) {
|
||
DIGITS_PORT = number < 4 ? (byte) (1 << number) : 0x00;
|
||
}
|
||
|
||
void initTimer() {
|
||
ASSR |= _BV(AS2); // асинхронный режим, тактируемся от часового кварца
|
||
TCCR2 = _BV(CS20) | _BV(CS22); //предделитель 128, одна секунда
|
||
TIMSK |= _BV(TOIE2); // включаем таймер
|
||
}
|
||
|
||
void initInterrupts() {
|
||
MCUCR = (1 << ISC01) | (1 << ISC00); //прерывание по растущему форонту
|
||
GICR = (1 << INT0); //включаем прерывание на INT0
|
||
sei(); // разрешаем прерывания
|
||
}
|
||
|
||
void initUART() {
|
||
// выставляем скорость: 9600 при частоте 8МГц
|
||
// UBRR=8000000/(16*9600)-1=51.0833, округляем = 51 (0x33)
|
||
UBRRH = 0x00;
|
||
UBRRL = 0x33;
|
||
|
||
// Разрешаем приём и передачу
|
||
UCSRB = (1 << RXEN) | (1 << TXEN);
|
||
UCSRB |= (1 << RXCIE);
|
||
|
||
// устанавливаем формат: 8 бит данных, 2 стоп бита
|
||
UCSRC = (1 << URSEL) | (1 << USBS) | (3 << UCSZ0);
|
||
}
|
||
|
||
void sendByte(byte b) {
|
||
while ( !(UCSRA & (1<<UDRE)) ); // ожидаем завершения передачи
|
||
UDR = b; // записываем байт в буфер
|
||
}
|
||
|
||
void sendString(byte * str) {
|
||
while (*str != 0) sendByte(*str++); // побайтно отправляем строку
|
||
}
|
||
```
|
||
|
||
Разводим печатку:
|
||
|
||
![|300](pcb.png)
|
||
|
||
Вот такую красоту можно понаблюдать:
|
||
|
||
![|300](3d1.png)
|
||
|
||
Результат:
|
||
|
||
![|226](pcb1.png)
|
||
|
||
Теперь самое главное - сделать корпус. Отталкиваться нужно от размера самой печатки. Делаем чертежи каждой части корпуса, потом варганим из них модели. Затем собираем всё воедино.
|
||
|
||
![|300](draft.png) ![|300](compas1.png)![|300](compas2.png) ![|300](compas3.png)
|
||
|
||
Три отверстия сзади для проводов: вход для сигнала, питание и UART. В прямоугольный вырез спереди вставляется семисегментный индикатор и шлейфом соединяется с печатной платой. Сама плата прикручивается маленькими саморезами/болтами. Крышка тоже.
|
||
|
||
##### Ссылки:
|
||
|
||
[Чертежи и модели в КОМПАС-3D](freq-meter-KOMPAS.zip)
|
||
|
||
[Чертежи в Corel Draw](7seg-freq-corel.zip)
|
||
|
||
[Сама курсовая работа (docx)](avr-7seg-freq-curse.docx)
|
||
|
||
[Внешние прерывания МК AVR (samou4ka.net)](http://samou4ka.net/page/vneshnie-preryvanija-mk-avr)
|
||
|
||
[Таймеры МК AVR (samou4ka.net)](http://samou4ka.net/page/tajmer-schetchik-mikrokontrollerov-avr)
|
||
|
||
[Асинхронный режим таймера AVR (easyelectronics.ru)](http://easyelectronics.ru/avr-uchebnyj-kurs-asinxronnyj-rezhim-tajmera.html)
|
||
|
||
[Самые простые часы на AVR (easyelectronics.ru)](http://we.easyelectronics.ru/antonluba/samye-prostye-chasy-na-avr.html)
|
||
|
||
[Работа с UART на примере ATmega16 (alex-exe.ru)](http://alex-exe.ru/radio/avr/avr-uart/)
|