调试过程中对接口协议有了一定了解,对芯片max232了解还是比较深入的
最流行的串口程序,用了特权同学程序,直接用来跑,果断不行啊,然后仔细看了遍程序,发现一些问题,程序计数中算了校验位,却没有对校验位进行传输,上位机上自然还要设置校验位(odd奇,even偶);后面以为还有一个错误,后来发现是自己的问题,对阻塞,非阻塞理解不够啊,num<=num+1;case(num),num状态是从0开始啊(而不是1),又自己去写了一个,还是调不通。
又去找了一个经验证版的,思想比上面的都简练很多,也很容易读懂,试了下,不行,换了个串口,发现重大突破了,竟让5个码能收对3个了,试着把上位机的停止位设成两位,OK了,好吧,把这个简练的程序各模块贴出来,各模块用原理图方式连接起来更方便,用语言写也可以的啦。本人是40M晶振,调了这么长时间,原来垃圾的坏串口真是坑爹死了。后来用新串口试了特权的程序,也跑通了。下面这个程序虽然长点,但是思想简单,没有绕太多。真心话,特权同学的程序绕太多了
总的来说,对于串口传输,主要还是按着时钟频率,计数,依次按bit接收和发送数据,用case语句控制较好,各大程序中,最大的差异还是在对波特率的书写上,发送阶段自然和上位机采用相同的波特率,接收阶段需要16*波特率,数据的中间点进行采样,相当于的到的数据还是以与上位机是相同的波特率(频率),好吧我承认这东西不难,是我太笨了,调了这长时间
采样时钟模块:
module clkp(clk, clkout);
input clk; //系统时钟
output clkout; //采样时钟输出
reg clkout;
reg [15:0] cnt;
always @(posedge clk) //分频进程
begin
if(cnt == 16'd130)
begin
clkout <= 1'b1;
cnt <= cnt + 16'd1;
end
else if(cnt == 16'd260)
begin
clkout <= 1'b0;
cnt <= 16'd0;
end
else
begin
cnt <= cnt + 16'd1;
end
end
endmodule
发送模块:
module uarttx(clk, datain, wrsig, idle, tx);
input clk; //UART时钟
input [7:0] datain; //需要发送的数据
input wrsig; //发送命令,上升沿有效
output idle; //线路状态指示,高为线路忙,低为线路空闲
output tx; //发送数据信号
reg idle, tx;
reg send;
reg wrsigbuf, wrsigrise;
reg presult;
reg[7:0] cnt; //计数器
parameter paritymode = 1'b0;
//检测发送命令是否有效
always @(posedge clk)
begin
wrsigbuf <= wrsig;
wrsigrise <= (~wrsigbuf) & wrsig;
end
always @(posedge clk)
begin
if (wrsigrise && (~idle)) //当发送命令有效且线路为空闲时,启动新的数据发送进程
begin
send <= 1'b1;
end
else if(cnt == 8'd176) //一帧资料发送结束
begin
send <= 1'b0;
end
end
always @(posedge clk)
begin
if(send == 1'b1)
begin
case(cnt) //产生起始位
8'd0:
begin
tx <= 1'b0;
idle <= 1'b1;
cnt <= cnt + 8'd1;
end
8'd16:
begin
tx <= datain[0]; //发送数据0位
presult <= datain[0]^paritymode;
idle <= 1'b1;
cnt <= cnt + 8'd1;
end
8'd32:
begin
tx <= datain[1]; //发送数据1位
presult <= datain[1]^presult;
idle <= 1'b1;
cnt <= cnt + 8'd1;
end
8'd48:
begin
tx <= datain[2]; //发送数据2位
presult <= datain[2]^presult;
idle <= 1'b1;
cnt <= cnt + 8'd1;
end
8'd64:
begin
tx <= datain[3]; //发送数据3位
presult <= datain[3]^presult;
idle <= 1'b1;
cnt <= cnt + 8'd1;
end
8'd80:
begin
tx <= datain[4]; //发送数据4位
presult <= datain[4]^presult;
idle <= 1'b1;
cnt <= cnt + 8'd1;
end
8'd96:
begin
tx <= datain[5]; //发送数据5位
presult <= datain[5]^presult;
idle <= 1'b1;
cnt <= cnt + 8'd1;
end
8'd112:
begin
tx <= datain[6]; //发送数据6位
presult <= datain[6]^presult;
idle <= 1'b1;
cnt <= cnt + 8'd1;
end
8'd128:
begin
tx <= datain[7]; //发送数据7位
presult <= datain[7]^presult;
idle <= 1'b1;
cnt <= cnt + 8'd1;
end
8'd144:
begin
tx <= presult; //发送奇偶校验位
presult <= datain[0]^paritymode;
idle <= 1'b1;
cnt <= cnt + 8'd1;
end
8'd160:
begin
tx <= 1'b1; //发送停止位
idle <= 1'b1;
cnt <= cnt + 8'd1;
end
8'd176:
begin
tx <= 1'b1;
idle <= 1'b0; //一帧资料发送结束
cnt <= cnt + 8'd1;
end
default:
begin
cnt <= cnt + 8'd1;
end
endcase
end
else
begin
tx <= 1'b1;
cnt <= 8'd0;
idle <= 1'b0;
end
end
endmodule
接受模块:
UART的接收数据时序为:当检测到数据的下降沿时,表明线路上有数据进行传输,这时计数器CNT开始计数,当计数器为24=16+8时,采样的值为第0位数据;当计数器的值为40时,采样的值为第1位数据,依此类推,进行后面6个数据的采样
module uartrx(clk, rx, dataout, rdsig, dataerror, frameerror);
input clk; //采样时钟
input rx; //UART数据输入
output dataout; //接收数据输出
output rdsig;
output dataerror; //资料出错指示
output frameerror; //帧出错指示
reg[7:0] dataout;
reg rdsig, dataerror;
reg frameerror;
reg [7:0] cnt;
reg rxbuf, rxfall, receive;
parameter paritymode = 1'b0;
reg presult, idle;
always @(posedge clk) //检测线路的下降沿
begin
rxbuf <= rx;
rxfall <= rxbuf & (~rx);
end
always @(posedge clk)
begin
if (rxfall && (~idle)) //检测到线路的下降沿并且原先线路为空闲,启动接收数据进程
begin
receive <= 1'b1;
end
else if(cnt == 8'd175) //接收数据完成
begin
receive <= 1'b0;
end
end
always @(posedge clk)
begin
if(receive == 1'b1)
begin
case (cnt)
8'd0:
begin
idle <= 1'b1;
cnt <= cnt + 8'd1;
rdsig <= 1'b0;
end
8'd24: //接收第0位数据
begin
idle <= 1'b1;
dataout[0] <= rx;
presult <= paritymode^rx;
cnt <= cnt + 8'd1;
rdsig <= 1'b0;
end
8'd40: //接收第1位数据
begin
idle <= 1'b1;
dataout[1] <= rx;
presult <= presult^rx;
cnt <= cnt + 8'd1;
rdsig <= 1'b0;
end
8'd56: //接收第2位数据
begin
idle <= 1'b1;
dataout[2] <= rx;
presult <= presult^rx;
cnt <= cnt + 8'd1;
rdsig <= 1'b0;
end
8'd72: //接收第3位数据
begin
idle <= 1'b1;
dataout[3] <= rx;
presult <= presult^rx;
cnt <= cnt + 8'd1;
rdsig <= 1'b0;
end
8'd88: //接收第4位数据
begin
idle <= 1'b1;
dataout[4] <= rx;
presult <= presult^rx;
cnt <= cnt + 8'd1;
rdsig <= 1'b0;
end
8'd104: //接收第5位数据
begin
idle <= 1'b1;
dataout[5] <= rx;
presult <= presult^rx;
cnt <= cnt + 8'd1;
rdsig <= 1'b0;
end
8'd120: //接收第6位数据
begin
idle <= 1'b1;
dataout[6] <= rx;
presult <= presult^rx;
cnt <= cnt + 8'd1;
rdsig <= 1'b0;
end
8'd136: //接收第7位数据
begin
idle <= 1'b1;
dataout[7] <= rx;
presult <= presult^rx;
cnt <= cnt + 8'd1;
rdsig <= 1'b1;
end
8'd152: //接收奇偶校验位
begin
idle <= 1'b1;
if(presult == rx)
dataerror <= 1'b0;
else
dataerror <= 1'b1; //如果奇偶校验位不对,表示数据出错
cnt <= cnt + 8'd1;
rdsig <= 1'b1;
end
8'd168:
begin
idle <= 1'b1;
if(1'b1 == rx)
frameerror <= 1'b0;
else
frameerror <= 1'b1; //如果没有接收到停止位,表示帧出错
cnt <= cnt + 8'd1;
rdsig <= 1'b1;
end
default:
begin
cnt <= cnt + 8'd1;
end
endcase
end
else
begin
cnt <= 8'd0;
idle <= 1'b0;
rdsig <= 1'b0;
end
end
endmodule