【平头哥RVB2601创意应用开发】模拟UART一
[复制链接]
【前言】RVB2601的项目中我原来考虑需要3个串口,一个用于接收电压、电流、功率监测的数据,另一路用于接收无线发过来的温湿度,第三路用于LOG的输入输出,现在LOG输入占用了UART0,UART1的默认端口被SPI占用了。所以只能考虑用IO来实现UART。
【可行性分析】模拟UART我以前没有实现过,但是听说过,所以说只有想不到的,没有做不到的。而且最近在试用报告中的@qq4988已经实现了,有了他的文章给了我信心。经过查阅资料,很多基于51、stm32的都有过成功实现用IO模拟实现了串口的例子。我想应该能实现这一功能。
【知识储备】
1、认真的学习了RISC-V RVB2601 初体验--第3节--IO模拟串口完成 https://bbs.eeworld.com.cn/thread-1196608-1-1.html这篇文章N篇,也留了言,可能是作者工作原因没有空回复。
2、网上学习了基于51、stm32的文章,最有借鉴意义的是这篇:单片机IO口模拟UART串口通信_C语言中文网 (biancheng.net)。大家如果有兴趣也可以去读一下这篇帖子。
3、串口发送的时序图学习,我这里只应用8位,1位停止位,无较验,波特率为9600.
4、再加上自己的N种设想。。。略去一万字。。。
【实现需要的外设】
1、两个IO,经仔佃查看原理图后,只能拿三个LED来开刀,PA7、PA25、PA4这三个IO,其实也可以用两个KEY,但是KEY可能还有其他的用处,所以用PA7作为TX,PA25作为RX来用。
2、定时器,由于串口需要精确的延时来实现,借鉴网友的经验,特别是@qq4988的经验,用定时器timer1来实现。
【实现的思路】
1、初始化PA7为TX,gpio输出,初始化PA25为中断输入。
2、初始化定时器,设置为0.104为一次中断触发,为波特率9600。
3、创建定时器中断回调函数。
【实现步聚】
1、定义发送结收结构体,用于发送接收:
enum{
VM_SEND,
VM_REVC,
IDLE
};
typedef struct {
uint8_t vm_uart_dir;//发送,接收
uint8_t vm_uart_tx_byte;//发送数据
uint8_t vm_uart_tx_bit;
uint8_t vm_uart_tx_flag;
uint8_t vm_uart_rx_byte;
uint8_t vm_uart_rx_irq;
uint8_t vm_uart_rx_count;
uint8_t vm_uart_tx_buf[UART_MAX_LEN];
uint8_t vm_uart_rx_buf[UART_MAX_LEN];
} _io_uart_para;
2、初始化IO、中断、定时器:
/*模拟UART IO初始化*/
void io_uart_init(void)
{
csi_pin_set_mux(PA7, PIN_FUNC_GPIO);
csi_pin_set_mux(PA25, PIN_FUNC_GPIO);
csi_gpio_pin_init(&io_uart0_tx, PA7);
csi_gpio_pin_init(&io_uart0_rx,PA25);
csi_gpio_pin_dir(&io_uart0_tx,GPIO_DIRECTION_OUTPUT);
csi_gpio_pin_dir(&io_uart0_rx,GPIO_DIRECTION_INPUT);
csi_gpio_pin_mode(&io_uart0_tx,GPIO_MODE_OPEN_DRAIN);
csi_gpio_pin_mode(&io_uart0_rx,GPIO_MODE_PULLNONE);
csi_gpio_pin_attach_callback(&io_uart0_rx,io_uart0_rx_interrupt_handler,NULL);
csi_gpio_pin_irq_mode(&io_uart0_rx,GPIO_IRQ_MODE_FALLING_EDGE);
csi_gpio_pin_irq_enable(&io_uart0_rx,true);
io_uart_para.vm_uart_dir = IDLE;
io_uart_para.vm_uart_tx_bit = 0;
io_uart_para.vm_uart_rx_count = 0;
io_uart0_tx_write_High;
}
//定时器初始化
void timer1_init(void)
{
int ret = -1;
timer1.port = TIMER1_PORT_NUM;
//timer1.config.period = 1000000; //1s
timer1.config.period = 104;//0.104ms
timer1.config.reload_mode = TIMER_RELOAD_AUTO;
timer1.config.cb = timer1_handler;
ret = hal_timer_init(&timer1);
if(ret != 0)
{
printf("timer1 init error !\n");
}
else{
hal_timer_stop(&timer1);
}
}
3、定时器发送函数:
//定时器回调函数
void timer1_handler(void *arg)
{
if(io_uart_para.vm_uart_dir == VM_SEND)//如果是发送
{
if(io_uart_para.vm_uart_tx_bit <=8 ) //如果发送不足8位
{
if((io_uart_para.vm_uart_tx_byte & 0x01) == 0x01 ){
io_uart0_tx_write_High;
}else{
io_uart0_tx_write_Low;
}
io_uart_para.vm_uart_tx_byte>>=1;
}
else{
if(io_uart_para.vm_uart_tx_bit > 8){
io_uart0_tx_write_High;
io_uart0_rx_timer_disable();
io_uart_para.vm_uart_tx_flag = 0; //设置为发送完毕
io_uart_para.vm_uart_dir = IDLE;
}
}
io_uart_para.vm_uart_tx_bit ++;
}
}
4、发送测试:
int main(void)
{
board_yoc_init();
LOGD(TAG, "%s\n", aos_get_app_version());
oled_init();
io_uart_init();
timer1_init();
LOGD(TAG, "led and key test");
while (1) {
io_uart_para.vm_uart_tx_byte = 0x01;
io_uart_para.vm_uart_tx_bit = 0;
io_uart_para.vm_uart_dir = VM_SEND;
io_uart0_tx_write_Low;
io_uart0_rx_timer_enable();
while(io_uart_para.vm_uart_dir == IDLE);
//io_uart0_tx_write_High;
LOGD(TAG, "send 0x1end");
//aos_msleep(2000);
io_uart0_tx_write_Low;
io_uart_para.vm_uart_tx_byte = 0x02;
io_uart_para.vm_uart_tx_bit = 0;
io_uart_para.vm_uart_dir = VM_SEND;
io_uart0_rx_timer_enable();
while(io_uart_para.vm_uart_dir == IDLE);
LOGD(TAG, "send 0x2end");
//aos_msleep(2000);
io_uart0_tx_write_Low;
io_uart_para.vm_uart_tx_byte = 0x03;
io_uart_para.vm_uart_tx_bit = 0;
io_uart_para.vm_uart_dir = VM_SEND;
io_uart0_rx_timer_enable();
while(io_uart_para.vm_uart_dir == IDLE);
LOGD(TAG, "send 0x3end");
aos_msleep(2000);
}
return 0;
}
【实验结果】:
【讨论】这次摸拟串口发送数据还只是成功的三分之一不到,还有串口接收功能没写完。在这里自己发出来,一来是再复习一遍,二来也是为记录。
下一步,对发送函数进行封装,再努力把串口接收功能写出来。
|