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