基于STM32F103C8T6+uC/OS-II的例程笔记(一)

大家好,在学习STM32和RTOS(uC/OS-II)的过程中遇到了很多问题,这里把本人的学习例程分享给大家,希望对学习STM32的同学有所启示。最后祝您学业有成,工作顺利。

首先感谢我要自学网黄琦龙老师设计的PCB,这块PCB上很多例程都没有给出,在这里补充一下。
先把原理图和layout图截图放一下,给大家了解下PCB上提供的资源。
1.核心版+底板,提供的资源有STM32C8T6和晶振
2.外设资源LCD+LED+Key+Beep+USART接口+SWD调试接口LCD+LED+Key核心版+底板,提供的资源有STM32C8T6和晶振
PCB
PCB的尺寸不大,很适合做智能家居类产品,黄老师说要用这个开发空气净化器不知道做成功了没有。

  • 首先蜂鸣器黄老师的资料里是没给出来的,我测了一下是PA6引脚连的三极管,IO通过三极管的开关作用驱动蜂鸣器。
void BEEP_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
    /*使能GPIOA的RCC时钟*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	/*配置GPIOA引脚,并初始化*/
	GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP;  //推挽输出
	GPIO_InitStructure.GPIO_Pin= GPIO_Pin_6;  //BEEP-->PA.6 端口配置
	GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);  //根据参数初始化GPIOA.6
	BEEP_OFF();
}
//在BEEP.h文件里可以增加位带操作
#define BEEP_ON()  do{GPIO_SetBits(GPIOA, GPIO_Pin_6);}while(0);
#define BEEP_OFF()  do{GPIO_ResetBits(GPIOA, GPIO_Pin_6);}while(0);
#define BEEP_Status PAout(6)// PA6蜂鸣器 位带操作
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C 
#define GPIOA_IDR_Addr    (GPIOA_BASE+8) //0x40010808 

ADC没有给出相应例程,底板侧边的PA1可以作为ADC1的channel1模拟信号输入,这里补充初始化函数和读模拟量函数

void  Adc_Init(void)
{ 	
	GPIO_InitTypeDef GPIO_InitStructure;
	ADC_InitTypeDef ADC_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE );	  //使能ADC1通道时钟
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);   //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
	ADC_DeInit(ADC1);   //恢复默认

	//PA1 作为模拟通道输入引脚                         
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;		//模拟输入引脚
	GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);	

	ADC_DeInit(ADC1);  //复位ADC1 
	ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;  //ADC工作模式:独立模式
	ADC_InitStructure.ADC_ScanConvMode=DISABLE;  //模数转换工作在单通道模式
	ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;  //模数转换工作在单次转换模式
	ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;  //ADC数据右对齐
	ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;  //转换由软件而不是外部触发启动
	ADC_InitStructure.ADC_NbrOfChannel=1;  //顺序进行规则转换的ADC通道的数目
 	ADC_Init(ADC1, &ADC_InitStructure);   //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
	ADC_Cmd(ADC1, ENABLE);  //使能指定的ADC1
	ADC_ResetCalibration(ADC1);	//使能复位校准  
	while(ADC_GetResetCalibrationStatus(ADC1));	//等待复位校准结束
	ADC_StartCalibration(ADC1);	 //开启AD校准
	while(ADC_GetCalibrationStatus(ADC1));	 //等待校准结束
}				
u16 Get_Adc(u8 ch)   //ch代表ADC哪个通道
{
  	//设置指定ADC的规则组通道,一个序列,采样时间	    
	ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5);  //ADC1,ADC通道,采样时间为239.5周期
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);		//使能指定的ADC1的软件转换启动功能	
	while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
	return ADC_GetConversionValue(ADC1);	//返回最近一次ADC1规则组的转换结果
}
 u16 Get_Adc_Average(u8 ch,u8 times)   //ch代表通道,times是采样次数
{
	u32 temp_val=0;
	u8 t;
	for(t=0;t<times;t++)
	{
		temp_val+=Get_Adc(ch);
		delay_ms(5);
	}
	return temp_val/times;
} 	 

DMA增加相应例程,本例程设置用DMA1通道4从全局变量SendBuff往USART1传数据

DMA_InitTypeDef DMA_InitStructure;
u16 DMA1_MEM_LEN;//保存DMA每次数据传送的长度 	    
//DMA1的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_CHx:DMA通道CHx
//cpar:外设地址
//cmar:存储器地址
//cndtr:数据传输量 
void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
 	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);	//使能DMA传输
	
  	DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为缺省值

	DMA1_MEM_LEN=cndtr;
	DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;  //DMA外设基地址
	DMA_InitStructure.DMA_MemoryBaseAddr = cmar;  //DMA内存基地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;  //数据传输方向,从内存读取发送到外设
	DMA_InitStructure.DMA_BufferSize = cndtr;  //DMA通道的DMA缓存的大小
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址寄存器递增
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //数据宽度为8位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  //工作在正常模式
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级 
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
	DMA_Init(DMA_CHx, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器	  	
} 
//开启一次DMA传输
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
{ 
	DMA_Cmd(DMA_CHx, DISABLE );  //关闭USART1 TX DMA1 所指示的通道      
 	DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);//DMA通道的DMA缓存的大小
 	DMA_Cmd(DMA_CHx, ENABLE);  //使能USART1 TX DMA1 所指示的通道 
}	  

