牛人的STM32学习笔记(寄存器版本)
更新时间:2024-01-17 13:53:01 阅读量: 教育文库 文档下载
- stm32怎么学推荐度:
- 相关推荐
一、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; }
正在阅读:
牛人的STM32学习笔记(寄存器版本)01-17
计算机组成原理课后答案05-24
燕化新型裂解汽油加氢催化剂在大庆成功应用09-01
论中国传统文化对当代大学生的积极意义10-09
2017人教版七年级英语上册电子教案11-28
叶绿体色素测定方法05-16
微生物生物学实验指导 - 图文04-16
西澳大学硕士会计学怎么样03-11
小数的初步认识的教学反思06-09
高压电工作业考试复习题06-28
- exercise2
- 铅锌矿详查地质设计 - 图文
- 厨余垃圾、餐厨垃圾堆肥系统设计方案
- 陈明珠开题报告
- 化工原理精选例题
- 政府形象宣传册营销案例
- 小学一至三年级语文阅读专项练习题
- 2014.民诉 期末考试 复习题
- 巅峰智业 - 做好顶层设计对建设城市的重要意义
- (三起)冀教版三年级英语上册Unit4 Lesson24练习题及答案
- 2017年实心轮胎现状及发展趋势分析(目录)
- 基于GIS的农用地定级技术研究定稿
- 2017-2022年中国医疗保健市场调查与市场前景预测报告(目录) - 图文
- 作业
- OFDM技术仿真(MATLAB代码) - 图文
- Android工程师笔试题及答案
- 生命密码联合密码
- 空间地上权若干法律问题探究
- 江苏学业水平测试《机械基础》模拟试题
- 选课走班实施方案
- 寄存器
- 版本
- 笔记
- 学习
- STM32
- 牛人的
- 农业推广复习题及答案
- 教育心理学121
- 资源勘查专业生产实习报告
- 父母课堂读后感 - 家长篇
- 景区介绍
- 幼儿教师口语教学大纲11.4
- 2016浙大远程教育管理统计学作业
- 高格VE安装手册
- 模拟电子技术实训(智电101)指导书
- 云南省2018年中考数学二模试卷(含解析)
- 初中古诗词理解性默写练习
- Verilog - HDL—乐曲演奏电路设计
- NG2-BASS3.0 - 技术规范 - 数据集市
- 2005年中国卫生事业发展情况统计公报
- 西安西隆电气有限责任公司与西安沙尔特宝电气有限公司实地参观学习 - 图文
- 厂房建设合同范本
- 2017年二年级数学暑假作业
- 来例假的时候可以运动减肥吗
- 超声诊断仪项目可行性研究报告
- 小学六年级体育与健康理论课教案