不推荐自己新建工程,直接用他给的例程,复制一份,然后加上一个 bsp 文件夹放自己新建的.h.c 文件就行了,会方便很多。
LCD sprintf 格式sprintf((char*)buf," The Humam : %d ",200);
注意是(char*)不是(char);
注意 字符长度不能超过20(数组长度),否则LED不会亮。
长度最好写 19 个。
输出百分号
sprintf((char*) display_buf,"%d %% ",10); //输出百分号:%
注:要打两个百分号才行。
输出格式:%R
%d 整型输出,%ld 长整型输出,
%o 以八进制数形式输出整数,
%x 以十六进制数形式输出整数,
%u 以十进制数输出 unsigned 型数据 (无符号数)。
%c 用来输出一个字符,
%s 用来输出一个字符串,
%f 用来输出实数,以小数形式输出,
%e 以指数形式输出实数,
%g 根据大小自动选 f 格式或 e 格式,且不输出无意义的零。
memset函数
memset(buf,0,20 * sizeof(uint8_t));
memset 的用法详解
LCD_DisplayStringLine函数
LCD_DisplayStringLine(Line4,buf);
buf 只能是 unsigned char 类型。
嘀嗒定时器
配置了嘀嗒定时器一定要写中断服务程序,不然 LCD 不会起作用。
高亮 行高亮
if(lcd_light == 0)LCD_SetBackColor(Green); sprintf((char*)Buf," Threshold 1: %02ucm ",Threshold_Buf[0]); LCD_DisplayStringLine(Line3,Buf); memset(Buf,0,20*sizeof(uint8_t)); LCD_SetBackColor(Blue);
一两个字符高亮 单个字节高亮
void highlight(uint8_t *str,uint8_t pos)
{
int i = 0;
for(i = 0; i <= 19; i++)
{
if(i != pos)
LCD_DisplayChar(Line4,(320 - (16 * i)),str[i]);
}
LCD_SetBackColor(Yellow);
LCD_DisplayChar(Line4,(320 - (16 * pos)),str[pos]);
LCD_SetBackColor(Blue);
}
使用
//LCD_Disp sprintf((char*)Buf," Threshold 1: %02ucm ",Threshold_Buf[0]); if(lcd_light == 0)highlight(Buf, 16); else LCD_DisplayStringLine(Line3,Buf); memset(Buf,0,20*sizeof(uint8_t));多个字节高亮
// 自己加参数,想高亮几个就高亮几个。
void highlight(u8* buf, u16 num1, u16 num2, u16 num3)
{
int i;
for (i = 0; i<=19; i++)
{
if ((i != num1) && (i != num2) && (i != num3))
{
LCD_DisplayChar(Line9, (320 - 16 * i), buf[i]);
}
}
LCD_SetBackColor(Red);
LCD_DisplayChar(Line9, (320 - 16 * num1),buf[num1]);
LCD_DisplayChar(Line9, (320 - 16 * num2),buf[num2]);
LCD_DisplayChar(Line9, (320 - 16 * num3),buf[num3]);
LCD_SetBackColor(Black);
}
使用
//LCD_Disp sprintf((char*)Buf," Threshold 1: %02ucm ",Threshold_Buf[0]); if(lcd_light == 0)highlight(Buf, 10, 11, 12); else LCD_DisplayStringLine(Line3,Buf); memset(Buf,0,20*sizeof(uint8_t));
行闪烁
//不过这个只能固定次数和行数。
//原理就是下面LED_Toggle 函数那里。
void LCD_Toggle(u16 time)
{
LCD_DisplayStringLine(Line3, lcd_buf);
Delay_Ms(time/4);
LCD_ClearLine(Line3);
Delay_Ms(time/4);
LCD_DisplayStringLine(Line3, lcd_buf);
Delay_Ms(time/4);
LCD_ClearLine(Line3);
Delay_Ms(time/4);
LCD_DisplayStringLine(Line3, lcd_buf);
}
使用
if(Lcd_Toggle_flag == 1)
{
LCD_Toggle(1000);//闪烁一秒
sprintf((char*)buf," %d ",g_stFSM.u8LedStat);
LCD_DisplayStringLine(Line4,buf);
}
else
{
sprintf((char*)buf," %d ",g_stFSM.u8LedStat);
LCD_DisplayStringLine(Line4,buf);
}
memset(buf,0,20*sizeof(u8));
LED display函数
void Led_Disp(u16 Ledx,FunctionalState NewState){
if(NewState == ENABLE){
GPIO_ResetBits(GPIOC,Ledx << 8);
GPIO_SetBits(GPIOD,GPIO_Pin_2);
GPIO_ResetBits(GPIOD,GPIO_Pin_2);
}
else{
GPIO_SetBits(GPIOC,Ledx << 8);
GPIO_SetBits(GPIOD,GPIO_Pin_2);
GPIO_ResetBits(GPIOD,GPIO_Pin_2);
}
}
void Led_Toggle(u16 Ledx){
GPIO_SetBits(GPIOD,GPIO_Pin_2);
GPIOC->ODR ^= (Ledx << 8);
GPIO_ResetBits(GPIOD,GPIO_Pin_2);
}
注意:为了解决与LCD的冲突问题,需要改变lcd.c里的三个函数——
在这三个函数的开头加上——
u16 pcout = GPIOC->ODR; // 先把GPIOC的ODR寄存器读取出来
结尾加上 ——
GPIOC->ODR = pcout; // 操作完LCD后,再将GPIOC的ODR寄存器原来的值恢复,即不改变LED的状态
Toggle 函数 ——LED 闪烁
void Led_Toggle(u16 Ledx){//led.c
GPIO_SetBits(GPIOD,GPIO_Pin_2);
GPIOC->ODR ^= (Ledx << 8);
GPIO_ResetBits(GPIOD,GPIO_Pin_2);
}
void Led_Toggle_Disp(u16 Ledx,u16 Toggle_Time_ms,u16 num){//main.c
//Toggle_Time_ms总时长,如1000ms即1s
//num 闪烁几次
//亮灭周期为 Toggle_Time_ms/num/2
int i = 0;
for(i = 0;i < 2 * num;i ++){
Delay_Ms(Toggle_Time_ms/num/2);
Led_Toggle(Ledx);
}
}
下次写 LED 闪烁可以直接用这个。
Time_Base 中断服务函数
void TIM4_IRQHandler(void)
{
static u16 buzzer_count=0; //定义为静态变量
if (TIM_GetITStatus(TIM4,TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
buzzer_count++;
if(buzzer_count==500)
{
buzzer_count = 0;
buzzer_flag =1;
}
}
}
注意: 要检测是否进入中断,以及清零标志位。
改变状态值的相关计数值,最好用 static 写在中断函数里面。
Buzzer 使能
void Buzzer_Ctrl(FunctionalState NewState)
{
if (NewState != ENABLE)
{
GPIO_SetBits(GPIOB,GPIO_Pin_4);
}
else
{
GPIO_ResetBits(GPIOB,GPIO_Pin_4);
}
}
注意: 让蜂鸣器响,是清零;让蜂鸣器安静,是置一。
配置GPIO
蜂鸣器的引脚是接到 PB4 的,而默认的情况下,PB4的功能是 JNTRST,所以我们要把他重映射为GPIO引脚,同时需要注意,还要使能 AFIO 功能的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); GPIO_PinRemapConfig(GPIO_Remap_SWJ_NoJTRST,ENABLE);
注意:在初始化 GPIO 的时候一定要细心.
GPIO_PinRemapConfig(GPIO_Remap_SWJ_NoJTRST,ENABLE);
注意, 这里是 ENABLE;
KEY 变量调用
KEY的相关变量可以定义在.c文件中,然后在.h文件中声明。
然后main.c文件中,只要包含.h文件就能直接使用该变量。
注意宏定义运算表达式
调用时一定要加括号,不然运算顺序可能和你想得不一样。
Scan函数
uint8_t Key_Scan(void)
{
uint8_t Key_Val = 0;
if (~GPIO_ReadInputData(GPIOA) & 0x101)
{
Delay_Key(10);
if (!GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_0))
return 1;
else if (!GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_8))
return 2;
}
else if (~GPIO_ReadInputData(GPIOB) & 0x06)
{
Delay_Key(10);
if (!GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_1))
Key_Val = 3;
else if (!GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_2))
Key_Val = 4;
}
return Key_Val;
}
void Delay_Key(u16 ms)
{
int i, j;
for(i = 0; i
注意取反和延时;
注意是 GPIO_Read InputData;
Disp函数
只考虑短按(单击)
void Key_Disp(void)
{
static uint8_t Key_Val = 0;
static uint8_t Key_Down = 0;
static uint8_t Key_Old = 0;
Key_Val = Key_Scan();
Key_Down = Key_Val & (Key_Val ^ Key_Old);
Key_Old = Key_Val;
switch (Key_Down)
{
case 1:lcd_count++;break;
case 2:lcd_count++;break;
case 3:lcd_count++;break;
case 4:lcd_count++;break;
}
}
考虑长短按
void Key_Disp(void)
{
static uint8_t Key_Val = 0;//键值
static uint8_t Key_Down = 0;//当前键值
static uint8_t Key_Old = 0;//上一次键值
static u16 Key1_time = 0;//B1按下时间
static uint8_t key1_flag = 1;//B1长按标志位
Key_Val = Key_Scan();
Key_Down = Key_Val & (Key_Val ^ Key_Old);
Key_Old = Key_Val;
if (Key_Old == 1)//当按下B1
{
Key1_time += 10;//开始计数,这个具体时间不清楚,按道理是每次10ms
if (Key1_time>150 && key1_flag == 1)
{
lcd_count += 10;
Key1_time = 0;
key1_flag = 0;
}
}
if (Key_Down == 0 && Key_Old == 0)//按键松开
{
//这个短按上限时间最好小于长按判断时间,不然长按完,还会触发一次短按,
//同时要注意短按的检测时间,若写0-50,则短按就检测不到了,50-150之间是不会进行操作的
if (Key1_time>0 && Key1_time<100)
{
lcd_count++;
}
//恢复B1相关标志位
Key1_time = 0;
key1_flag = 1;
}
}
E2PROM
参考博客:蓝桥杯嵌入式_STM32 学习_IIC。
掉电不丢失
uint8_t IIC_flag = 0xdd;//标志位的写入数据
uint8_t IIC_Flag = 0;//标志位的读取数据
uint8_t Threshold_Buf[3] = {0};//数据的读取数据
uint8_t IIC_Buf[3] = {30,50,70};//数据的写入数据
//写main.c里
E2PROM_Read_long(&IIC_Flag,0x10,1);
if(IIC_Flag == 0xdd){
E2PROM_Read_long(Threshold_Buf, 0x00, 3);
}
else{
E2PROM_Write_long(&IIC_flag, 0x10, 1);
Delay_Ms(200);
E2PROM_Write_long(IIC_Buf, 0x00, 3);
Delay_Ms(200);
E2PROM_Read_long(Threshold_Buf, 0x00, 3);
}
在别的地址写入一个值作为标志位,每次重启先判断标志位是否是我们写入的值:
如果是,那么就直接读取我们在指定位置写入的数据;如果不是,那我们就写入标志位数据,以及我们自己运行的变量数据。
注意:写入标志位的值要用十六进制,因为一个地址是八位。
写入数据函数
(图片来源:AT24C02.pdf)
void E2PROM_Write_long(unsigned char* pucBuf, unsigned char ucAddr,
unsigned char ucNum)
{
I2CStart(); //起始信号
I2CSendByte(0xa0); //器件地址
I2CWaitAck(); //等待应答
I2CSendByte(ucAddr); //数据地址
I2CWaitAck();//等待应答
while(ucNum--)//写入个数
{
I2CSendByte(*pucBuf++);//写入数据
I2CWaitAck(); //等待应答
}
I2CStop();//停止信号
delay1(500);
}
读取多个数据
(图片来源:AT24C02.pdf)
void E2PROM_Read_long(unsigned char* pucBuf, unsigned char ucAddr,
unsigned char ucNum)
{
I2CStart(); //起始信号
I2CSendByte(0xa0);//器件地址 - 写
I2CWaitAck(); //等待应答
I2CSendByte(ucAddr);//数据地址
I2CWaitAck(); //等待应答
I2CStart();//起始信号
I2CSendByte(0xa1); //器件地址 - 读
I2CWaitAck();//等待应答
while(ucNum--)//读取个数
{
*pucBuf++ = I2CReceiveByte();//读取数据
if(ucNum)//判断是否继续读取
I2CSendAck();//发送有效应答
else
I2CSendNotAck();//发送无效应答
}
I2CStop();//停止信号
}
多类型读写
EEPROM一个地址,存入一个字节的数据,所以当数据范围小于256时,尽量用 uint8_t 类型;若大于256,则针对不同类型的数据,需要分离成单个字节的数据。
但分离字节有点麻烦。
所以我们可以用共用体来写——
//利用共用体读写EEPROM
union eeprom_dat
{
uint8_t t1;
uint16_t t2;
uint32_t t3;
int16_t t4;
float f1;
double f2;
unsigned char str[20];
}eeprom_dat_write,eeprom_dat_read;
//main.c文件中
i2c_init(); //I2C总线初始化
strcpy((char *)eeprom_dat_write.str, "Hello World!!!");
E2PROM_Write_long(eeprom_dat_write.str, 0x10, strlen((char *)eeprom_dat_write.str));
Delay_Ms(200);
E2PROM_Read_long(eeprom_dat_read.str, 0x10, strlen((char *)eeprom_dat_write.str));
Delay_Ms(200);
sprintf((char*)lcd_string,"%s ", eeprom_dat_read.str);
LCD_DisplayStringLine(Line4, (unsigned char *)lcd_string);
eeprom_dat_write.f2 = 3.1415926535;
E2PROM_Write_long(eeprom_dat_write.str, 0x20, sizeof(eeprom_dat_write.f2));
Delay_Ms(200);
E2PROM_Read_long(eeprom_dat_read.str, 0x20, sizeof(eeprom_dat_write.f2));
Delay_Ms(200);
sprintf((char*)lcd_string,"%f ",eeprom_dat_read.f2);
LCD_DisplayStringLine(Line5,(unsigned char *)lcd_string);
注意:字符串用 strlen,其他用 sizeof。
(字符串自带结尾空格,strlen 计数不加空格,sizeof 计数加空格)
注意加上强制转换。
记得共用体是分号。
注意
(1) 自己完成读写EEPROM的函数,I2C驱动比赛提供;
(2) 两次数据写入间隔为:5ms;
(3) 赛题指定初值时:在程序初始化时预先写入数值,再屏蔽掉该函数;
(4) 不要在while(1)频繁读写EEPROM,AT24C02写的寿命为100w次。如果5ms写入一次,500分钟就把EEPROM写坏了。
(参考:电子设计工坊)
注意:
写地址是0xa0,读地址是0xa1;
初始化函数别忘写了!
起始函数该写的地方都加上!!!
一定要记得在main函数里调用初始化函数!!!!
RTC
快速配置
在比赛提供的 V3.5 库的 “Project->STM32F10x_StdPeriph_Examples->RTC->LSI_Calib” 文件夹下,打开 “main.c”:
直接复制粘贴 RTC_Configuration () 部分,去掉最后两句代码,加上我们的计数值设定即可。
RTC_SetCounter () 和 RTC_GetCounter () 可以在库函数文件 “stm32f10x_rtc.h” 里找到。
记得加上中断设置。
u32 Time=23*3600+50*60+10;//写为rtc.c里的全局变量
NVIC_InitTypeDef NVIC_InitStructure;
//使能RTC中断
NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//加上这一句就行,设置初始时间
//RTC_SetCounter(3600 * HH + 60 * MM + SS);
RTC_SetCounter(Time);
注意:u32 Time 要是u32类型的,u16会出错。
在相同目录下,打开 “stm32f10x_it.c” 还可以看到 RTC 中断函数相关代码,修改修改直接使用即可:
void RTC_IRQHandler(void)
{
if (RTC_GetITStatus(RTC_IT_SEC) != RESET)
{
RTC_Time = RTC_GetCounter();
if (RTC_Time == 86400)//到24点清零
{
RTC_Time = 0;//当前秒的值
RTC_SetCounter(1);//下一秒的值
RTC_WaitForLastTask();//这个一定要加,不然会出错
}
RTC_ClearITPendingBit(RTC_FLAG_SEC);
}
}
//main.c 函数调用
#include "rtc.h"
void window()
{
u8 str[20];
Time = RTC_GetCounter();
sprintf(str1,"%0.2d-%0.2d-%0.2d",Time/3600,(Time%3600)/60,(Time%3600)%60);
LCD_DisplayStringLine(Line2, str);
}
int main()
{
...
rtc_Init();
while(1)
{
window();
delay_ms(300);
}
}
ADC
注意的点
1、GPIO管脚是PB0,通道8;
2、分频系数是6:RCC_PCLK2_Div6;
3、sprintf((char*)lcd_buf, "ADC_val : %.2f ", ADC_val/4095.0f*3.3f);
if (ADC_flag)
{
ADC_flag = 0;
ADC_val = ADC_GetConversionValue(ADC1);//这个函数在ADC库函数里
}
PWM
定时器通道引脚分布图表
输出两路频率固定占空比可调的 PWM
快速配置
在比赛提供的 V3.5 库的 “Project->STM32F10x_StdPeriph_Examples->TIM->PWM_Output” 文件夹下,打开 “main.c”:
(注意,不是7PWM_Output)
上面的根据自己的需求修改就行。
设置占空比
void PWM1_ctrl(u16 cycle, u16 ch2_duty, u16 ch3_duty)
{
TIM_SetAutoreload(TIM2, cycle);//周期@1us
TIM_SetCompare2(TIM2,ch2_duty / 100.0f * fq);//占空比
TIM_SetCompare3(TIM2,ch3_duty / 100.0f * fq);//占空比
}
周期与频率换算方法:
设置定时器为72分频,则计数一次为us.
周期与频率互为倒数,将已知频率求倒数再乘上10的六次方us,即一秒,即可得到周期。
使用方法
#include "pwm_1.h"
//main.c里——
PWM1_Init();
PWM1_ctrl(1000, 30, 60);//周期都为1000us,即频率都为1000Hz,占空比一个30%,一个60%
输出两路频率可调占空比可调的 PWM
快速配置
在比赛提供的 V3.5 库的 “Project->STM32F10x_StdPeriph_Examples->TIM->OCToggle” 文件夹下,打开 “main.c”:
基本上比较输出的设置代码都在这里,更改相应的引脚和通道就可以。
再打开同目录下的 “stm32f10x_it.h”:
这里中断服务函数已经有了输出相应频率的代码,
我们对中断服务函数进行扩充增加占空比部分即可——
uint16_t capture = 0;
u8 pa6_state=0,pa7_state=0;
void TIM3_IRQHandler(void)
{
if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1 );
capture = TIM_GetCapture1(TIM3);
if (pa6_state==0)
{
TIM_SetCompare1(TIM3, capture + (u16)CCR1_Val * duty1 );
pa6_state = 1;
}
else
{
TIM_SetCompare1(TIM3, capture + (u16)CCR1_Val * (1 - duty1) );
pa6_state = 0;
}
}
if (TIM_GetITStatus(TIM3, TIM_IT_CC2) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);
capture = TIM_GetCapture2(TIM3);
if (pa7_state==0)
{
TIM_SetCompare2(TIM3, capture + (u16)CCR2_Val * duty2 );
pa7_state = 1;
}
else
{
TIM_SetCompare2(TIM3, capture + (u16)CCR2_Val * (1 - duty2) );
pa7_state = 0;
}
}
}
使用方法
CCR1_Val 是频率;
duty1 是占空比;
pa6_state 是为了产生方波;
所有变量都定义在PWM.c文件里即可,需要改变的值如 CCR1_Val 和 duty1 在头文件用 extern 声明就行,然后用按键什么的改变这些参数即可。
注意:
PA6是CH1通道,写代码要与 CCR1_Val、TIM_OC1Init()、TIM_OC1PreloadConfig()、TIM_IT_CC1、TIM_GetCapture1()、TIM_SetCompare1() 对应,不要CH1用到了CH2的参数或者库函数。
当发现波形不是你想要的答案时,除了参数的数据设置要注意,这个参数与库函数的对应关系也要注意。
串口与TIM2冲突问题
#define PWM_ENABLE 1
#define UART_ENABLE 2
void PWM_UART_Enable(u8 flag)
{
if(flag==PWM_ENABLE)
{
USART_Cmd(USART2, DISABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, DISABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_Cmd(TIM2, ENABLE);
}
if(flag==UART_ENABLE)
{
TIM_Cmd(TIM2, DISABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, DISABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
USART_Cmd(USART2, ENABLE);
}
}
串口
基本知识
管脚: GPIOA2、GPIOA3; PA2 是复用推挽输出,PA3 是浮空输入;
时钟: USART2 是 APB1 总线;记得开复用时钟;
使用
// 发送字符
unsigned char USART_SendChar(USART_TypeDef* USARTx, unsigned char ucChar)
{
USART_SendData(USARTx, ucChar);
while(!USART_GetFlagStatus(USARTx, USART_FLAG_TXE));//USART_FLAG_TC 表示传输完毕;USART_FLAG_TXE表示发送缓冲区空
return ucChar;
}
// 发送字符串
void USART_SendString(USART_TypeDef* USARTx, unsigned char* pucStr)
{
while(*pucStr != ' ')
USART_SendChar(USARTx, *pucStr++);
}
void USART2_IRQHandler(void){
if(USART_GetITStatus(USART2,USART_IT_RXNE)){
RxOver = 1;
Uart_Buf = USART_ReceiveData(USART2);
USART_ClearITPendingBit(USART2,USART_IT_RXNE);
}
}
闲时判断
在串口中断服务函数加上一个时间标志位,当触发串口中断就将其清零。
在定时器里用此标志位计数,当增加到一定数量时,清空串口缓冲数组。
void USART2_IRQHandler(void)
{
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
//使用函数内容
rx_ideltime=0;//一个时间标志位
}
}
//定时器闲时判断
void TIM4_IRQHandler(void)
{
if (TIM_GetITStatus(TIM4,TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
rx_ideltime++;
if(rx_ideltime>=50)
{
rx_ideltime=0;
rx_count=0;
memset(rx_buf,0,15);
}
}
}
善用状态
用一个标志来作为状态切换触发;
例如,输入一个ABCD-1234-A2C4
那么,可以将’-'作为状态切换的契机,每四位数据作为一组状态——
extern uint8_t RxOver;
extern uint8_t Uart_Buf[5];
extern uint8_t Uart_flag1[5];
extern uint8_t Uart_flag2[5];
extern uint8_t Uart_flag3[5];
void USART2_IRQHandler(void){
static uint8_t temp;
static uint8_t t = 0;
static uint8_t k_state = 0;
if(USART_GetITStatus(USART2,USART_IT_RXNE)){
RxOver = 1;
temp = USART_ReceiveData(USART2);
if (temp != '-')
{
switch (k_state)
{
case 0:Uart_Buf[t] = temp;break;
case 1:
{
if(strcmp((char*)Uart_Buf,(char*)Uart_flag1) != 0)
k_state = 0;
}break;
case 2:
{
if(strcmp((char*)Uart_Buf,(char*)Uart_flag2) != 0)
k_state = 0;
}break;
case 3:
{
strcpy(目标数组,(char*)Uart_Buf);
RxOver = 1;
k_state = 0;
}break;
}
if(++t == 4){t = 0;k_state++;}
}
USART_ClearITPendingBit(USART2,USART_IT_RXNE);
}
}
当三个标志都需要存储,可以用一个一维数组来当缓冲区,每次判断正确都存入缓冲区,当全部都符合标准,则将缓冲区内的数据存入目标数组。
若有一次不符合,则清零状态,同时清空缓冲区。
C语言知识点
字符与字符串
字符之间判断可以直接写 ——
if(num_buf[0] != '@')num_buf[0] ++;
字符串直接必须用相对应的函数才行 ——
strcpy 复制
char*strcpy(char *dest, const char *src)
后者拷贝给前者,结尾空格也会拷贝,要注意分配空间。
strcmp 比较
int strcmp(const char *s1, const char *s2);
strcmp比较两个字符串的大小,一个字符一个字符比较,按ASCII码比较
标准规定:
第一个字符串大于第二个字符串,则返回大于0的数字;
第一个字符串等于第二个字符串,则返回 0;
第一个字符串小于第二个字符串,则返回小于0的数字。
注意,比较双方的数组都要留一个空格位才行,不然会出错。
如——
uint8_t str1[4] = {'1', '2', '3'};
uint8_t str2[4] = {'1', '2', '3'};
uint8_t str3[4] = {'4', '2', '3'};
strcmp((char*)str1, (char*)str2);
strcmp((char*)str1, (char*)str3);
strlen 取长度(不包括结尾’ ’)
size_t strlen(const char *str)
注意 size_t 是无符号整形。
strcat 追加拷贝
char *strcat(char *dest,char *src)
将后面的字符串加到前面的字符串的最后。
注意
这些函数的参数都是 char* 类型的,要使用强制转换。
提取字符串
// 从字符串中提取车辆信息
void get_car_info(char *str, pCar_t pcar)
{
int i = 0, count = 0;
//提取车辆类型
for(i = 0; i < 4; i++, count++)
pcar->type[i] = str[count];
//提取车辆ID
count++;
for(i = 0; i < 4; i++, count++)
pcar->id[i] = str[count];
//取车辆录入时间
count++;
for(i = 0; i < 12; i++, count++)
pcar->time[i] = str[count];
}



