——串口USART实战部分)
一、前言学习了UART的相关理论知识我就让ai给我出题目了然后自己写的肯定第一遍都是有问题的还得改正。二、题目三、我的解答第一版答案void reg_serial_init(void) { //tx A9 Rx A10 RCC-APB2ENR | (1ul2) | (1ul14); //打开GPIOA,UART1时钟 GPIOA-CRH ~(0xF4*1 | 0xF4*2); //清零PA9PA10的原始配置 GPIOA-CRH | (0xB4*1); //PA9为Tx引脚配置为复用推挽输出50MHz GPIOA-CRH | (0x84*2); //PA10为Rx引脚配置为上拉输入 GPIOA-BSRR | (1ul10); //上拉PA10 USART1-BRR 0x0271; //对应二进制0000 0010 0111.0001是分频系数39.0625 USART1-CR1 | (1ul2) | (1ul3) | (1ul5); //开启接收、发送使能开启接收中断 //SR2寄存器不需要配置00默认为1位停止位CR3也不需要配置默认其他功能都关闭 USART1-CR1 | (1ul13); //最后开启UART NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置优先级位数 NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannelUSART1_IRQn; //选择USART1中断 NVIC_InitStructure.NVIC_IRQChannelCmdENABLE; //使能中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; //设置抢占优先级和响应优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority1; NVIC_Init(NVIC_InitStructure); } void reg_serial_send_arr(uint8_t *data) { //通过DR寄存器发送数据 int data_len sizeof(data)/sizeof(uint8_t); for(int i0;idata_len;i) { USART1-DR data[i]; } } void reg_serial_send_byte(uint8_t data) { USART1-DR data; //数据直接写入DR寄存器 } uint8_t reg_serial_receive_byte(void) { uint8_t data; data USART1-DR; //读取DR寄存器的值然后返回 return data; } void serial_send_byte_wait(uint8_t data) //在while循环里调用查询方式发送 1 字节数据的流程 { if(USART1-SR (1ul6)) //先检查有没有发送完成 { if(USART1-SR (1ul7)) //再检查发送移位寄存器有没有发送完数据 { reg_serial_send_byte(data); //发送一字节数据 } } } void USART1_IRQHandler(void) { if(USART1-SR (1ul5)) //判断是否是接收中断 { uint8_t data reg_serial_receive_byte(); //接收一字节数据对DR寄存器进行读操作可以自动清零该中断请求 reg_serial_send_byte(data); //读出后发送回去 } //下面接其他类型的中断请求比如这里是判断是否发送完数据的中断请求当然得确定在CR1寄存器中开启TC中断 if(USART1-SR (1ul6)) {} }AI给的错误提示reg_serial_send_arr里用sizeof(data)/sizeof(uint8_t)获取长度错误形参是指针sizeof 拿到的是指针字节数不是数组长度。reg_serial_send_byte、reg_serial_send_arr直接写 DR没有先查询 TXE 标志上一字节没移入移位寄存器时写入会覆盖 TDR 数据造成丢字节。serial_send_byte_wait逻辑问题发送下一字节只需要判断 TXE不需要额外判断 TC使用 if 而不是 while标志不满足时函数直接退出数据发不出去。中断服务函数内调用reg_serial_send_byte(data)同样缺少 TXE 判断存在数据覆盖丢失风险。serial_send_byte_wait里面同时判断 TC 和 TXE 是多余的TC 代表整帧含停止位发送完成不是载入下一个数据的判断条件。第二版答案尝试改了sizeof的参数为*data但是其实这只是一个数据的字节长度所以是错的然后在发送相关的函数添加了判断发送数据寄存器状态的判断if(USART1-SR (1ul7))防止数据被覆盖防止数据覆盖的问题把发送时判断TC的语句删了我以为要先判断发送是否完成才能判断要不要继续发送void reg_serial_init(void) { //tx A9 Rx A10 RCC-APB2ENR | (1ul2) | (1ul14); //打开GPIOA,UART1时钟 GPIOA-CRH ~(0xF4*1 | 0xF4*2); //清零PA9PA10的原始配置 GPIOA-CRH | (0xB4*1); //PA9为Tx引脚配置为复用推挽输出50MHz GPIOA-CRH | (0x84*2); //PA10为Rx引脚配置为上拉输入 GPIOA-BSRR | (1ul10); //上拉PA10 USART1-BRR 0x0271; //对应二进制0000 0010 0111.0001是分频系数39.0625 USART1-CR1 | (1ul2) | (1ul3) | (1ul5); //开启接收、发送使能开启接收中断 //SR2寄存器不需要配置00默认为1位停止位CR3也不需要配置默认其他功能都关闭 USART1-CR1 | (1ul13); //最后开启UART NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置优先级位数 NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannelUSART1_IRQn; //选择USART1中断 NVIC_InitStructure.NVIC_IRQChannelCmdENABLE; //使能中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; //设置抢占优先级和响应优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority1; NVIC_Init(NVIC_InitStructure); } void reg_serial_send_arr(uint8_t *data) { //通过DR寄存器发送数据 int data_len sizeof(*data)/sizeof(uint8_t); for(int i0;idata_len;i) { if(USART1-SR (1ul7)) //判断发送数据寄存器是否为空防止数据被覆盖 USART1-DR data[i]; } } void reg_serial_send_byte(uint8_t data) { if(USART1-SR (1ul7)) //判断发送数据寄存器是否为空防止数据被覆盖 USART1-DR data; //数据直接写入DR寄存器 } uint8_t reg_serial_receive_byte(void) { uint8_t data; data USART1-DR; //读取DR寄存器的值然后返回 return data; } void serial_send_byte_wait(uint8_t data) //在while循环里调用查询方式发送 1 字节数据的流程 { if(USART1-SR (1ul7)) //再检查发送移位寄存器有没有发送完数据 { reg_serial_send_byte(data); //发送一字节数据 } } void USART1_IRQHandler(void) { if(USART1-SR (1ul5)) //判断是否是接收中断 { uint8_t data reg_serial_receive_byte(); //接收一字节数据对DR寄存器进行读操作可以自动清零该中断请求 reg_serial_send_byte(data); //读出后发送回去 } //下面接其他类型的中断请求比如这里是判断是否发送完数据的中断请求当然得确定在CR1寄存器中开启TC中断 if(USART1-SR (1ul6)) {} }AI给的问题reg_serial_send_arr里int data_len sizeof(*data)/sizeof(uint8_t);错误*data是单个 uint8_tsizeof 结果永远是 1无法得到数组长度函数形参是指针不能这样获取数组长度。reg_serial_send_arr、reg_serial_send_byte、serial_send_byte_wait使用 if 判断 TXE属于非阻塞判断如果此时 TXE0数据直接丢弃不会等待发送条件满足数据发不出去。中断服务函数里面调用reg_serial_send_byte(data)同样是 if 判断 TXE若此时 TXE0收到的数据直接丢失回显失败。reg_serial_send_arr的 for 循环如果某次 TXE0这一轮 i 对应的字节直接跳过循环继续下一个会出现乱序丢字节。注释笔误代码里写的SR2寄存器实际是 USART_CR2。第三版答案主要还是这2个函数的问题我修改了判断TXE的方式但是逻辑错误了我修改发送整包数据的参数为数组但是在sizeof函数里面数组会退化成指针结果还是1AI给的提示reg_serial_send_byte的 while 条件写反while(USART1-SR (1ul7))代表 TXE1 的时候进入循环、反复写 DRTXE 一旦变成 0循环立刻退出。正确应该是等待 TXE 不等于 1TXE0时空循环卡住等 TXE 变为 1 之后跳出循环再写 DR也就是while(!(USART1-SR (1ul7)));循环体内不写 DR。你现在的写法会疯狂重复写入同一个字节。reg_serial_send_arr里int data_len sizeof(data)/sizeof(uint8_t);仍然错误形参uint8_t data[]等价指针sizeof 获取的是指针大小得不到数组长度。void reg_serial_send_byte(uint8_t data) { while(USART1-SR (1ul7)) //判断发送数据寄存器是否为空防止数据被覆盖 USART1-DR data; //数据直接写入DR寄存器 } void reg_serial_send_arr(uint8_t data[]) { //通过DR寄存器发送数据 int data_len sizeof(data)/sizeof(uint8_t); for(int i0;idata_len;i) { reg_serial_send_byte(data[i]); } }第四版答案解决数组长度的问题就是直接从外部获取数组长度因为外部获取时不受退化影响如果是内部计算数组会退化成指针uint8_t buf[] {0x11,0x00,0x22,0x33}; reg_serial_send_arr(buf, sizeof(buf));这里在调用方用sizeof(buf)只有在调用处、buf 还是真实数组的时候sizeof才能算出真实数组大小一旦传到函数内部变成指针就不行了。后面单独修改发送整包数据的函数了如下void reg_serial_send_arr(uint8_t data[],int len) { //通过DR寄存器发送数据 for(int i0;ilen;i) { reg_serial_send_byte(data[i]); } }