Поэтому недавно я попытался внедрить Software UART (только TX) для ATTiny85. Я хочу управлять им с помощью внутреннего Timer1 .
Таймер должен прерываться с частотой передачи данных. Каждый бит ISR будет передаваться до тех пор, пока не останется ничего для отправки, и прерывание не будет снова отключено.
(Примечание: F_CPU = 1000000; Предохранители - заводские настройки по умолчанию (E: FF, H : DF, L: 62))
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdint.h>
#define SET(reg, pos) (reg |= 1<<(pos))
#define FLP(reg, pos) (reg ^= 1<<(pos))
#define CLR(reg, pos) (reg &= ~(1<<(pos)))
#define GET(reg, pos) (reg & 1<<(pos))
#define UART_TX_BIT PB1
#define UART_BAUDRATE 600
static volatile uint16_t txframe;
/* Timer1A interrupt at BAUDRATE */
ISR(TIM1_COMPA_vect)
{
/* Write current bit */
if(txframe & 1) SET(PORTB, UART_TX_BIT);
else CLR(PORTB, UART_TX_BIT);
/*
* If the 1 mark at the end of txframe is reached,
* disable interrupts (stop transmitting)
*/
if(txframe == 1) CLR(TIMSK, OCIE1A);
txframe >>= 1;
}
static void uart_putc(const char c)
{
/* Wait until pending txframe is transmitted */
do {
sei();
__asm__ __volatile__ ("nop");
cli();
}while(txframe);
/* MARK . STOP | DATA | START */
txframe = (0b11<<9) | ((uint16_t) c<<1) | 0;
/* Enable timer interrupt and clear flag */
SET(TIMSK, OCIE1A);
SET(TIFR, OCF1A);
sei();
}
static void uart_init()
{
uint8_t sreg = SREG;
cli();
/* Set timer1 (CK) to CTC with divisor of 8 */
TCCR1 = _BV(CTC1) | _BV(CS12);
/* Set BAUDRATE clock divisor */
OCR1A = (uint8_t) ((uint32_t) (F_CPU/8)/UART_BAUDRATE)-1;
/* Enable and pull TX Pin to HIGH */
SET(DDRB, UART_TX_BIT);
SET(PORTB, UART_TX_BIT);
txframe = 0;
SET(TIFR, OCF1A);
sreg = SREG;
}
int main()
{
uart_init();
for(;;) {
uart_putc('A');
_delay_ms(2000);
}
}
При этой настройке приемник получает только 0x00 или 0xFF и иногда какой-то другой мусор (в зависимости от скорости передачи)
В конце концов я попытался чтобы достичь того же самого без прерываний:
#define UART_FALLBACK_DELAY() _delay_us(1000000UL/UART_BAUDRATE)
static void uart_putc_fallback(uint8_t c)
{
uint8_t sreg = SREG;
cli();
/* Start */
CLR(PORTB, UART_TX_BIT);
UART_FALLBACK_DELAY();
/* Data */
for(int i = 0; i < 8; i++, c>>=1) {
if(c&1) SET(PORTB, UART_TX_BIT);
else CLR(PORTB, UART_TX_BIT);
UART_FALLBACK_DELAY();
}
/* Stop */
SET(PORTB, UART_TX_BIT);
UART_FALLBACK_DELAY();
SREG = sreg;
}
static void uart_putc_fallback2(const char c)
{
uint8_t sreg = SREG;
cli();
txframe = (0b11<<9) | ((uint16_t) c<<1) | 0;
while(txframe) {
if(txframe & 1) SET(PORTB,UART_TX_BIT);
else CLR(PORTB,UART_TX_BIT);
txframe >>= 1;
UART_FALLBACK_DELAY();
}
SREG = sreg;
}
Удивительно, но обе эти функции работают, как и ожидалось, поэтому я думаю, что я что-то напутал с Timer1. К сожалению, у меня нет осциллографа, поэтому я не могу проверить сигнал вручную. Но в целом сигнал кажется немного медленным при использовании Timer1. Настройка должна прерываться каждые 1664 мкс с помощью:
- Бод = 600 Гц
- CK = 1 МГц
- Timer1DIV = 8
- Timer1CK = CK / Timer1DIV = 125 кГц
- OCR1A = таймер 1CK / бод = 208
- задержка = (Timer1DIV * OCR1A) / CK = (8 * 208) / 1 МГц = 1664 мкс
Может кто угодно Скажите, почему подход прерывания не работает должным образом?
Дополнительная информация: