《树莓派CM3》I2C引脚映射到GPIO28/29

本文介绍了一种在树莓派CM3上通过CMIO扩展GPIO至GPIO45的方法,解决了因DPI占用GPIO0-GPIO27导致的接口不足问题。详细讲解了如何修改bcm2835库,将I2C引脚重新映射到GPIO28和GPIO29,以连接DS3231时钟芯片。

目前网上树莓派很少针对CM3的资料,手上有一项目,因为使用DPI占用了GPIO0-GPIO27,也就是说树莓派3B的板子上J8的IO全部用掉了,另外项目还要接OLED,DS3231时钟芯片等其他外设,所以不能使用树莓派3B,还好树莓派提供工业计算模块CM3,从微雪资料上看GPIO至少扩展到GPIO45,所以就先选用了CM3Lite+微雪的CMIO+内存卡等其他配件,CM3lite是不带eMMC的版本,我是感觉研究期间还是内存卡方便,更贴近pi-3B。
因为微雪CMIO上面的DS3231的I2C引脚连接到默认的GPIO2(SDA1)、GPIO3(SCL1),提供的例程 http://www.waveshare.net/wiki/Compute_Module_IO_Board_Plus 也是针对这个引脚,所以需要对程序进行修改,其中Python、wiringPi的库、驱动不太熟悉,所以决定从BCM2835例程修改。树莓派3B、CM3的CPU是BCM2837,但是最详细的CPU资料只有BCM2835的PDF,大概看了一下,里面基本以bcm283x描述,可以预判bcm2837和bcm2835的寄存器应该没有太多的变化,于是便研究bcm2835 C library http://www.airspayce.com/mikem/bcm2835/ ,我下载版本是1.58。下载后解压,目录下面主要看src文件夹下的bcm2835.c和bcm2835.h。
在这里插入图片描述
打开微雪DS3231的bcm2835例程ds3231.c和bcm2835库的bcm2835.c、bcm2835.h,找到对应I2C有关的代码,如下图:
在这里插入图片描述
在bcm2835.c中找到了bcm2835_i2c_begin(void)函数,其中红色框是使用SDA0(GPIO0)和SCL0(GPIO1)两个引脚,绿框是默认的(源码#define I2C_V1是被注释掉的),SDA1(GPIO2)和SCL1(GPIO3),对应看bcm2835.h第797行定义,有个RPiGPIOPin的枚举类型。
在这里插入图片描述
可以看出就是没定义GPIO28(SDA0)和GPIO29(SCL0),所以直接把#ifdef I2C_V1到#endif 这段引脚分配改成:

volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_DIV/4;
/* Set the I2C/BSC0 pins to the Alt 0 function to enable I2C access on them */
 bcm2835_gpio_fsel(28, BCM2835_GPIO_FSEL_ALT0); /* SDA */
 bcm2835_gpio_fsel(29, BCM2835_GPIO_FSEL_ALT0); /* SCL */

之后把bcm2835.c中其他有#ifdef I2C_V1的地方对应修改掉,总之只留bcm2835_bsc0部分,GPIO改成对应的28,29。
改好之后保存,按照下图重新编译安装到树莓派:
http://www.airspayce.com/mikem/bcm2835/
然后编译微雪ds3231.c,即cd到对应文件夹,再 gcc -Wall ds3231.c -o ds3231 -lbcm2835,生成ds3231可执行文件,然后sudo ./ds3231回车,就看到了终端开始打印出时间日期,1s更新一次。

注意:

  • 执行ds3231时一定要加sudo!
  • GPIO28,GPIO29做I2C引脚需要加上拉电阻。
根据以下程序,分别概括成几个模块的软件设计:#include "stm32f10x.h" #include "1602.h" #include "math.h" typedef unsigned char u8_t; typedef signed char s8_t; typedef unsigned short u16_t; typedef signed short s16_t; typedef unsigned long u32_t; typedef signed long s32_t; //位带操作,实现51类似的GPIO控制功能 //具体实现思想,参考<<CM3权威指南>>第五章(87页~92页). //IO口操作宏定义 #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)) //IO口地址映射 #define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C #define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C #define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C #define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C #define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C #define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C #define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C #define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808 #define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08 #define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008 #define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408 #define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808 #define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08 #define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08 //IO口操作,只对单一的IO口! //确保n的值小于16! #define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出 #define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入 #define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出 #define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入 #define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出 #define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入 #define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出 #define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入 #define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //输出 #define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //输入 #define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出 #define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入 #define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出 #define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入 //以下为汇编函数 void WFI_SET(void); //执行WFI指令 void INTX_DISABLE(void);//关闭所有中断 void INTX_ENABLE(void); //开启所有中断 void MSR_MSP(u32 addr); //设置堆栈地址 #define EnableINT() INTX_ENABLE() #define DisableINT() INTX_DISABLE() static u8 fac_us=0; //us延时倍乘数 static u16 fac_ms=0; //ms延时倍乘数,在ucos下,代表每个节拍的ms数 void delay_init() { #if SYSTEM_SUPPORT_OS //如果需要支持OS. u32 reload; #endif SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟 HCLK/8 fac_us=SystemCoreClock/8000000; //为系统时钟的1/8 #if SYSTEM_SUPPORT_OS //如果需要支持OS. reload=SystemCoreClock/8000000; //每秒钟的计数次数 单位为M reload*=1000000/delay_ostickspersec; //根据delay_ostickspersec设定溢出时间 //reload为24位寄存器,最大值:16777216,在72M下,约合1.86s左右 fac_ms=1000/delay_ostickspersec; //代表OS可以延时的最少单位 SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //开启SYSTICK中断 SysTick->LOAD=reload; //每1/delay_ostickspersec秒中断一次 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK #else fac_ms=(u16)fac_us*1000; //非OS下,代表每个ms需要的systick时钟数 #endif } //延时nus //nus为要延时的us数. void delay_us(u32 nus) { u32 temp; SysTick->LOAD=nus*fac_us; //时间加载 SysTick->VAL=0x00; //清空计数器 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 do { temp=SysTick->CTRL; }while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器 SysTick->VAL =0X00; //清空计数器 } //延时nms //注意nms的范围 //SysTick->LOAD为24位寄存器,所以,最大延时为: //nms<=0xffffff*8*1000/SYSCLK //SYSCLK单位为Hz,nms单位为ms //对72M条件下,nms<=1864 void delay_ms(u16 nms) { u32 temp; SysTick->LOAD=(u32)nms*fac_ms; //时间加载(SysTick->LOAD为24bit) SysTick->VAL =0x00; //清空计数器 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 do { temp=SysTick->CTRL; }while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器 SysTick->VAL =0X00; //清空计数器 } void USART1_init(void) { USART_InitTypeDef USART_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_AFIO , ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; 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_Tx | USART_Mode_Rx; //USART1 USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); USART_ClearITPendingBit(USART1, USART_IT_RXNE); USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); USART_ITConfig(USART1, USART_IT_PE, ENABLE); USART_ITConfig(USART1, USART_IT_ERR, ENABLE); USART1->DR = (0X55 & (uint16_t)0x01FF); while((USART1->SR&0X40)==0); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } void UsartSendData1(unsigned char *send_buff,unsigned long int length) { unsigned long int i = 0; delay_ms(1); for(i = 0;i < length;i ++) { USART1->DR = (send_buff[i] & (uint16_t)0x01FF); while((USART1->SR&0X40)==0); } delay_ms(1); } int GQ=0;//保存光强 int LX=0;//光类型 0白光 1黄光 2混合光 int DW=0;//档位 0~5档 int LPWM=0;//亮度 0~100 int moshi=0;//0手动 1自动 2声控 #define LED_B PBout(8) #define LED_H PBout(7) #define C_YR PBin(6) void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE); //ENABLEPBCLK GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable , ENABLE); //Disable jtag RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); //LCD1602 管脚 D0~D7 双向I/O GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15| GPIO_Pin_3| GPIO_Pin_4| GPIO_Pin_5; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8| GPIO_Pin_11| GPIO_Pin_12| GPIO_Pin_15; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; GPIO_Init(GPIOA, &GPIO_InitStructure); //ADC GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_0; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStructure); //LCD1602 管脚 控制 推挽模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 |GPIO_Pin_13|GPIO_Pin_14; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOB, &GPIO_InitStructure); //按键 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOB, &GPIO_InitStructure); //LED灯 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_8; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOB, &GPIO_InitStructure); } /* ADC配置 */ void ADC_Set(unsigned char ch) { ADC_InitTypeDef ADC_InitStructure;//ADC结构体变量//注意在一个语句快内变量的声明要放在可执行语句的前面,否则出错,因此要放在ADC1_GPIO_Config();前面 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC1和ADC2工作在独立模式 ADC_InitStructure.ADC_ScanConvMode = DISABLE; //使能扫描 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//ADC转换工作在连续模式 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//由软件控制转换,不使用外部触发 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//转换数据右对齐 ADC_InitStructure.ADC_NbrOfChannel = 1;//转换通道为1 ADC_Init(ADC1, &ADC_InitStructure); //初始化ADC if(ch == ADC_Channel_7) { ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 1, ADC_SampleTime_239Cycles5); } if(ch == ADC_Channel_6) { ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_239Cycles5); } ADC_Cmd(ADC1, ENABLE);//使能ADC1 ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE); ADC_SoftwareStartConvCmd(ADC1, ENABLE); } #define ADC_TIMES 10 #define ADCLOST 2 unsigned int GetADC(unsigned char ch) { unsigned int i, j; unsigned int buf[ADC_TIMES]; unsigned long int sum; long int temp; static long int XQ = 0; ADC_Set(ch); for(i=0; i < ADC_TIMES; i++) { buf[i] = ADC_GetConversionValue(ADC1);; delay_ms(10); } for(i = 0; i < ADC_TIMES - 1; i++) { for(j = i + 1; j < ADC_TIMES; j++) { if(buf[i] > buf[j]) { temp = buf[i]; } } } sum = 0; for(i = ADCLOST; i < ADC_TIMES - ADCLOST; i++) { sum += buf[i]; } temp = sum / (ADC_TIMES - 2 * ADCLOST); if( (XQ>=temp && (XQ-temp)<=1 ) || (XQ<temp && (temp-XQ)<=1)) { temp = XQ; } else { XQ = temp; } return (unsigned int)(temp>>4); } void Display_1(int T1,int T2,int T3,int T4,int T5) { char xx=0,yy=0; //光强 WrByte1602(yy,xx++,&#39;G&#39;); //. WrByte1602(yy,xx++,&#39;Q&#39;); WrByte1602(yy,xx++,&#39;=&#39;); WrByte1602(yy,xx++,T1%1000/100+&#39;0&#39;); WrByte1602(yy,xx++,T1%100/10+&#39;0&#39;); WrByte1602(yy,xx++,T1%10+&#39;0&#39;); WrByte1602(yy,xx++,&#39; &#39;); //光类型 WrByte1602(yy,xx++,&#39;T&#39;); //. WrByte1602(yy,xx++,&#39;Y&#39;); WrByte1602(yy,xx++,&#39;P&#39;); WrByte1602(yy,xx++,&#39;E&#39;); WrByte1602(yy,xx++,&#39;-&#39;); if(T2==0)//0混合 1白光 2黄光 { WrByte1602(yy,xx++,&#39;B&#39;); WrByte1602(yy,xx++,&#39;H&#39;); } else if(T2==1)//0混合 1白光 2黄光 { WrByte1602(yy,xx++,&#39;B&#39;); WrByte1602(yy,xx++,&#39; &#39;); } else if(T2==2)//0混合 1白光 2黄光 { WrByte1602(yy,xx++,&#39;H&#39;); WrByte1602(yy,xx++,&#39; &#39;); } WrByte1602(yy,xx++,&#39; &#39;); xx=0,yy=1; //档位 WrByte1602(yy,xx++,&#39;D&#39;); //. WrByte1602(yy,xx++,&#39;W&#39;); WrByte1602(yy,xx++,&#39;=&#39;); WrByte1602(yy,xx++,T3%10+&#39;0&#39;); WrByte1602(yy,xx++,&#39; &#39;); //亮度 WrByte1602(yy,xx++,&#39;L&#39;); //. WrByte1602(yy,xx++,&#39;D&#39;); WrByte1602(yy,xx++,&#39;=&#39;); WrByte1602(yy,xx++,T4%1000/100+&#39;0&#39;); WrByte1602 if(T5==0)//0手动 1自动 2声控 { WrByte1602(yy,xx++,&#39;S&#39;); WrByte1602(yy,xx++,&#39;D&#39;); } else if(T5==1)//0手动 1自动 2声控 { WrByte1602(yy,xx++,&#39;Z&#39;); WrByte1602(yy,xx++,&#39;D&#39;); } else if(T5==2)//0手动 1自动 2声控 { WrByte1602(yy,xx++,&#39;S&#39;); WrByte1602(yy,xx++,&#39;K&#39;); } WrByte1602(yy,xx++,&#39; &#39;); } //int GQ=0;//保存光强 //int LX=0;//光类型 0白光 1黄光 2混合光 //int DW=0;//档位 0~5档 //int LPWM=0;//亮度 0~100 //int moshi=0;//0手动 1自动 2声控 void anjiansaom(void) { static uchar K1,K2,K3; static uchar K1Flag=0,K2Flag=0,K3Flag=0; K1 = GPIO_ReadInputDataBit(GPIOB , GPIO_Pin_9); K2 = GPIO_ReadInputDataBit(GPIOB , GPIO_Pin_11); K3 = GPIO_ReadInputDataBit(GPIOB , GPIO_Pin_10); //手动/自动 if(K1==0) { K1Flag = 1; } else { if(K1Flag) { K1Flag = 0; if(moshi==1||moshi==2)//0手动 1自动 2声控 { moshi=0; } else { moshi=1; } } } //亮度 if(K2==0) { K2Flag = 1; } else { if(K2Flag) { K2Flag = 0; if(moshi==0)//0手动 1自动 2声控 { DW++; if(DW>5) { DW=0; } LPWM=DW*20; } } } { if(K3Flag) { K3Flag = 0; //光类型 0白光 1黄光 2混合光 LX++; if(LX>2) { LX=0; } } } } void TimerConfig(TIM_TypeDef* TIMx,unsigned long int time) { NVIC_InitTypeDef NVIC_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2,ENABLE); RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM3,ENABLE); TIM_DeInit(TIMx); TIM_TimeBaseStructure.TIM_Period = time * 2; /* 自动重装载寄存器周期的值(计数值) */ /* 累计 TIM_Period个频率后产生一个更新或者中断 */ TIM_TimeBaseStructure.TIM_Prescaler= (1000- 1); /* 时钟预分频数 例如:时钟频率=72MHZ/(时钟预分频+1) */ TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; /* 采样分频 */ TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; /* 向上计数模式 */ TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure); TIM_ClearFlag(TIMx, TIM_FLAG_Update); /* 清除溢出中断标志 */ TIM_ITConfig(TIMx,TIM_IT_Update,ENABLE); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); NVIC_InitStr NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } /** * @brief 关闭或者开启指定定时器 * @param TIMx: where x can be 1 to 17 to select the TIM peripheral * @param NewState: new state of the TIMx peripheral. * This parameter can be: ENABLE or DISABLE. * @retval None */ void TimerSW( TIM_TypeDef* TIMx, FunctionalState NewState ) { TIM_SetCounter(TIMx,0); TIM_Cmd(TIMx,NewState); } int main(void) { float temp; SystemInit(); SystemCoreClockUpdate(); delay_init(); //引脚配置 GPIO_Configuration(); //关闭LED灯 LED_B=0; LED_H=0; delay_ms(1000); //初始化LCD1602 Init1602(); TimerConfig(TIM2,1); TimerSW(TIM2, ENABLE); //串口初始化 USART1_init(); while (1) { delay_ms(20); //读取ADC的值,然后计算光强 ADC_Set(0); //光强 //先读取ADC值 GQ =(255-GetADC(0))/2-60; if(GQ<0) { GQ=0; } GQ = GQ*2; if(GQ>100) { GQ = 100; } //按键扫描 anjiansaom(); //光强 光类型 档位 亮度 模式 Display_1(GQ,LX,DW,LPWM,moshi); //自动模式光强控制 //首先得有人才会亮 if(moshi==1) { if(C_YR==1 )//传感器输出高电平代表有人 { //根据亮度计算光亮 //越暗 越亮 LPWM=100-GQ; } else { LPWM=0;//关灯 } } } } /** * @brief 定时器2中断服务函数 * @param TIMx: where x can be 1 to 17 to select the TIM peripheral * @param NewState: new state of the TIMx peripheral. * This parameter can be: ENABLE or DISABLE. * @retval None */ void TIM2_IRQHandler(void) { static int i=0; if ( TIM_GetITStatus(TIM2 , TIM_IT_Update) != RESET ) { TIM_ClearITPendingBit(TIM2 , TIM_FLAG_Update); // PCout(13)=~PCout(13); //这里实现PWM 波形的产生 i++; if(i<=LPWM) { //开灯 //暖光 还是白光 还是混合 if(LX==0)//0混合 1白光 2黄光 { } } else { //关灯 LED_B=0;LED_H=0; } //不能超过100 if(i>100) { i=0; } } } //串口1接收中断 void USART1_IRQHandler(void) { char shuju=0; if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { USART_ClearITPendingBit(USART1, USART_IT_RXNE); //保存收到的数据 shuju=USART_ReceiveData(USART1); //读取接收到的数据 //收到数据1 //你好小灯 唤醒 if(shuju==1)//进入声控模式 { moshi=2; } if(moshi==2) { //收到数据2 //开灯 if(shuju==2)//开灯 { if(LPWM==0) { DW=3; LPWM=DW*20; } } else if(shuju==3)//关灯 { DW=0; LPWM=DW*20; } else if(shuju==4)//亮一点 { DW++; if(DW>5) } } } if(USART_GetITStatus(USART1, USART_IT_PE) != RESET) { USART_ClearITPendingBit(USART1, USART_IT_PE); } if (USART_GetFlagStatus(USART1, USART_IT_LBD) != RESET) { USART_ClearITPendingBit(USART1, USART_IT_LBD); } if(USART_GetFlagStatus(USART1, USART_FLAG_ORE) != RESET) { USART_ReceiveData(USART1); USART_ClearFlag(USART1, USART_FLAG_ORE); } if(USART_GetFlagStatus(USART1, USART_FLAG_NE) != RESET) { USART_ClearFlag(USART1, USART_FLAG_NE); } if(USART_GetFlagStatus(USART1, USART_FLAG_FE) != RESET) { USART_ClearFlag(USART1, USART_FLAG_FE); } if(USART_GetFlagStatus(USART1, USART_FLAG_PE) != RESET) { USART_ClearFlag(USART1, USART_FLAG_PE); } if (USART_GetITStatus(USART1, USART_IT_TC) != RESET) { USART_ClearITPendingBit(USART1, USART_IT_TC); } } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t* file, uint32_t line) { /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* Infinite loop */ while (1) {} } #endif /******************* (C) COPYRIGHT 2009 STMicroelectronics *****END OF FILE****/ #include "stm32f10x.h" #include "1602.h" #define uchar unsigned char #define uint unsigned int uchar ASCII[]={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39};//2 /* PA8~PA15为数据口 */ #define LCD1602_RS_0 GPIO_ResetBits(GPIOB , GPIO_Pin_12) #define LCD1602_RS_1 GPIO_SetBits(GPIOB , GPIO_Pin_12) #define LCD1602_RW_0 GPIO_ResetBits(GPIOB , GPIO_Pin_13) #define LCD1602_RW_1 GPIO_SetBits(GPIOB , GPIO_Pin_13) #define LCD1602_EN_0 GPIO_ResetBits(GPIOB , GPIO_Pin_14) #define LCD1602_EN_1 GPIO_SetBits(GPIOB , GPIO_Pin_14) #define LCD1602_D0_0 GPIO_ResetBits(GPIOB , GPIO_Pin_15) #define LCD1602_D0_1 GPIO_SetBits(GPIOB , GPIO_Pin_15) #define LCD1602_D1_0 GPIO_ResetBits(GPIOA , GPIO_Pin_8) #define LCD1602_D1_1 GPIO_SetBits(GPIOA , GPIO_Pin_8) #define LCD1602_D2_0 GPIO_ResetBits(GPIOA , GPIO_Pin_11) #define LCD1602_D2_1 GPIO_SetBits(GPIOA , GPIO_Pin_11) #define LCD1602_D3_0 GPIO_ResetBits(GPIOA , GPIO_Pin_12) #define LCD1602_D3_1 GPIO_SetBits(GPIOA , GPIO_Pin_12) #define LCD1602_D4_0 GPIO_ResetBits(GPIOA , GPIO_Pin_15) #define LCD1602_D4_1 GPIO_SetBits(G #define LCD1602_D6_0 GPIO_ResetBits(GPIOB , GPIO_Pin_4) #define LCD1602_D6_1 GPIO_SetBits(GPIOB , GPIO_Pin_4) #define LCD1602_D7_0 GPIO_ResetBits(GPIOB , GPIO_Pin_5) #define LCD1602_D7_1 GPIO_SetBits(GPIOB , GPIO_Pin_5) #define GET_LCD1602_D0 GPIO_ReadInputDataBit(GPIOB , GPIO_Pin_15) #define GET_LCD1602_D1 GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_8) #define GET_LCD1602_D2 GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_11) #define GET_LCD1602_D3 GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_12) #define GET_LCD1602_D4 GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_15) #define GET_LCD1602_ void SetLCD1602DValue(unsigned char value) { if(value&0x01) { LCD1602_D0_1; } else { LCD1602_D0_0; } if(value&0x02) { LCD1602_D1_1; } { LCD1602_D2_1; } else { LCD1602_D2_0; } if(value&0x08) { LCD1602_D3_1; } else { LCD1602_D3_0; } if(value&0x10) { LCD1602_D4_1; } else { LCD1602_D4_0; } if(value&0x20) { LCD1602_D5_1; } else { LCD1602_D5_0; } if(value&0x40) if(value&0x80) { LCD1602_D7_1; } else { LCD1602_D7_0; } } unsigned char GetLCD1602DValue(void) { unsigned char dat; dat = 0x00; if(GET_LCD1602_D0) { dat = dat |0x01; } if(GET_LCD1602_D1) { dat = dat |0x02; } if(GET_LCD1602_D2) { dat = dat |0x04; } if(GET_LCD1602_D3) { dat = dat |0x08; } if(GET_LCD1602_D4) { dat = dat |0x10; } if(GET_LCD1602_D5) { dat = dat |0x20; } if(GET_LCD1602_D6) { dat = dat |0x40; } if(GET_LCD1602_D7) { dat = dat |0x80; } return(dat); } /*******************通用延时子程序**************************/ void mDelay(uint i) { for (;i>0;i--) {uchar j=10;while(--j);} } void LCD1602DTest(void) { LCD1602_RS_1; LCD1602_RW_1; LCD1602_EN_1; SetLCD1602DValue(0x00); mDelay(100); mDelay(100); LCD1602_EN_0; SetLCD1602DValue(0xFF); mDelay(100); mDelay(100); LCD1602_RS_0; LCD1602_RW_0; LCD1602_EN_0; SetLCD1602DValue(0x00); } /************************************************************* 函数名称:读忙状态子函数 全局变量:无 参数说明:无 返回说明:无 版 本:1.0 说 明:读取LCD1602忙状态 **************************************************************/ void Read1602() //读忙状态 { long int i=90000; //建一个循环变量避免器件发生故障停在这里 SetLCD1602DValue(0xff); LCD1602_RS_0; LCD1602_RW_1; //设置LCD为读取数据状态 LCD1602_EN_1; //使能LCD,高电平 while ((i--)&&(GetLCD1602DValue()&0x80)); //检测数据口最高位状态,为0则空闲 LCD1602_EN_0; //关闭使能 } /************************************************************** 函数名称:写操作子函数 全局变量:无 参数说明:Dat为数据,command为指令(1为写数据,0为写指令) 返回说明:无 版 本:1.0 说 明:往LCD1602写入数据、指令 ***************************************************************/ void Write1602(uchar Dat,unsigned char command) { Read1602(); SetLCD1602DValue(Dat); if(command) { LCD1602_RS_1; //RS为1写数据、为0写指令 } else { LCD1602_RS_0; } LCD1602_RW_0; //RW为低,进行写操作 LCD1602_EN_1; LCD1602_EN_0; //E端控制一个高脉冲 } /*************************************************************** 函数名称:LCD1602初始化子函数 全局变量:无 参数说明:无 返回说明:无 版 本:1.0 说 明:设置工作模式、清屏、开显示 ***************************************************************/ void Init1602(void) { LCD1602DTest(); mDelay(10); mDelay(10); Write1602(0x38,0); //8位点阵方式 mDelay(10); Write1602(0x38,0); mDelay(10); Write1602(0x38,0); mDelay(10); Write1602(0x38,0); mDelay(10); Write1602(0x01,0); //清屏 mDelay(10); Write1602(0x0c,0); //开显示,光标不显示 } void WrByte1602_EX(uchar x,uchar y,uchar dat) { Write1602(0x80+(x<<6)+y,0); //合并生成地址 Write1602(dat,1); //写入数据 } void WrByte1602(uchar x,uchar y,uchar dat) { Write1602(0x80+(x<<6)+y,0); //合并生成地址 Write1602(dat,1); //写入数据 } /*************************************************************** 函数名称:LCD1602整行字符串写入子函数 全局变量:无 参数说明:x为写入的行(0或1),*p为写入的字符串数组 返回说明:无 版 本:1.0 说 明:在LCD1602任意行写入字符串 ****************************************************************/ void LCD_Write_String(uchar x1,uchar x,uchar *p) { x1 = 1; Write1602(0x80+(x<<6),0); //合并生成行首地址 while(*p) {Write1602(*p,1);p++;} //逐个字符写入 } #include "stm32f10x.h" #include "1602.h" #define uchar unsigned char #define uint unsigned int uchar ASCII[]={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39};//2 /* PA8~PA15为数据口 */ #define LCD1602_RS_0 GPIO_ResetBits(GPIOB , GPIO_Pin_12) #define LCD1602_RS_1 GPIO_SetBits(GPIOB , GPIO_Pin_12) #define LCD1602_RW_0 GPIO_ResetBits(GPIOB , GPIO_Pin_13) #define LCD1602_RW_1 GPIO_SetBits(GPIOB , GPIO_Pin_13) #define LCD1602_EN_0 GPIO_ResetBits(GPIOB , GPIO_Pin_14) #define LCD1602_EN_1 GPIO_SetBits(GPIOB , GPIO_Pin_14) #define LCD1602_D0_0 GPIO_ResetBits(GPIOB , GPIO_Pin_15) #define LCD1602_D0_1 GPIO_SetBits(GPIOB , GPIO_Pin_15) #define LCD1602_D1_0 GPIO_ResetBits(GPIOA , GPIO_Pin_8) #define LCD1602_D1_1 GPIO_SetBits(GPIOA , GPIO_Pin_8) #define LCD1602_D2_0 GPIO_ResetBits(GPIOA , GPIO_Pin_11) #define LCD1602_D2_1 GPIO_SetBits(GPIOA , GPIO_Pin_11) #define LCD1602_D3_0 GPIO_ResetBits(GPIOA , GPIO_Pin_12) #define LCD1602_D3_1 GPIO_SetBits(GPIOA , GPIO_Pin_12) #define LCD1602_D4_0 GPIO_ResetBits(GPIOA , GPIO_Pin_15) #define LCD1602_D4_1 GPIO_SetBits(GPIOA , GPIO_Pin_15) #define LCD1602_D5_0 GPIO_ResetBits(GPIOB , GPIO_Pin_3) #define LCD1602_D5_1 GPIO_SetBits(GPIOB , GPIO_Pin_3) #define LCD1602_D6_0 GPIO_ResetBits(GPIOB , GPIO_Pin_4) #define LCD1602_D6_1 GPIO_SetBits(GPIOB , GPIO_Pin_4) #define LCD1602_D7_0 GPIO_ResetBits(GPIOB , GPIO_Pin_5) #define LCD1602_D7_1 GPIO_SetBits(GPIOB , GPIO_Pin_5) #define GET_LCD1602_D0 GPIO_ReadInputDataBit(GPIOB , GPIO_Pin_15) #define GET_LCD1602_D1 GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_8) #define GET_LCD1602_D2 GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_11) #define GET_LCD1602_D3 GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_12) #define GET_LCD1602_D4 GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_15) #define GET_LCD1602_D5 GPIO_ReadInputDataBit(GPIOB , GPIO_Pin_3) #define GET_LCD1602_D6 GPIO_ReadInputDataBit(GPIOB , GPIO_Pin_4) #define GET_LCD1602_D7 GPIO_ReadInputDataBit(GPIOB , GPIO_Pin_5) void SetLCD1602DValue(unsigned char value) { if(value&0x01) { LCD1602_D0_1; } else { LCD1602_D0_0; } if(value&0x02) { LCD1602_D1_1; } else { LCD1602_D1_0; } if(value&0x04) { LCD1602_D2_1; } else { LCD1602_D2_0; } if(value&0x08) { LCD1602_D3_1; } else { LCD1602_D3_0; } if(value&0x10) { LCD1602_D4_1; } else { LCD1602_D4_0; } if(value&0x20) { LCD1602_D5_1; } else { LCD1602_D5_0; } if(value&0x40) { LCD1602_D6_1; } else { LCD1602_D6_0; } if(value&0x80) { LCD1602_D7_1; } else { LCD1602_D7_0; } } unsigned char GetLCD1602DValue(void) { unsigned char dat; dat = 0x00; if(GET_LCD1602_D0) { dat = dat |0x01; } if(GET_LCD1602_D1) { dat = dat |0x02; } if(GET_LCD1602_D2) { dat = dat |0x04; } if(GET_LCD1602_D3) { dat = dat |0x08; } if(GET_LCD1602_D4) { dat = dat |0x10; } if(GET_LCD1602_D5) { dat = dat |0x20; } if(GET_LCD1602_D6) { dat = dat |0x40; } if(GET_LCD1602_D7) { dat = dat |0x80; } return(dat); } /*******************通用延时子程序**************************/ void mDelay(uint i) { for (;i>0;i--) {uchar j=10;while(--j);} } void LCD1602DTest(void) { LCD1602_RS_1; LCD1602_RW_1; LCD1602_EN_1; SetLCD1602DValue(0x00); mDelay(100); mDelay(100); LCD1602_RS_0; LCD1602_RW_0; LCD1602_EN_0; SetLCD1602DValue(0xFF); mDelay(100); mDelay(100); LCD1602_RS_0; LCD1602_RW_0; LCD1602_EN_0; SetLCD1602DValue(0x00); } /************************************************************* 函数名称:读忙状态子函数 全局变量:无 参数说明:无 返回说明:无 版 本:1.0 说 明:读取LCD1602忙状态 **************************************************************/ void Read1602() //读忙状态 { long int i=90000; //建一个循环变量避免器件发生故障停在这里 SetLCD1602DValue(0xff); LCD1602_RS_0; LCD1602_RW_1; //设置LCD为读取数据状态 LCD1602_EN_1; //使能LCD,高电平 while ((i--)&&(GetLCD1602DValue()&0x80)); //检测数据口最高位状态,为0则空闲 LCD1602_EN_0; //关闭使能 } /************************************************************** 函数名称:写操作子函数 全局变量:无 参数说明:Dat为数据,command为指令(1为写数据,0为写指令) 返回说明:无 版 本:1.0 说 明:往LCD1602写入数据、指令 ***************************************************************/ void Write1602(uchar Dat,unsigned char command) { Read1602(); SetLCD1602DValue(Dat); if(command) { LCD1602_RS_1; //RS为1写数据、为0写指令 } else { LCD1602_RS_0; } LCD1602_RW_0; //RW为低,进行写操作 LCD1602_EN_1; LCD1602_EN_0; //E端控制一个高脉冲 } /*************************************************************** 函数名称:LCD1602初始化子函数 全局变量:无 参数说明:无 返回说明:无 版 本:1.0 说 明:设置工作模式、清屏、开显示 ***************************************************************/ void Init1602(void) { LCD1602DTest(); mDelay(10); mDelay(10); Write1602(0x38,0); //8位点阵方式 mDelay(10); Write1602(0x38,0); mDelay(10); mDelay(10); Write1602(0x01,0); //清屏 mDelay(10); Write1602(0x0c,0); //开显示,光标不显示 } void WrByte1602_EX(uchar x,uchar y,uchar dat) { Write1602(0x80+(x<<6)+y,0); //合并生成地址 Write1602(dat,1); //写入数据 } void WrByte1602(uchar x,uchar y,uchar dat) { Write1602(0x80+(x<<6)+y,0); //合并生成地址 Write1602(dat,1); //写入数据 } /*************************************************************** 函数名称:LCD1602整行字符串写入子函数 全局变量:无 参数说明:x为写入的行(0或1),*p为写入的字符串数组 返回说明:无 版 本:1.0 说 明:在LCD1602任意行写入字符串 ****************************************************************/ void LCD_Write_String(uchar x1,uchar x,uchar *p) { x1 = 1; Write1602(0x80+(x<<6),0); //合并生成行首地址 while(*p) {Write1602(*p,1);p++;} //逐个字符写入 }
05-13
<think>好的,我现在需要帮助用户将他们的STM32嵌入式系统代码概括为几个主要的功能模块。用户提到了包括GPIO配置、延时函数、USART初始化、按键扫描、ADC读取、LCD1602显示控制、定时器配置以及中断服务程序等部分。我需要先回忆一下每个模块在STM32中的常见实现方式,并结合用户提供的引用内容来组织回答。 首先,用户提供的引用中提到了GPIO的配置,引用[1]说明了GPIO是用于处理数字输入输出的外设,有多种工作模式。所以在概括GPIO配置模块时,应该包括初始化函数,设置引脚的模式(输入/输出)、速度、上下拉等参数,还可能涉及复用功能配置。 接下来是延时函数。通常,STM32的延时函数可以通过SysTick定时器实现,或者使用简单的循环延时。需要区分不同精度的延时,比如微秒级和毫秒级,这里可能需要用户代码中有具体的实现方式,比如基于系统时钟配置的延时函数。 USART初始化部分,引用[2]提到USART用于串行通信,用户的项目中可能用到了USART来传输数据,比如温度监测中的串行通信。因此USART模块应包括初始化函数,配置波特率、数据位、停止位、校验位等参数,以及发送和接收数据的函数。 按键扫描模块通常涉及GPIO输入模式的配置,检测按键的状态,可能包括消抖处理。用户提到按键扫描,所以需要说明如何轮询或中断方式检测按键,并返回按键值。 ADC读取模块,引用[2]和[3]提到ADC用于模拟信号转换,用户项目中用到了内部温度传感器。ADC模块应包括初始化函数,配置通道、采样时间、触发方式(如定时器触发),以及读取ADC值的函数。可能还涉及DMA传输,如引用[3]中的DMA应用,需要说明是否使用DMA来提高效率。 LCD1602显示控制模块,用户代码中可能有初始化LCD、发送命令和数据的函数,以及显示字符串或数值的函数。需要说明接口方式,比如4位或8位数据线,控制引脚的定义等。 定时器配置模块,引用[3]提到了定时器3触发ADC,所以需要配置定时器的时钟、预分频、重载值等参数,设置定时器中断,并编写中断服务程序来处理周期性任务,比如定时触发ADC转换。 中断服务程序部分,需要列出各个中断的处理函数,比如定时器中断、ADC转换完成中断、USART接收中断等,说明它们如何被触发和处理的流程。 在组织这些模块时,要确保每个模块的功能和关键点被清晰描述,同时结合用户提供的引用内容中的信息,比如GPIO的工作模式、ADC与DMA的结合使用等。此外,用户可能需要了解模块之间的协作关系,比如定时器触发ADC采样,ADC通过DMA传输数据,再通过LCD显示,这样的数据流需要简要说明。 最后,生成相关问题时要考虑用户可能的进一步需求,比如优化各模块的方法、模块间的协作问题、调试技巧等,这样能帮助用户深入理解系统设计。</think>根据提供的功能描述,可将STM32嵌入式系统代码概括为以下主要功能模块: ### 1. GPIO配置模块 - 功能:初始化控制LED、按键、LCD等外设的GPIO端口 - 关键点: - 设置引脚工作模式(输入/输出/复用) - 配置输出速度(如50MHz) - 选择上下拉电阻模式 - 例:LED控制引脚配置为推挽输出模式[^1] ### 2. 延时函数模块 - 实现方式: - 微秒级延时(`delay_us`):基于SysTick定时器 - 毫秒级延时(`delay_ms`):通过系统时钟循环计数 - 特殊处理:需考虑时钟树配置参数 ### 3. USART通信模块 - 包含: - 初始化函数(设置波特率/数据位/校验位) - 数据发送函数(阻塞/中断/DMA方式) - 接收中断服务程序 - 典型应用:与上位机的温度数据传输[^2] ### 4. 按键扫描模块 - 实现机制: ```c uint8_t Key_Scan(void) { if(KEY按下){ delay_ms(10); // 消抖处理 return 按键值; } return 0; } ``` - 支持:单按/长按检测 ### 5. ADC采集模块 - 核心配置: - ADC通道选择(如通道16对应内部温度传感器) - 采样时间设置(如239.5周期) - 触发方式(定时器触发/连续转换)[^3] - 数据获取: - 直接读取模式 - DMA传输模式(提高效率) ### 6. LCD1602显示模块 - 主要函数: - `LCD_Init()`:初始化4位数据线模式 - `LCD_WriteString()`:显示格式化数据 - 特殊字符处理:自定义CGRAM字符 ### 7. 定时器配置模块 - 典型配置流程: ```c TIM_TimeBaseInitTypeDef TIM_InitStruct; TIM_InitStruct.TIM_Period = 999; // 自动重装载值 TIM_InitStruct.TIM_Prescaler = 7199; // 预分频值 TIM_TimeBaseInit(TIM3, &TIM_InitStruct); ``` - 应用场景:生成ADC触发信号/周期任务调度 ### 8. 中断服务程序 - 包含中断类型: - 定时器更新中断 - ADC转换完成中断 - USART接收中断 - 处理原则:保持ISR简洁,使用标志位机制 各模块通过`main()`函数中的初始化序列建立关联,例如: ```c int main(void) { GPIO_Config(); TIM3_Init(); ADC1_Init(); LCD_Init(); while(1) { Key_Process(); Display_Update(); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值