mmote.ru/content/posts/avr-freq-meter/index.md

240 lines
9.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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/)