键值读取函数原例程用的是外部中断,太浪费资源,改成扫码键值的架构

#define KEY0_State  GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) //读取按键0
#define KEY1_State  GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12) //读取按键1
#define KEY2_State  GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13) //读取按键2
#define KEY3_State  GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) //读取按键3
#define KEY0_PRES 1	//KEY1按下
#define KEY1_PRES 2	//KEY1按下
#define KEY2_PRES 3	//KEY2按下
#define KEY3_PRES 4	//KEY3按下
#define NoKEY_PRES 0	//无按键按下
#define NoPress 1
#define IsPress 0
u8 KEY_Scan(u8 mode)
{
	static u8 key_up=NoPress;  	//按键按松开标志
	if(mode)key_up=NoPress;  	//支持连按
	if(key_up && (KEY0_State==IsPress||KEY1_State==IsPress||KEY2_State==IsPress||KEY3_State==IsPress))
	{
		delay_ms(10); //延迟10ms
		key_up=IsPress;
		if(KEY0_State==IsPress)return KEY0_PRES;
		else if(KEY1_State==IsPress)return KEY1_PRES;
		else if(KEY2_State==IsPress)return KEY2_PRES;
		else if(KEY3_State==IsPress)return KEY3_PRES;
	}
	else if(KEY0_State==NoPress&&KEY1_State==NoPress&&KEY2_State==NoPress&&KEY3_State==NoPress)
		key_up=NoPress;
	return NoKEY_PRES;
}

原例程RTC配置写的不够好,参考正点原子的修改,计算日历的函数太长这里就不给出,感兴趣可去看正点原子的PDF

u8 RTC_Init(void)
{
	//检查是不是第一次配置时钟
	u8 temp=0;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	//使能PWR和BKP外设时钟   
	PWR_BackupAccessCmd(ENABLE);	//使能后备寄存器访问  
	if (BKP_ReadBackupRegister(BKP_DR1) != 0x5050)		//从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎
		{	 			
		BKP_DeInit();	//复位备份区域 	
		RCC_LSEConfig(RCC_LSE_ON);	//设置外部低速晶振(LSE),使用外设低速晶振
		while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&temp<250)	//检查指定的RCC标志位设置与否,等待低速晶振就绪
			{
			temp++;   //判断外部晶振是否Ready 2.5s
			delay_ms(10);
			}
		if(temp>=250)return 1;//初始化时钟失败,晶振有问题	    
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);		//设置RTC时钟(RTCCLK),选择LSE作为RTC时钟    
		RCC_RTCCLKCmd(ENABLE);	//使能RTC时钟  
		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
		RTC_WaitForSynchro();		//等待RTC寄存器同步  
		RTC_ITConfig(RTC_IT_SEC, ENABLE);		//使能RTC秒中断
		
		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成

		RTC_SetPrescaler(32767); //设置RTC预分频的值
		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
		RTC_EnterConfigMode();/// 允许配置	
		RTC_Set(2019,1,23,0,5,55);  //设置时间	
		RTC_ExitConfigMode(); //退出配置模式  
		BKP_WriteBackupRegister(BKP_DR1, 0X5050);	//向指定的后备寄存器中写入用户程序数据
		}
	else//系统继续计时
		{
		RTC_WaitForSynchro();	//等待最近一次对RTC寄存器的写操作完成
		RTC_ITConfig(RTC_IT_SEC|RTC_IT_ALR, ENABLE);	//使能RTC秒和闹钟中断
		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
		}
	RTC_NVIC_Config();//RCT中断分组设置		    				     
	RTC_Get();//更新时间	
	return 0; //ok
}	
void RTC_IRQHandler(void)
{		 
	if (RTC_GetITStatus(RTC_IT_SEC) != RESET)//秒钟中断
	{							
		RTC_Get();//更新时间   
 	}
	if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)//闹钟中断
	{
		RTC_ClearITPendingBit(RTC_IT_ALR);		//清闹钟中断	  	
	  	RTC_Get();				//更新时间   
  		
		RTC_Alarm_Flag=ENABLE;
  	} 				  								 
	RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW);		//清秒中断
	RTC_WaitForLastTask();	  	    						 	   	 
}	 			

延时函数也做修改,原例程用Systick中断实现,比较浪费资源,这里可以参考@newsteinguo写的三种延时函数

https://blog.csdn.net/qqGHJ/article/details/81429100

下一讲说明如何用UCOSII去多线程调用这些功能

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值