- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
ATtiny88单片机不包含串口模块,因此只能使用软件方式模拟串口时序.
串口通信时序通常由起始位、数据位、校验位和停止位四个部分组成,常见的配置为1位起始位、8位数据位、无校验位和1位停止位.
ATtiny88有8个外部中断源:INT0、INT1、PCI0、PCI1、PCI2、PCI3。其中INT0/1支持低电平/下降沿/上升沿触发,PCI0/1/2/3在引脚状态改变时触发.
ATtiny88外部中断和引脚的对应关系如下:
中断源 | 引脚 |
---|---|
INT0 | PD2 |
INT1 | PD3 |
PCI0 | PB[0:7] -> PCINT[0:7] |
PCI1 | PC[0:7] -> PCINT[8:15] |
PCI2 | PD[0:7] -> PCINT[16:23] |
PCI3 | PA[0:3] -> PCINT[24:27] |
注意:即使引脚配置为输出模式,也能触发相应的中断.
ISC1[1:0]
:设置INT1中断触发方式。 ISC0[1:0]
:设置INT0中断触发方式,取值同 ISC1[1:0]
。
INT1
:设为1使能INT1中断。 INT0
:设为1使能INT0中断。
INTF1
:INT1中断标志位,执行中断函数时自动清零,也可以写1清零。 INTF0
:INT0中断标志位,执行中断函数时自动清零,也可以写1清零。
PCIE3
:设为1使能PCI3(PCINT[27:24])中断。 PCIE2
:设为1使能PCI2(PCINT[23:16])中断。 PCIE1
:设为1使能PCI1(PCINT[15:8])中断。 PCIE0
:设为1使能PCI0(PCINT[7:0])中断。
PCIF3
:PCI3(PCINT[27:24])中断标志位,执行中断函数时自动清零,也可以写1清零。 PCIF2
:PCI2(PCINT[23:16])中断标志位,执行中断函数时自动清零,也可以写1清零。 PCIF1
:PCI1(PCINT[15:8])中断标志位,执行中断函数时自动清零,也可以写1清零。 PCIF0
:PCI0(PCINT[7:0])中断标志位,执行中断函数时自动清零,也可以写1清零。
PCINTx
:设为1使能PCINTx中断。 代码文件的整体结构如下:
.
├── Makefile
├── inc
│ └── serial.h
└── src
├── main.c
└── serial.c
inc/serial.h 头文件的代码内容如下:
#pragma once
#include <stdint.h>
#define UART (&serial)
typedef struct {
const uint8_t *cfg;
uint8_t flag;
uint8_t tx_idx;
uint8_t tx_temp;
uint8_t tx_data;
uint8_t rx_idx;
uint8_t rx_temp;
uint8_t rx_data;
uint8_t rx_cnt;
} serial_t;
typedef enum {
SERIAL_BR_1200 = 0,
SERIAL_BR_2400,
SERIAL_BR_4800,
SERIAL_BR_9600,
SERIAL_BR_19200,
SERIAL_BR_38400,
SERIAL_BR_57600,
SERIAL_BR_115200
} serial_baudrate_t;
typedef enum {
SERIAL_FLAG_TXE = 0x01,
SERIAL_FLAG_RXNE = 0x02
} serial_flag_t;
extern serial_t serial;
void serial_setup(serial_t *serial, serial_baudrate_t br);
uint8_t serial_get_flag(serial_t *serial, serial_flag_t flag);
void serial_send_data(serial_t *serial, uint8_t data);
uint8_t serial_receive_data(serial_t *serial);
src/serial.c 源文件的代码内容如下,其中将PD1引脚定义为TX,将PD2引脚定义为RX:
#include <serial.h>
#include <avr/io.h>
#include <avr/interrupt.h>
serial_t serial;
static const uint8_t serial_cfg[] = {
0x03, 208, 35, 69, // 1200
0x03, 104, 17, 35, // 2400
0x03, 52, 9, 17, // 4800
0x02, 208, 35, 69, // 9600
0x02, 104, 17, 35, // 19200
0x02, 52, 9, 17, // 38400
0x02, 35, 6, 12, // 57600
0x01, 139, 23, 46, // 115200
};
void serial_setup(serial_t *serial, serial_baudrate_t br)
{
serial->cfg = &serial_cfg[br * 4];
serial->flag = SERIAL_FLAG_TXE; // initial value for serial->flag
// setup tx pin
PORTD |= _BV(PORTD1); // PD1 outputs high level
DDRD |= _BV(DDD1); // set PD1 as output
// setup rx pin
PORTD |= _BV(PORTD2); // enable PD2 pull-up resistance
DDRD &= ~_BV(DDD2); // set PD2 as input
// setup INT0
EICRA &= ~(_BV(ISC01) | _BV(ISC00));
EICRA |= _BV(ISC01); // the falling edge of INT0 generates an interrupt request
EIFR = _BV(INTF0); // clear INT0 interrupt flag
EIMSK |= _BV(INT0); // enable INT0 interrupt
// setup TIMER0
TCNT0 = 0; // clear counter
TIMSK0 = 0; // disable all interrupts of TIMER0
TIFR0 = _BV(OCF0B) | _BV(OCF0A); // clear TIMER0_COMPA & TIMER0_COMPB interrupt flags
TCCR0A = serial->cfg[0]; // set mode & prescaler of TIMER0
}
uint8_t serial_get_flag(serial_t *serial, serial_flag_t flag)
{
return serial->flag & flag;
}
void serial_send_data(serial_t *serial, uint8_t data)
{
serial->flag &= ~SERIAL_FLAG_TXE; // clear TXE flag
serial->tx_data = data; // store the data to transmit
serial->tx_temp = data;
serial->tx_idx = 0; // reset index of transmission
OCR0A = TCNT0 + serial->cfg[1] - 1; // set period of TIMER0_COMPA
PORTD &= ~_BV(PORTD1); // PD1 outputs low level
TIFR0 = _BV(OCF0A); // clear TIMER0_COMPA interrupt flag
TIMSK0 |= _BV(OCIE0A); // enable TIMER0_COMPA interrupt
}
uint8_t serial_receive_data(serial_t *serial)
{
uint8_t data = serial->rx_data; // read the data received
serial->flag &= ~SERIAL_FLAG_RXNE; // clear RXNE flag
return data;
}
static inline void serial_tx_timer_isr(serial_t *serial)
{
if (serial->tx_idx < 8) { // send databits
if (serial->tx_temp & 0x01) { // output the lowest bit
PORTD |= _BV(PORTD1);
} else {
PORTD &= ~_BV(PORTD1);
}
serial->tx_temp >>= 1;
} else if (serial->tx_idx == 8) { // send stopbit
PORTD |= _BV(PORTD1);
} else { // end of transmission
serial->flag |= SERIAL_FLAG_TXE; // set TXE flag
TIMSK0 &= ~_BV(OCIE0A); // disable TIMER0_COMPA interrupt
}
OCR0A += serial->cfg[1]; // set time of the next interrupt
serial->tx_idx++; // update index of transmission
}
static inline void serial_rx_int_isr(serial_t *serial)
{
OCR0B = TCNT0 + serial->cfg[2] - 1; // set time of the first TIMER0_COMPB interrupt
EIMSK &= ~_BV(INT0); // disable INT0 interrupt
TIFR0 = _BV(OCF0B); // clear TIMER0_COMPB interrupt flag
TIMSK0 |= _BV(OCIE0B); // enable TIMER0_COMPB interrupt
serial->rx_idx = 0; // reset index of reception
serial->rx_cnt = 0; // clear counter of 0/1
}
static inline void serial_rx_timer_isr(serial_t *serial)
{
serial->rx_cnt += PIND & _BV(PIND2) ? 0x10 : 0x01; // count 0/1
if (serial->rx_idx == 2) { // receive startbit
if (serial->rx_cnt > 0x20) { // if startbit is '1'
TIMSK0 &= ~_BV(OCIE0B); // disable TIMER0_COMPB interrupt
EIFR = _BV(INTF0); // clear INT0 interrupt flag
EIMSK |= _BV(INT0); // enable INT0 interrupt flag
}
serial->rx_cnt = 0; // reset counter of 0/1
} else if (serial->rx_idx == 29) { // receive stopbit
if (serial->rx_cnt > 0x20) { // if stopbit is '1'
serial->rx_data = serial->rx_temp; // the data received is valid, store it to serial->rx_data
serial->flag |= SERIAL_FLAG_RXNE; // set RXNE flag
}
TIMSK0 &= ~_BV(OCIE0B); // disable TIMER0_COMPB interrupt
EIFR = _BV(INTF0); // clear INT0 interrupt flag
EIMSK |= _BV(INT0); // clear INT0 interrupt flag
} else if (serial->rx_idx % 3 == 2) { // receive databits
serial->rx_temp >>= 1;
if (serial->rx_cnt > 0x20) {
serial->rx_temp |= 0x80;
}
serial->rx_cnt = 0; // reset counter of 0/1
}
OCR0B += serial->cfg[3]; // set time of the next interrupt
serial->rx_idx++; // update index of reception
}
ISR(TIMER0_COMPA_vect)
{
uint8_t sreg = SREG;
serial_tx_timer_isr(UART);
SREG = sreg;
}
ISR(INT0_vect)
{
uint8_t sreg = SREG;
serial_rx_int_isr(UART);
SREG = sreg;
}
ISR(TIMER0_COMPB_vect)
{
uint8_t sreg = SREG;
serial_rx_timer_isr(UART);
SREG = sreg;
}
注意:实测115200以下(含)的波特率发送都正常,但是9600以上(不含)的波特率接收不正常,建议日常使用9600波特率.
为了更方便的使用串口,可以将标准输入输出重定向到串口,在AVR GCC中的做法如下:
int putc(char c, FILE *stream);
int getc(FILE *stream);
FDEV_SETUP_STREAM
创建一个stream。
FILE s = FDEV_SETUP_STREAM(putc, getc, flag)
stdout
/ stdin
。
stdout = stdin = &s;
src/main.c 源文件的代码内容如下:
#include <stdint.h>
#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <serial.h>
static void stdio_setup(void);
int main(void)
{
cli();
stdio_setup();
sei();
printf("Hello, ATtiny88!\r\n");
for (;;) {
putchar(getchar());
}
}
static int serial_putchar(char c, FILE *stream)
{
while (!serial_get_flag(UART, SERIAL_FLAG_TXE));
serial_send_data(UART, c);
return 0;
}
static int serial_getchar(FILE *stream)
{
while (!serial_get_flag(UART, SERIAL_FLAG_RXNE));
return serial_receive_data(UART);
}
static void stdio_setup(void)
{
static FILE f = FDEV_SETUP_STREAM(serial_putchar, serial_getchar, _FDEV_SETUP_RW);
serial_setup(UART, SERIAL_BR_9600);
stdout = &f;
stdin = &f;
}
最后此篇关于ATtiny88初体验(三):串口的文章就讲到这里了,如果你想了解更多关于ATtiny88初体验(三):串口的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我想用 python 与我的串口通信。我为 linux 安装了 pyserial 和 uspp: import serial ser = serial.Serial('/dev/pts/1', 192
如何实现 IP 串行,反之亦然。 我听说可以用 SOCAT 做到这一点(9600 N,8,1)--> 串口 --> 网络 --> 串口 -->(9600 N81) 请求人们帮助我解决这个问题 最佳答案
ATtiny88初体验(三):串口 ATtiny88单片机不包含串口模块,因此只能使用软件方式模拟串口时序。 串口通信时序通常由起始位、数据位、校验位和停止位四个部分组成,常见的
我有一个 c# 应用程序,它通过串行端口将 pc 连接到设备。 当我向设备发送数据时,如果设备发回数据,则会触发 datareceived 事件。 我想问这个。 有没有办法模拟设备的数据发送? 我的意
所以我将数据从 Arduino 传输到 C# Winform,后者将数据输出到文本框并将其保存到文件中。传输数据格式如下18|25|999|100~;第一部分是以秒为单位的时间,它让我知道什么时候跳过
规范模式状态的 Termios 手册页 ( http://man7.org/linux/man-pages/man3/termios.3.html ): Input is made available
串口代码有问题。 我只是这样做: opencomm(); send(); closecomm(); ClearCommError()(在 recv() 内) 返回comstat.cbInQue 发送的
我想通过音频插孔使用串行端口获取数据。我对此一无所知。但是我找到了一个应用audioserial可以发送数据到。所以,我认为应该获取像 audioserial 这样的数据。 .是否有相同的项目或对此很
串口有问题 我写了一个程序,可以读取端口 COM1 到 COM9,但可以打开 COMXX(如 com10、com11 等) 我搜索并了解到 tCOM1–COM9 是 NT 命名空间中保留名称的一部分。
我正在尝试在 Linux 中使用串口组织 nob-blocking 读写功能。这是我的代码:http://pastebin.com/RSPw7HAi一切正常,但已缓冲。这意味着,如果我通过控制台 +
我想将出现在 Arduino 中的数据传输到我的 C# 应用程序,但不知道我的代码有什么问题。Arduino 代码来了: int switchPin = 7; int ledPin = 13; boo
我正在编写一个网络驱动程序,它应该使用串行通信将数据包发送到 Arduino。这是一项家庭作业,仅用于教育目的。请在建议一切都可以在用户空间中完成之前考虑到这一点。 这answer说明了 filp_o
我想在笔记本电脑和模块之间进行通信。为此,我创建了一个 python 文件,它将一些数据包发送到 UART,它必须读取它们。我有一个创建数据包的 python 脚本(笔记本电脑): SOF= '24'
正在寻找正确的方法来在主板启动消息期间检测一个关键字。检测到关键字后,一秒后发送 Enter 键。内核是Linux。 # Serial port inisialisation is finished
我尝试通过串口读取数据,但读取操作总是返回0。 // Opening COM port and m_fd returned a valid number m_fd = open (m_com_por
微 Controller :dsPIC33EP512MU810 编译器:MikroC 我正在尝试通过 UART 从远程设备请求多个字节。要获得所需的信息,您发送一个请求字节以接收一个数据字节。当请求超
我计划很快开始围绕串行设备的输入进行编码,很高兴找到 Ruby-serialport . API 看起来很容易使用,但我对如何采用基于事件的方法来接收数据有点困惑。 每当 \n 出现时,我想对数据做一
我想在 Linux 中实现一个驱动程序,它有一个以太网堆栈,但在硬件上输出的数据将是一个串行端口。基本上,我想将我的串行端口注册为以太网驱动程序。有谁知道这是否可能?我希望能够将 IPv6 和/或 U
我正在开发一个项目,其中有许多硬件传感器通过 RS232 串行端口连接到部署机器。 但是……我正在一台没有物理 RS232 串行端口的机器上进行开发,但我想制作假的串行端口,我可以连接到这些端口并
我正在制作一个非常简单的 c++ 程序,它通过串行端口向 arduino 发送一个角度,然后 arduino 将该角度应用于伺服电机。我知道 Unix 把串口设备看成一个文件,实际上这是 c++ 代码
我是一名优秀的程序员,十分优秀!