USART_TypeDef* SERIAL_USART[SERIALn] = {RIKI_SERIAL1, RIKI_SERIAL2, RIKI_SERIAL3};
GPIO_TypeDef* SERIAL_PORT[SERIALn] = {RIKI_SERIAL1_GPIO_PORT, RIKI_SERIAL2_GPIO_PORT, RIKI_SERIAL3_GPIO_PORT};
const uint32_t SERIAL_USART_CLK[SERIALn] = {RIKI_SERIAL1_CLK, RIKI_SERIAL2_CLK, RIKI_SERIAL3_CLK};
const uint32_t SERIAL_PORT_CLK[SERIALn] = {RIKI_SERIAL1_GPIO_CLK, RIKI_SERIAL2_GPIO_CLK, RIKI_SERIAL3_GPIO_CLK};
const uint16_t SERIAL_TX_PIN[SERIALn] = {RIKI_SERIAL1_TX_PIN, RIKI_SERIAL2_TX_PIN, RIKI_SERIAL3_TX_PIN};
const uint16_t SERIAL_RX_PIN[SERIALn] = {RIKI_SERIAL1_RX_PIN, RIKI_SERIAL2_RX_PIN, RIKI_SERIAL2_RX_PIN};
const uint16_t SERIAL_IRQn[SERIALn] = {RIKI_SERIAL1_IRQ, RIKI_SERIAL2_IRQ, RIKI_SERIAL3_IRQ};
const uint16_t SERILA_NVIC[SERIALn] = {RIKI_SERIAL1_NVIC, RIKI_SERIAL2_NVIC, RIKI_SERIAL3_NVIC};
HardwareSerial::HardwareSerial(Serial_TypeDef _Serial)
{
Serial = _Serial;
if(this->Serial == SERIAL1)
Serial1 = this;
if(this->Serial == SERIAL2)
Serial2 = this;
if(this->Serial == SERIAL3)
Serial3 = this;
}
- this->Serial???
void HardwareSerial::begin(uint32_t baud)
{//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//使能USART_CLK,PORT_CLK(类比使能USART1,GPIOA)
if(this->Serial == SERIAL1) {
RCC_APB2PeriphClockCmd(SERIAL_USART_CLK[this->Serial]|SERIAL_PORT_CLK[this->Serial], ENABLE);
} else {
RCC_APB1PeriphClockCmd(SERIAL_USART_CLK[this->Serial], ENABLE);
RCC_APB2PeriphClockCmd(SERIAL_PORT_CLK[this->Serial], ENABLE);
}
//USART_TX
GPIO_InitStructure.GPIO_Pin = SERIAL_TX_PIN[this->Serial];
//类比 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_Init(SERIAL_PORT[this->Serial], &GPIO_InitStructure);
//类比 GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.0 发送端
//USART_RX
GPIO_InitStructure.GPIO_Pin = SERIAL_RX_PIN[this->Serial];
// 类比GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PA.10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(SERIAL_PORT[this->Serial], &GPIO_InitStructure);
//类比 GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.10 接收端
//USART初始化设置
USART_InitStructure.USART_BaudRate = baud; // 波特率设置
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //全双工收发模式
USART_Init(SERIAL_USART[this->Serial], &USART_InitStructure); //初始化串口
//USART NVIC中断配置
NVIC_InitStructure.NVIC_IRQChannel = SERIAL_IRQn[this->Serial]; //对应中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0; //抢先优先级0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = SERILA_NVIC[this->Serial]; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure);//中断优先级配置
USART_ITConfig(SERIAL_USART[this->Serial], USART_IT_RXNE, ENABLE);//开启中断
USART_Cmd(SERIAL_USART[this->Serial], ENABLE);//使能串口
}
- 所有的GPIOn,USARTn,子优先级都以【this->serial】代替了???
uint32_t HardwareSerial::available(void)//available[r] 当前系统对r类资源的可用资源数
{
return (uint32_t)(SERIAL_BUFFER_SIZE + rx_buffer._iHead - rx_buffer._iTail) % SERIAL_BUFFER_SIZE ;
}//buffer size缓冲区大小
uint8_t HardwareSerial::read(void)
{
// if the head isn't ahead of the tail, we don't have any characters
if ( rx_buffer._iHead == rx_buffer._iTail )
return -1 ;
//只有头与尾有一定差距才有缓冲区大小为正
uint8_t uc = rx_buffer._aucBuffer[rx_buffer._iTail] ;
rx_buffer._iTail = (unsigned int)(rx_buffer._iTail + 1) % SERIAL_BUFFER_SIZE ;
return uc ;
}
- 有关buffer.size???
uint32_t HardwareSerial::write(uint8_t ch)
{
USART_SendData(SERIAL_USART[this->Serial], ch);
while(USART_GetFlagStatus(SERIAL_USART[this->Serial], USART_FLAG_TXE) == RESET); //判断是否接受中断
return 1;
}
USART_FLAG_TXE 和USART_FLAG_TC
USART_FLAG_TXE发送缓冲区空标志:说明可以往数据寄存器写入数据了,但并不代码数据发送完成了。
USART_FLAG_TC发送完成标志:这个才是代表USART在缓冲区的数据发送完成了,即从机接收到了数据。
这两个标志的区别在于:它们分别表示数据在发送过程中,在两个不同的阶段中的完成情况。TXE 表示数据被从发送缓冲区中取走,转移到的移位寄存器中,此时发送缓冲是空的,可以向其中补充新的数据了。而 TC 则表示最后放入发送缓冲区的数据已经完成了从移位寄存器向发送信号线 Tx 上的转移。所以,判定数据最终发送完成的标志是 TC,而不是 TXE.
TXE是指“弹仓”空;1为空
TC是“枪膛”空。
也就是说,你写数据到串口时,是装入弹仓,硬件会将数据移到枪膛,这时,TXE为1,TC为0,STM32硬件的TX脚正在发送数据,但你还可以装入数据到弹仓,装入后,TXE为0,TC为0.
TX发送完一个数据后,立即将数据从弹仓移入枪膛,这时,TXE为1,TC为0.
最后TX发送完数据,你又没有装入新数据,这时。TXE为1,TC为1.
void HardwareSerial::flush()
{
rx_buffer._iTail = rx_buffer._iHead;
}
Flush() 是清空,而不是刷新
flush本意是冲刷,这个方法大概取自它引申义冲马桶的意思,马桶有个池子,你往里面扔东西,会暂时保存在池子里,只有你放水冲下去,东西才会进入下水道。
同理很多流都有一个这样的池子,专业术语叫缓冲区,当你print或者write的时候,会暂时保存在缓冲区,并没有发送出去,这是出于效率考虑的,因为数据不会自己发送过去,必须有其他机制,而且这个很消耗资源,就像马桶你需要很多水,才能冲走,你如果扔一点东西,就冲一次水,那你水费要爆表了,同样如果你写一行文字,或者一个字节,就要马上发送出去,那网络流量,CPU使用率等等都要爆表了,所以一般只有在你真正需要发送否则无法继续的时候,调用flush,将数据发送出去。
void HardwareSerial::print(const char *format, ...)
{
va_list args;//是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行,定义args为va_list类型的变量
char buf[256];
va_start (args, format);//让args指向可变参数表里面的第一个参数,format是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;
vsprintf (buf, format, args);
va_end (args); //获取所有的参数之后,将这个 args指针关掉,输入的参数 args 置为 NULL,应该养成获取完参数表之后关闭指针的习惯
putstr(buf);
}
va_list 是在C语言中解决变参问题的一组宏,变参问题是指参数的个数不定,可以是传入一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有实际的名称与之相对应,用起来是很灵活。
va_arg是获取参数,它的第一个参数是args,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 args 的位置指向变参表的下一个变量位置
当调用va_start(list,param1) 时:list指针指向情况对应下图:
函数参数的传递原理
函数参数是以数据结构:栈的形式存取,从右至左入栈。
首先是参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址,举个例子如下:
void func(int x, float y, char z);
那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。
void HardwareSerial::putstr(const char *str)//putstr(); 输出数组
{
int i;
for(i = 0; i < strlen(str); i++){ //strlen()函数用来计算字符串的长度
write(str[i]);//写到一文件中
}
}
这是定义了一个 char类型的指针数组常量,const 定义后不能修改
str[1]里存放的是指向"one"字符数组的指针,
const char *str[]={“zero”,“one”,“two”,“three”,“four”,“five”,“six”,“seven”,“eight”,“nine”};
printf(str[1]);
输出 one
void HardwareSerial::irq()//中断请求
{
uint8_t data;
if (USART_GetITStatus(SERIAL_USART[Serial], USART_IT_RXNE) != RESET)//接收中断 {
data = USART_ReceiveData(SERIAL_USART[Serial]);
rx_buffer.store_char(data) ; //读取接收到的数据
USART_ClearITPendingBit(SERIAL_USART[Serial], USART_IT_RXNE); //清除相对应的标志位
}
}
USART_ClearFlag()和USART_ClearITPendingBit()
https://blog.csdn.net/qq_35629563/article/details/80879819
实际上两个函数实现的功能是一样的,都是清除相对应的标志位,只是标志位和中断位含义不一样,是标志位但不一定会产生中断
#define USART_IT_TXE ((uint16_t)0x0727)
#define USART_IT_TC ((uint16_t)0x0626)
#define USART_IT_RXNE ((uint16_t)0x0525)
这是中断位,可以产生中断
中断是跳转到中断函数执行
#define USART_FLAG_TXE ((uint16_t)0x0080)
#define USART_FLAG_TC ((uint16_t)0x0040)
#define USART_FLAG_RXNE ((uint16_t)0x0020)
这是标志位,有的标志位不能产生中断
标志位在程序中可以作为判定条件,支持程序的运行
USART_ClearFlag函数
//调用形式
USART_ClearFlag(USART3,USART_FLAG_TC);
//USART_FLAG_TC的定义
#define USART_FLAG_TC ((uint16_t)0x0040)
//USART_ClearFlag函数的原型
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG)
{
/* Check the parameters */
assert_param(IS_USART_ALL_PERIPH(USARTx));
assert_param(IS_USART_CLEAR_FLAG(USART_FLAG));
assert_param(IS_USART_PERIPH_FLAG(USARTx, USART_FLAG)); /* The CTS flag is not available for UART4 and UART5 */
USARTx->SR = (uint16_t)~USART_FLAG;
}
USART_ClearITPendingBit函数
//调用形式
USART_ClearITPendingBit(USART3,USART_IT_TC);
//USART_IT_TC的定义
#define USART_IT_TC ((uint16_t)0x0626)//=0000'0110'0010'0110
//USART_ClearITPendingBit的函数原型
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT)
{
uint16_t bitpos = 0x00, itmask = 0x00;
/* Check the parameters */
assert_param(IS_USART_ALL_PERIPH(USARTx));
assert_param(IS_USART_CLEAR_IT(USART_IT));
assert_param(IS_USART_PERIPH_IT(USARTx, USART_IT)); /* The CTS interrupt is not available for UART4 and UART5 */
bitpos = USART_IT >> 0x08; //=0000'0110
itmask = (uint16_t)((uint16_t)0x01 << bitpos); //=0100'0000
USARTx->SR = (uint16_t)~itmask; //=~(0100'0000)
}
注意,这两个函数的第二个参数,是不一样的