牛人的STM32学习笔记(寄存器版本)

更新时间:2024-01-17 13:53:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

一、GPIO口的配置

STM32的DGPIO口最多可以有7组(GPIOa~GPIOg),而每一组GPIO口均有16个双向IO组成。并且没个IO口均可配置成8种模式(4种输入模式,4种输出模式)。不管配置哪个IO口也不论将其配置成哪种模式(但是配置成哪种模式要看具体应用,参考《中文参考手册》第105页)都可以按以下步骤来进行配置: (1)使能PORTx(x=A~G)时钟

这里就得操作寄存器RCC_APB2ENR(32为寄存器)了

15 ADC3EN 7 IOPFEN 14 13 USART1EN TIM8EN 6 5 IOPEEN IOPDEN 12 SPI1EN 4 IOPCEN 11 TIM1EN 3 IOPBEN 10 ADC2EN 2 IOPAEN 9 ADC1EN 1 保留 8 IOPGEN 0 AFIOEN RCC_APB2ENR的0~15位(06~32位保留)

第2~8分别是使能GPIOA~GPIOG时钟的,只要将其置“1”即可,如

RCC_APB2ENR|=1<<2;就是使能GPIOA的时钟;其余IO口的始终使能一次类推。 (2)对相应的IO模式进行配置,低8位配置GPIOx_CRL;高8位配置GPIOx_CRH 31 30 CNF7[1:0] 23 22 CNF5[1:0] 15 14 CNF3[1:0] 7 6 CNF1[1:0] 29 28 MODE7[1:0] 21 20 MODE5[1:0] 13 12 MODE3[1:0] 5 4 MODE1[1:0] 27 26 CNF6[1:0] 19 18 CNF4[1:0] 11 10 CNF2[1:0] 3 2 CNF0[1:0] 25 24 MODE6[1:0] 17 16 MODE4[1:0] 9 8 MODE2[1:0] 1 0 MODE0[1:0] GPIOx_CRL(x=A~G(端口配置低寄存器x=A?E)

该寄存器用于配置GPIOx的低8位,具体8种模式的配置见《中文参考手册》例如: GPIOD->CRL&=0XFFFFF0FF;GPIOD->CRL|=0X00000300;/PD.2推挽输出;其余IO口的低8位以此类推。 31 30 CNF15[1:0] 23 22 CNF13[1:0] 15 14 CNF11[1:0] 7 6 CNF9[1:0] 29 28 MODE15[1:0] 21 20 MODE13[1:0] 13 12 MODE11[1:0] 5 4 MODE9[1:0] 27 26 CNF14[1:0] 19 18 CNF12[1:0] 11 10 CNF10[1:0] 3 2 CNF8[1:0] 25 24 MODE14[1:0] 17 16 MODE12[1:0] 9 8 MODE10[1:0] 1 0 MODE8[1:0] GPIOx_CRH(端口配置高寄存器x=A?E)

该寄存器用于配置GPIOx的高8位,具体8种模式的配置见《中文参考手册》例如: GPIOA->CRH&=0XFFFFFFF0;;GPIOA->CRH|=0X00000003;//PA8 推挽输出;其余IO口的高8位以此类推。

(3)端口的输入和输出电平配置 15 IDR15 7 IDR7 14 IDR14 6 IDR6 13 IDR13 5 IDR5 12 IDR12 4 IDR4 11 IDR11 3 IDR3 10 IDR10 2 IDR2 9 IDR9 1 IDR1 8 IDR8 0 IDR0

GPIOx_IDR(端口输入数据寄存器x=A?E) 该寄存器配置IO口的0~15位的输入数据,以16位读出。 15 ODR15 7 ODR7 14 ODR14 6 ODR6 13 ODR13 5 ODR5 12 ODR12 4 ODR4 11 ODR11 3 ODR3 10 ODR10 2 ODR2 9 ODR9 1 ODR1 8 ODR8 0 ODR0 GPIOx_ODR(端口输出数据寄存器x=A?E)

该寄存器配置IO口的0~15位的输入初始状态,例如:GPIOA->ODR|=1<<13;//PA13上拉输入

一般GPIO口配置可仿以下两个程序: void KEY_Init(void) {

RCC->APB2ENR|=1<<2; //使能PORTA时钟 GPIOA->CRL&=0XFFFFFFF0;//PA0设置成输入 GPIOA->CRL|=0X00000008;

GPIOA->CRH&=0X0F0FFFFF;//PA13,15设置成输入 GPIOA->CRH|=0X80800000; GPIOA->ODR|=1<<13; //PA13上拉,PA0默认下拉 GPIOA->ODR|=1<<15; //PA15上拉 }

void LED_Init(void) {

RCC->APB2ENR|=1<<2; //使能PORTA时钟 RCC->APB2ENR|=1<<5; //使能PORTD时钟 GPIOA->CRH&=0XFFFFFFF0;

GPIOA->CRH|=0X00000003;//PA8 推挽输出 GPIOA->ODR|=1<<8; //PA8 输出高 GPIOD->CRL&=0XFFFFF0FF;

GPIOD->CRL|=0X00000300;//PD.2推挽输出 GPIOD->ODR|=1<<2; //PD.2输出高 }

二、串口通信

STM32最多可以提供5路串口,其串口配置主要有以下步骤: (1)串口时钟使能

15 ADC3EN 7 IOPFEN 14 13 USART1EN TIM8EN 6 5 IOPEEN IOPDEN 12 SPI1EN 4 IOPCEN 11 TIM1EN 3 IOPBEN 10 ADC2EN 2 IOPAEN 9 ADC1EN 1 保留 8 IOPGEN 0 AFIOEN RCC_APB2ENR的0~15位(16~32位保留) 在寄存器RCC_APB2ENR里的第14位就是对串口1的时钟使能即: RCC_APB2ENR|=1<<14; //使能串口1时钟 , 那么除串口1的时钟使能在RCC_APB2ENR外其余的时钟使能位在寄存器RCC_APB1ENR里,看下表:

31 保留 23 USBEN 15 SPI3EN 7 30 22 I2C2EN 14 SPI2EN 6 保留 29 DACEN 21 I2C1EN 13 28 PWREN 20 UART5EN 12 保留 5 4 TIM7EN TIM6EN 27 26 25 24 BKPEN 保留 CANEN 保留 19 18 17 16 UART4EN UART3EN UART2EN 保留 11 10 9 8 WWDGEN 保留 3 2 1 0 TIM5EN TIM4EN TIM3EN TIM2EN

RCC_APB1ENR

例如:RCC_APB1ENR|=1<<17; //使能串口2时钟 ,其余串口时钟使能以此类推。 (2)串口复位即结束复位

STM32在使用串口时不管当前该串口出于什么状态都先要将其复位,而复位后要将其结束复位。串口复位主要在寄存器RCC_APB1RSTR(串口1的复位)和寄存器 RCC_APB2RSTR(其余串口复位)这两个寄存器如下表 15 ADC3RST 7 IOPFRST 14 13 USART1RST TIM8RST 6 5 IOPERST IOPDRST 12 SPI1RST 4 IOPCRST 11 TIM1RST 3 IOPBRST 10 ADC2RST 2 IOPARST 9 ADC1RST 1 保留 8 IOPGRST 0 AFIORST

RCC_APB2RSTR(APB2外设复位寄存器)

寄存器RCC_APB2RSTR的第14位是进行串口1的复位如:RCC_APB1RSTR|=1<<14; //将串口1复位 ,然后结束复位RCC_APB1RSTR|=~(1<<14); //结束串口1复位 其余串口复位在寄存器 RCC_APB1RSTR里如下表:

29 28 27 26 25 24 保留 DACRST PWRRST BKPRST 保留 CANRST 保留 23 22 21 20 19 18 17 16 USBRST I2C2RST I2C1RST UART5RST UART4RST UART3RST UART2RST 保留 15 14 13 12 11 10 9 8 SPI3RST SPI2RST 保留 WWDGRST 保留 7 6 5 4 3 2 1 0 保留 TIM7RST TIM6RST TIM5RST TIM4RST TIM3RST TIM2TST RCC_APB1RSTR(APB1外设复位寄存器)

如: RCC_APB1RSTR|=1<<17; //复位串口2 RCC_APB1RSTR|=~(1<<17); // 结束串口2复位 ,其余串口复位操作以此类推。

(3)串口波特率设置 31 30 15 7 14 12 11 10 9 8 DIV_Mantissa[11:4] 6 5 4 3 2 1 0 DIV_Mantissa[3:0] DIV_Fraction[3:0] 13 USART_BRR(波特比率寄存器)

该寄存器的15-4位:DIV_Mantissa[11:0]USARTDIV的整数部分,这12位定义了USART分频器除法因子(USARTDIV)的整数部分;3-0位:DIV_Fraction[3:0]USARTDIV的小数部分,这4位定义了USART分频器除法因子(USARTDIV)的小数部分。关于波特率设置在函数 void uart_init(u32 pclk2,u32 bound)里已经设置好,并且封装在usart.c文件里面可以直接调用。 (4)串口控制

STM32的每个串口都有3个控制寄存器(USART_CR1~3)控制,例如USART_CR1如下: 15 保留 7 TXEIE 14 UE 6 TCIE 13 M 5 RXNEIE 12 4 IDLEIE 11 WAKE 3 TE 10 PCE 2 RE 9 PS 1 RWU 8 PEIE 0 SBK USART_CR1(控制寄存器1) 该寄存器32~14位保留,第13位使能串口(任何串口在应用的时候都必需将其置“1”)第12位设置字长,当这位为“0”的时候设置串口位8个字长外加n个停止位,这n个停止位在寄存器USART_CR2中第[13:12]位来决定。PCE为奇偶校验使能位设置为“0”则禁止校验,否则使能校验。PS是交验选择位,设置为“0”则为偶校验,否则为奇校验。PEIE:PE(校验错误)中断使能,该位由软件设置或清除,定义:0(禁止产生中断),1(当USART_SR中的PE为’1’时,产生USART中断)。TXEIE发送缓冲区空中断使能,(手动),定义:0(禁止产生中断),1(当USART_SR中的TXE为’1’时,产生USART中断)。TCIE发送完成中断使能,(手动),定义:0(禁止产生中断)1(当USART_SR中的TC为’1’时,产生USART中断)。RXNEIE接收缓冲区非空中断使能,(手动),定义:0(禁止产生中断),1(当USART_SR中的ORE或者RXNE为’1’时,产生USART中断)。TE为发送使能位,设置为“1”将开启串口的发送功能。RE为接收使能位,用法同TE。

15 保留 7 保留 14 LINEN 6 LBDIE 13 12 STOP[1:0] 5 4 LBDL 保留 11 CLKEN 3 10 CPOL 2 9 CPHA 1 ADD[3:0] 8 LBCL 0

USART_CR2(控制寄存器2)

如:USART1->CR1|=0X200C; //1位停止,无校验位. 0X200C=0010 0000 0000 1100B 设置成使能串口8个字长1个停止位(USART_CR2中[13:12]默认为“0”)禁止校验,禁止校验所有中断,使能发送和接收。 (5)数据发送和接收 15 7 14 6 13 5 12 11 保留 4 3 DR[7:0] 10 2 9 1 8 DR[8] 0 USART_DR(数据寄存器) 发送数据缓存寄存器(向它写数据它会自动发送数据),当接收到数据时则存放接收的数据 (6)串口控制 15 7 TXE TC 14 6 13 保留 5 RXNE 12 4 LDLE 11 3 ORE NE 10 CTS 2 FE 9 LBD 1 PE 8 0 USART_SR 参考程序:

void uart_init(u32 pclk2,u32 bound) { float temp; u16 mantissa; u16 fraction; temp=(float)(pclk2*1000000)/(bound*16);//得到USARTDIV mantissa=temp; //得到整数部分 fraction=(temp-mantissa)*16; //得到小数部分 mantissa<<=4; mantissa+=fraction; RCC->APB2ENR|=1<<2; //使能PORTA口时钟 RCC->APB2ENR|=1<<14; //使能串口时钟 GPIOA->CRH&=0XFFFFF00F; GPIOA->CRH|=0X000008B0;//IO状态设置 RCC->APB2RSTR|=1<<14; //复位串口1 RCC->APB2RSTR&=~(1<<14);//停止复位 //波特率设置 USART1->BRR=mantissa; // 波特率设置

USART1->CR1|=0X200C; //1位停止,无校验位. #ifdef EN_USART1_RX //如果使能了接收 //使能接收中断 USART1->CR1|=1<<8; //PE中断使能 USART1->CR1|=1<<5; //接收缓冲区非空中断使能 MY_NVIC_Init(3,3,USART1_IRQChannel,2);//组2,最低优先级 #endif }

void USART1_IRQHandler(void) { u8 res; if(USART1->SR&(1<<5))//接收到数据 { res=USART1->DR; if((USART_RX_STA&0x80)==0)//接收未完成 { if(USART_RX_STA&0x40)//接收到了0x0d { if(res!=0x0a)USART_RX_STA=0;//接收错误,重新开始 else USART_RX_STA|=0x80; //接收完成了 }else //还没收到0X0D { if(res==0x0d)USART_RX_STA|=0x40; else { USART_RX_BUF[USART_RX_STA&0X3F]=res; USART_RX_STA++; if(USART_RX_STA>63)USART_RX_STA=0;//接收数据错误,重新开始接收 } } } } }

以上两个函数已经封装在usart.c中可直接调用

三、外部中断

STM32的每一个IO口都可以作为中断输入,要想把IO口作为中断输入则必须将IO口设置成上拉/下拉输入或浮空输入(设置成浮空输入时要接上拉或下拉电阻否则可能导致中断不断触发)。下面总结一下设置IO口为外部中断时的步骤:

(1)将IO口设置成输入模式 这个在第一章总结过,这里不多说。

(2)开启IO口复用时钟,设置IO口与中断线的映射关系

这一步在函数void Ex_NVIC_Config(u8 GPIOx,u8 BITx,u8 TRIM) 中已经封装好可直接调用这里说一下IO口的复用时钟使能:

15 ADC3EN 7 IOPFEN 14 13 USART1EN TIM8EN 6 5 IOPEEN IOPDEN 12 SPI1EN 4 IOPCEN 11 TIM1EN 3 IOPBEN 10 ADC2EN 2 IOPAEN 9 ADC1EN 1 保留 8 IOPGEN 0 AFIOEN

RCC_APB2ENR RCC_APB2ENR|=0X01; //使能IO口复用时钟

(3)开启与该IO口相对应的线上中断/事件,并设置触发条件

这一步封装在函数void Ex_NVIC_Config(u8 GPIOx,u8 BITx,u8 TRIM) 中,可以直接调用,例如:Ex_NVIC_Config(GPIO_A,0,RTIR); //设置PA(0)上升沿触发

Ex_NVIC_Config(GPIO_A,13,FTIR);//设置PA(13)下降沿触发 (4)配置中断分组(NVIC)并使能中断

这一步封装在函数void MY_NVIC_Init(u8 NVIC_PreemptionPriority,u8 NVIC_SubPriority,u8 NVIC_Channel,u8 NVIC_Group) 里面可以直接调用,例如:

MY_NVIC_Init(2,2,EXTI0_IRQChannel,2); //抢占2,子优先级2,组2 这里值得注意的是EXTI0、EXTI1、EXTI2、EXTI3、EXTI4为Line0~Line4 EXTI15_10为Line15~Line10 EXTI9_5为Line9~Line5 (5)编写中断服务函数

例如: void EXTI15_10_IRQHandler(void) {

delay_ms(10); //消抖 if(KEY0==0) //按键0 {

LED0=!LED0; }

else if(KEY1==0)//按键1 {

LED1=!LED1; }

EXTI->PR=1<<13; //清除LINE13上的中断标志位 EXTI->PR=1<<15; //清除LINE15上的中断标志位 }

四、定时计数器中断

STM32共有8个定时计数器,其中TIME1和TIME8是高级定时器,TIME2~TIME5是通用定时器,TIME6和TIME7是基本定时器,这里以TIME3为例先总结以下定时计数器的基本用法。

定时计数器TIME3中断的配置步骤: (1)TIME3时钟使能 31 保留 23 USBEN 15 SPI3EN 7 30 22 I2C2EN 14 SPI2EN 6 保留 29 DACEN 21 I2C1EN 13 28 PWREN 20 UART5EN 12 保留 5 4 TIM7EN TIM6EN 27 26 25 24 BKPEN 保留 CANEN 保留 19 18 17 16 UART4EN UART3EN UART2EN 保留 11 10 9 8 WWDGEN 保留 3 2 1 0 TIM5EN TIM4EN TIM3EN TIM2EN RCC_APB1ENR

例如: RCC->APB1ENR|=1<<1; //使能TIME3的时钟; RCC->APB1ENR|=1<<2; //使能TIME4的时钟; RCC->APB1ENR|=1<<3; //使能TIME5的时钟; RCC->APB1ENR|=0X01;//使能TIME2的时钟使能;(RCC->APB1ENR|=1<<0;) 其余的一次类推。

15 ADC3EN 7 IOPFEN 14 13 USART1EN TIM8EN 6 5 IOPEEN IOPDEN 12 SPI1EN 4 IOPCEN 11 TIM1EN 3 IOPBEN 10 ADC2EN 2 IOPAEN 9 ADC1EN 1 保留 8 IOPGEN 0 AFIOEN RCC_APB2ENR (2)设置TIM3_ARR和TIM3_PSC的值 这两个位分别设置自动重装值及分频系数 15 7 14 6 13 5 12 11 ARR[15:8] 4 3 ARR[7:0] 10 2 9 1 8 0 TIMx_ARR(TIM6和TIM7自动重装载寄存器) 15 7 14 6 13 5 12 11 PSC[15:8] 4 3 PSC[7:0] 10 2 9 1 8 0

TIMx_PSC(TIM6和TIM7预分频器)

(3)设置TIM3_DIER允许更新中断

15 14 保留 TDE 7 6 保留 TIE 13 12 保留 CC4DE 5 4 保留 CC4IE 11 CC3DE 3 CC3IE 10 CC2DE 2 CC2IE 9 CC1DE 1 CC1IE 8 UDE 0 UIE TIMx_DIER 例如:TIM3->DIER|=1<<0; //允许更新中断 TIM3->DIER|=1<<6; //允许触发中断 (4)允许TIM3工作(使能TIM3)

15 7 ARPE 14 6 CMS[1:0] 13 12 保留 5 4 DIR 11 3 OPM 10 2 URS 9 8 CKD[1:0] 1 0 UDIS CEN

TIMx_CR1 例如:TIM3->CR1|=0x01; //使能定时器3 15 7 BIF 14 保留 6 TIF 13 5 COMIF 12 CC4OF 4 CC4IF 11 CC3OF 3 CC3IF 10 CC2OF 2 CC2IF 9 CC1OF 1 CC1IF 8 保留 0 UIF

TIMx_SR

该寄存器用来标记当前预定时器相关的各种事件/中断是否发生。 UIF:更新中断标记,当产生更新事件时该位由硬件置’1’。 例如:if(TIM3->SR&0X0001)//溢出中断 (5)TIM3中断分组设置

例如:MY_NVIC_Init(1,3,TIM3_IRQChannel,2);//抢占1,子优先级3,组2 直接调用该函数就行 (6)编写中断服务程序

例如:void TIM3_IRQHandler(void) { if(TIM3->SR&0X0001)//溢出中断 {

LED1=!LED1; }

TIM3->SR&=~(1<<0);//清除中断标志位 }

参考程序: void Timerx_Init(u16 arr,u16 psc) { RCC->APB1ENR|=1<<1;//TIM3时钟使能 TIM3->ARR=arr; //设定计数器自动重装值//刚好1ms

TIM3->PSC=psc; //预分频器7200,得到10Khz的计数时钟 //这两个东东要同时设置才可以使用中断 TIM3->DIER|=1<<0; //允许更新中断 TIM3->DIER|=1<<6; //允许触发中断 TIM3->CR1|=0x01; //使能定时器3 MY_NVIC_Init(1,3,TIM3_IRQChannel,2);//抢占1,子优先级3,组2 }

TIME2的CH2模式2的PWM输出:

脉冲宽度调制模式可以产生一个由寄存器TIMx_ARR确定频率和由寄存器TIMx_CCRx确定其占空比的PWM波形;

使能定时器:RCC->APB1ENR|=1<<0;//使能TIME2的时钟

选择通道(共4个通道):在TIMx_CCMRx寄存器中的OCxM位写入’110’(PWM模式1)或’111’(PWM模式2); 15 OC2CE 7 OC1CE 14 13 OC2M[2:0] IC2F[3:0] 6 5 OC1M[2:0] IC1F[3:0] 12 4 11 10 OC2PE OC2FE IC2PSC[1:0] 3 2 OC1PE OC1FE IC1PSC[1:0] 9 8 CC2S[1:0] 1 0 CC1S[1:0] TIMx_CCMR1(捕获/比较模式寄存器1) TIM2->CCMR1|=7<<12; //选择PWM模式2

使能预装载寄存器:必须设置TIMx_CCMRx寄存器OCxPE位以使能相应的预装载寄存器; 15 OC2CE 7 OC1CE 14 13 OC2M[2:0] IC2F[3:0] 6 5 OC1M[2:0] IC1F[3:0] 12 4 11 10 OC2PE OC2FE IC2PSC[1:0] 3 2 OC1PE OC1FE IC1PSC[1:0] 9 8 CC2S[1:0] 1 0 CC1S[1:0]

TIMx_CCMR1(捕获/比较模式寄存器1) TIM2->CCMR1|=1<<11; //使能预装载寄存器

使能自动重装载的预装载寄存器:最后还要设置TIMx_CR1寄存器的ARPE位,(在向上计数或中心对称模式中)使能自动重装载的预装载寄存器;

15 7 ARPE 14 6 CMS[1:0] 13 12 保留 5 4 DIR 11 3 OPM 10 2 URS 9 8 CKD[1:0] 1 0 UDIS CEN

TIMx_CR1

TIMx_CR1|=1<<7; //使能自动装载的预分频寄存器

设置极性:OCx的极性可以通过软件在TIMx_CCER寄存器中的CCxP位设置,它可以设置为高电平有效或低电平有效。TIMx_CCER寄存器中的CCxE位控制OCx输出使能;

SPI控制寄存器1(SPI_CR1) SPI1->CR1|=1<<2; //SPI主机 SPI1->CR1|=0<<11;//8bit数据格式 4、设置时钟极性和相位极性: 15 14 BIDIMODE BIDIOE 7 6 LSBFIRST SPE 13 CRCEN 5 12 11 CRCNEXT DFF 4 3 BR[2:0] 10 RXONLY 2 MSTR 9 SSM 1 CPOL SSI 8 0 CPHA SPI控制寄存器1(SPI_CR1)

SPI1->CR1|=1<<1; //空闲模式下SCK为1 CPOL=1 SPI1->CR1|=1<<0; //数据采样从第二个时间边沿开始,CPHA=1

5、传输速率和LSBFIRST帧格式设置: 15 14 BIDIMODE BIDIOE 7 6 LSBFIRST SPE 13 CRCEN 5 12 11 CRCNEXT DFF 4 3 BR[2:0] 10 RXONLY 2 MSTR 9 SSM 1 CPOL SSI 8 0 CPHA

SPI控制寄存器1(SPI_CR1)

SPI1->CR1|=7<<3; //Fsck=Fcpu/256 SPI1->CR1|=0<<7; //MSBfirst 6、使能SPI设备: 15 14 BIDIMODE BIDIOE 7 6 LSBFIRST SPE 13 CRCEN 5 12 11 CRCNEXT DFF 4 3 BR[2:0] 10 RXONLY 2 MSTR 9 SSM 1 CPOL SSI 8 0 CPHA

SPI控制寄存器1(SPI_CR1)

SPI1->CR1|=1<<6; //SPI设备使能

参考程序:

void SPIx_Init(void) { RCC->APB2ENR|=1<<2; //PORTA时钟使能 RCC->APB2ENR|=1<<12; //SPI1时钟使能 //这里只针对SPI口初始化 GPIOA->CRL&=0X000FFFFF; GPIOA->CRL|=0XBBB00000;//PA5.6.7复用 GPIOA->ODR|=0X7<<5; //PA5.6.7上拉 SPI1->CR1|=0<<10;//全双工模式 SPI1->CR1|=1<<9; //软件nss管理 SPI1->CR1|=1<<8; SPI1->CR1|=1<<2; //SPI主机 SPI1->CR1|=0<<11;//8bit数据格式 SPI1->CR1|=1<<1; //空闲模式下SCK为1 CPOL=1 SPI1->CR1|=1<<0; //数据采样从第二个时间边沿开始,CPHA=1 SPI1->CR1|=7<<3; //Fsck=Fcpu/256 SPI1->CR1|=0<<7; //MSBfirst SPI1->CR1|=1<<6; //SPI设备使能 SPIx_ReadWriteByte(0xff);//启动传输 }

SPI读写数据程序(注意全双工同时读写): //SPIx读写一个字节

//TxData:要写入的字节 //返回值:读到的字节

u8 SPIx_ReadWriteByte(u8 Txdata) {

u8 temp=0; while((SPI->SR&1<<1)==0)

//SPI->SR&1<<1(发送缓冲区为空);(SPI->SR&1<<1)==0(发送缓冲区为空不成立即 //发送缓冲区不为空)则一直判断等待发送缓冲区为空(跳出while循环)才发送数据 { temp++; if(temp>300) return 0; //若经判断发送缓冲区不为空则返回值“0” } SPI->DR=Txdata; //若发送缓冲区为空则发送数据 temp=0;

while((SPI->SR&1<<0)==0)

//(SPI->SR&1<<0)==0(判断接收缓冲区是否为空)若为空则接收数据 { temp++; if(temp>300) return 0; } return SPI->DR; }

六、DS18B20温度传感器

用单片机控制温度传感器DS18B20进行温度采集主要进行一下步骤: (1)复位:首先我们必须对DS18B20芯片进行复位,复位就是由控制器(单片机)给DS18B20单总线至少480uS的低电平信号。当18B20接到此复位信号后则会在15~60uS后回发一个芯片的存在脉冲。

void DS18B20_Rst(void) { DS18B20_IO_OUT(); //讲PA0设置成输出状态 DS18B20_DQ_OUT=0; //拉低DQ(PA0接DQ) delay_us(750); //拉低750us

DS18B20_DQ_OUT=1; / /DQ=1 释放数据线讲DQ交给1820 delay_us(15); / /15US }

(2)存在脉冲:在复位电平结束之后,控制器应该将数据单总线拉高,以便于在15~60uS后接收存在脉冲,存在脉冲为一个60~240uS的低电平信号。至此,通信双方已经达成了基本的协议,接下来将会是控制器与18B20间的数据通信。如果复位低电平的时间不足或是单总 线的电路断路都不会接到存在脉冲,在设计时要注意意外情况的处理。 u8 DS18B20_Check(void) //j检测ds18b20是否存在 {

u8 retry=0;

DS18B20_IO_IN();//SET PA0 INPUT while (DS18B20_DQ_IN&&retry<200) { retry++; delay_us(1); }; if(retry>=200)return 1; else retry=0;

while (!DS18B20_DQ_IN&&retry<240) { retry++; delay_us(1); }; if(retry>=240)return 1; return 0; }

(3)控制器发送ROM指令:双方打完了招呼之后最要将进行交流了,ROM指令共有5条,每一个工作周期只能发一条,ROM指令分别是读ROM数据、指定匹配芯片、跳跃ROM、芯片搜索、报警芯片搜索。ROM指令为8位长度,功能是对片内的64位光刻ROM进行操作。其主要目的是为了分辨一条总线上挂接的多个器件并作处理。诚然,单总线上可以同时挂接多个器件,并通过每个器件上所独有的ID号来区别,一般只挂接单个18B20芯片时可以跳过ROM指令(注意:此处指的跳过ROM指令并非不发送ROM指令,而是用特有的一条“跳过指令”)。控制器发送存

储器操作指令:在ROM指令发送给18B20之后,紧接着(不间断)就是发送存储器操作指令了。操作指令同样为8位,共6条,存储器操作指令分别是写RAM数据、读RAM数据、将RAM数据复制到EEPROM、温度转换、将EEPROM中的报警值复制到RAM、工作方式切换。存储器操作指令的功能是命令18B20作什么样的工作,是芯片控制的关键。 void DS18B20_Start(void)// ds1820 start convert { DS18B20_Rst(); DS18B20_Check();

DS18B20_Write_Byte(0xcc); // skip rom 跳过ROM DS18B20_Write_Byte(0x44); // convert 温度转换命令 }

(4)执行或数据读写:一个存储器操作指令结束后则将进行指令执行或数据的读写,这个操作要视存储器操作指令而定。如执行温度转换指令则控制器(单片机)必须等待18B20执行其指令,一般转换时间为500uS。如执行数据读写指令则需要严格遵循18B20的读写时序来操作。数据的读写方法将有下文有详细介绍。若要读出当前的温度数据我们需要执行两次工作周期,第一个周期为复位、跳过ROM指令、执行温度转换存储器操作指令、等待500uS温度转换时间。紧接着执行第二个周期为复位、跳过ROM指令、执行读RAM的存储器操作指令、读数据(最多为9个字节,中途可停止,只读简单温度值则读前2个字节即可)。其它的操作流程也大同小异,在此不多介绍。

七、内部ADC转换

STM32最多会有三个内部ADC转换器,每个转换器均有18个通道其中16个外部通道和2个内部通道(信号源)。每个ADC转换器都有单次、连续、扫描或间断四种工作模式,ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。 1、ADC开关控制

通过设置ADC_CR2寄存器的ADON位可给ADC上电。

(1)当第一次设置ADON位时,它将ADC从断电状态下唤醒;

(2)ADC上电延迟一段时间后(tSTAB),再次设置ADON位时开始进行转换; (3)通过清除ADON位可以停止转换,并将ADC置于断电模式。 2、ADC时钟控制

由时钟控制器提供的ADCCLK时钟和PCLK2(APB2时钟)同步,RCC控制器为ADC时钟提供一个专用的可编程预分频器。

3、通道选择

有16个多路通道。可以把转换组织成两组:规则组和注入组。在任意多个通道上以任意顺序进行的一系列转换构成成组转换。例如,可以如下顺序完成转换:通道3、通道8、通道2、通道2、通道0、通道2、通道2、通道15。

(1)规则组由多达16个转换组成。规则通道和它们的转换顺序在ADC_SQRx寄存器中选择。规则组中转换的总数应写入ADC_SQR1寄存器的L[3:0]位中。

(2)注入组由多达4个转换组成。注入通道和它们的转换顺序在ADC_JSQR寄存器中选择。注入组里的转换总数目应写入ADC_JSQR寄存器的L[1:0]位中。 如果ADC_SQRx或ADC_JSQR寄存器在转换期间被更改,当前的转换被清除,一个新的启动脉冲将发送到ADC以转换新选择的组。

先说说利用ADC1的通道0的单次转换模式下的相关设置,这一节我们使用来进行AD转换,其详细设置步骤如下:

1、开启PA口时钟,设置PA0为模拟输入。

这里将PA0配置为 ADC的第0个采样通道,所以,我们先要使能PORTA的时钟,然后设置PA0为模拟输入。

RCC_APB2ENR|=1<<2; //使能PORTA口的时钟 GPIOA->CRL&=0XFFFF0000; //PA0 1 2 3 anolog输入 2、使能ADC1时钟,并设置分频因子。

要使用 要使用 ADC1,第一步就是要使能ADC1时钟,在使能完之后进行一次 的时钟,在使能完之后进行一次 ADC1的 复位。接着我们就可以通过RCC_CFGR设置 ADC1的分频因子。分频因子要确保 ADC1的时钟(ADCCLK)不要超过14Mhz 。

15 ADC3EN 7 IOPFEN 14 13 USART1EN TIM8EN 6 5 IOPEEN IOPDEN 12 SPI1EN 4 IOPCEN 11 TIM1EN 3 IOPBEN 10 ADC2EN 2 IOPAEN 9 ADC1EN 1 保留 8 IOPGEN 0 AFIOEN

RCC_APB2ENR的0~15位(06~32位保留) 例如:RCC->APB2ENR|=1<<9; //ADC1时钟使能 同理:RCC->APB2ENR|=1<<10; //ADC2时钟使能

RCC->APB2ENR|=1<<15; //ADC3时钟使能 15 ADC3RST 7 IOPFRST 14 13 12 11 10 9 8 USART1RST TIM8RST SPI1RST TIM1RST ADC2RST ADC1RST IOPGRST 6 5 4 3 2 1 0 IOPERST IOPDRST IOPCRST IOPBRST IOPARST 保留 AFIORST RCC_APB2RSTR

RCC_APB2RSTR|=1<<9; //ADC1复位 同理:RCC->APB2ENR|=1<<10; //ADC2复位 RCC->APB2ENR|=1<<15; //ADC3复位

RCC_APB2RSTR|=~(1<<9); //ADC1结束复位 同理:RCC->APB2ENR|=~(1<<10); //ADC2结束复位 RCC->APB2ENR|=~(1<<15); //ADC3结束复位 15 14 13 12 11 10 9 8 ADCPRE[1:0] PPRE2[2:0] PPRE1[2:0] 7 6 5 4 3 2 1 0 HPRE[3:0] SWS[1:0] SW[1:0]

RCC_CFGR RCC->CFGR&=~(3<<14); //分频因子清零 RCC->CFGR|=2<<14; //SYSCLK/DIV2=12M ADC时钟设置为 12M,ADC最大时钟不能超过14M,否则将导致ADC准确度下降! 3、设置ADC1的工作模式。

在设置完分频因子之后,我们就可以开始ADC1的模式配置了,设置单次转换模式、触发方式选择、数据对齐方式等都在这一步实现。

23 AWDEN 15 22 21 20 19 18 17 16 JAWDEN 保留 DUALMOD[3:0] 14 13 12 11 10 9 8 DISCNUM[2:0] JDISCN DISCEN JAUTO AWDSGL SCAN 7 6 5 4 3 2 1 0 JEOCIE AWDIE EOCIE AWDCH[4:0] ADC_CR1(ADC控制寄存器1) ADC1->CR1&=0XF0FFFF; //工作模式清零 ADC1->CR1|=0<<16; //独立工作模式 ADC1->CR1&=~(1<<8); //非扫描模式 23 22 21 20 19 18 17 TSVREFE SWSTART JSWSTART EXTTRIG EXTSEL[2:0] 15 14 13 12 11 10 9 JEXTTRIG JEXTSEL[2:0] ALIGN 保留 7 6 5 4 3 2 1 保留 RSTCAL CAL CONT ADC_CR2(ADC控制寄存器2)

16 保留 8 DMA 0 ADON ADC1->CR2&=~(1<<1); //单次转换模式 ADC1->CR2&=~(7<<17);

ADC1->CR2|=7<<17; //软件控制转换 ADC1->CR2|=1<<20; //使用用外部触发(SWSTART)!!! 必须使用一个事件来触发

ADC1->CR2&=~(1<<11); //右对齐 4、设置ADC1规则序列的相关信息。 接下来我们要设置规则序列的相关信息,这里只有一个通道并且是单次转换所以设置规则序列中通道数为1,然后设置通道0的采样周期。

23 15 SQ16[0] 7 22 21 L[3:0] 14 13 6 SQ14[2:0] 5 20 12 SQ15[2:0] 4 19 11 3 18 17 16 SQ16[4:1] 10 9 8 SQ14[2:0] 2 1 0 SQ13[2:0] ADC_SQR1(ADC规则序列寄存器1) 31 保留 23 30 29 28 27 26 25 24 SMP9[2:0] SMP8[2:0] 22 21 20 19 18 17 16 SMP7[2:0] SMP6[2:0] SMP5[2:1] 15 14 13 12 11 10 9 8 SMP5[0] SMP4[2:0] SMP3[2:0] SMP2[2] 7 6 5 4 3 2 1 0 SMP2[1:0] SMP1[2:0] SMP0[2:0] ADC_SMPR2(ADC采样时间寄存器) ADC1->SQR1&=~(0XF<<20); ADC1->SQR1&=0<<20; //1个转换在规则序列中 也就是只转换规则序列1 //设置通道0~3的采样时间

ADC1->SMPR2&=0XFFFFF000;//通道0,1,2,3采样时间清空 ADC1->SMPR2|=7<<9; //通道3 239.5周期,提高采样时间可以提高精确度 ADC1->SMPR2|=7<<6; //通道2 239.5周期,提高采样时间可以提高精确度 ADC1->SMPR2|=7<<3; //通道1 239.5周期,提高采样时间可以提高精确度 ADC1->SMPR2|=7<<0; //通道0 239.5周期,提高采样时间可以提高精确度 5)开启AD转换器,并校准。

在设置完了以上信息后,我们就开启AD 转换器,执行复位校准和AD 校准,注意这两步是必须的!不校准将导致结果很确。

23 22 21 20 19 18 17 TSVREFE SWSTART JSWSTART EXTTRIG EXTSEL[2:0] 15 14 13 12 11 10 9 JEXTTRIG JEXTSEL[2:0] ALIGN 保留 7 6 5 4 3 2 1 保留 RSTCAL CAL CONT 16 保留 8 DMA 0 ADON ADC_CR2(ADC控制寄存器2) ADC1->CR2|=1<<0; //开启AD转换器 ADC1->CR2|=1<<3; //使能复位校准 while(ADC1->CR2&1<<3); //等待校准结束

//该位由软件设置并由硬件清除。在校准寄存器被初始化后该位将被清除。 ADC1->CR2|=1<<2; //开启AD校准 while(ADC1->CR2&1<<2); //等待校准结束 //该位由软件设置以开始校准,并在校准结束时由硬件清除 6)读取 ADC 值。

在上面的校准完成之后,ADC 就算准备好了。接下来我们要做的是设置规则序列0里面的通道,然后启动 ADC转换。在结束后,读取 ADC1_DR里面的值就是了。

u16 Get_Adc(u8 ch) {

//设置转换序列 ADC1->SQR3&=0XFFFFFFE0;//规则序列1 通道ch ADC1->SQR3|=ch; ADC1->CR2|=1<<22; //启动规则转换通道 while(!(ADC1->SR&1<<1));//等待转换结束 return ADC1->DR; //返回adc值 }

八、IIC总线

IIC总线是由数据线SDA和时钟SCL构成的串行总线可发送和接收数据。IIC总线在传送数据过程中共有3种类型信号,分别是开始信号、结束信号和接收信号。 开始信号 结束信号 应答信号 SCL 高电平 高电平 SDA 由高电平向低电平跳变,开始传送数据 由低电平向高电平跳变,结束传送数据 接收数据的IC在接收到8bit数据后向发送数据的IC发出特定的低电平脉冲,表示已经收到数据;CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断;若未收到应答信号,则判断为受控单元出现故障。

一、使能IO口 void IIC_Init(void) {

RCC->APB2ENR|=1<<4; //使能PORTC的时钟 GPIOC->CHR&=0XFFF00FFF;

GPIOC->CRH|=0X00033000; //PC11,12推挽输出 GPIOC->ODR|=3<<11; //PC11,12输出高 }

void IIC_Start(void) {

SDA_OUT(); IIC_SDA=1;

IIC_SCL=1; //准备发送数据 delay_us(4); //等待

IIC_SDA=0; //SDA电平由高变低正在发送 delay_us(4);

IIC_SCL=0; //将始终总线SCL钳在低电平准备发送数据(只有SCL在低电平时SDA的数据才可高低电平变化) }

void IIC_Stop(void) {

SDA_OUT(); IIC_SDA=0;

IIC_SCL=0; //此时IIC还在传递数据 delay_us(4); IIC_SCL=1;

IIC_SDA=1; //在SCL为高电平时SDA由低电平变高电平时结束传送 delay_us(4); }

Void IIC_ASK(void)

{ IIC_SCL=0; SDA_OUT(); IIC_SDA=0; Delay_us(4); IIC_SCL=1; Delay_us(4); IIC_SCL=0; }

void IIC_NASK(void) {

IIC_SCL=0; SDA_OUT(); IIC_SDA=1; Delay_us(4); IIC_SCL=1; Delay_us(4); IIC_SCL=0; }

本文来源:https://www.bwwdw.com/article/bzvo.html

Top