笔记采子正点原子STM32F1开发指南
系统框架:
时钟树:
一般时钟初始化:如103对应着上面时钟树看
/* RCC system reset(for debug purpose) */
RCC_DeInit();
/* Enable HSE */
RCC_HSEConfig(RCC_HSE_ON);//设置为外部高速时钟源
/* Wait till HSE is ready */
HSEStartUpStatus = RCC_WaitForHSEStartUp();
if(HSEStartUpStatus == SUCCESS)
{
/* HCLK = SYSCLK */
RCC_HCLKConfig(RCC_SYSCLK_Div1);
/* PCLK2 = HCLK */
RCC_PCLK2Config(RCC_HCLK_Div1); //设置PCLK2的时钟跟HCLK一样,不分频HCLK即APB2
/* PCLK1 = HCLK/2 */ APB1
RCC_PCLK1Config(RCC_HCLK_Div2);//设置PCLK1的时钟为HCLK二分频,因为最大为HCLK二分一
/* Flash 2 wait state */
FLASH_SetLatency(FLASH_Latency_2);//设置FLASH的等待时间
STM32的CPU的最大速率已知为72MHz,但FLASH无法达到这么高的速度,因此要在CPU存取FLASH的过程中插入所谓的“等待周期”,显然CPU速度越快,所要插入的等待周期个数越多,原则是:
- 当CPU速率为0 ~ 24MHz时,不需要插入等待周期,即等到周期个数为0;
- 当CPU速率为24 ~ 48MHz时,插入1个等待周期;
- 当CPU速率为48MHz ~ 72MHz时,插入2个等待周期;
/* Enable Prefetch Buffer */使能预取内存
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
#ifdefine STM32F10X_CL
RCC_PREDIV2Config(RCC_PREDIV2_Div5);//25/5=5M
RCC_PLL2Config(RCC_PLL2Mul_8);//5X8=40M
RCC_PLL2Cmd(ENABLE);
while(RCC_GetFlagStatus(RCC_FLAG_PLL2RDY) == RESET);
RCC_PREDIV1Config(RCC_PREDIV1_Source_PLL2, RCC_PREDIV1_Div5);40/5=8M
#endif
/* PLLCLK = 8MHz * 9 = 72 MHz */倍频外部时钟信号源得PLLCLK
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
/* Enable PLL */
RCC_PLLCmd(ENABLE);
/* Wait till PLL is ready */
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}
/* Select PLL as system clock source */
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);//设置系统时钟为倍频后的时钟(看上图系统时钟有多种选择)
/* Wait till PLL is used as system clock source */
while(RCC_GetSYSCLKSource() != 0x08)
{
}
}
F107互联型 MCU区别与F103,因为他们具有USB OTG功能,因此需要特别的时钟
所以使用外部25M的晶震,分频5得8通过PLL2 X5得40,再分频5得8M;
端口复用和重映射:
为了使不同器件封装的外设 IO 功能数量达到最优,可以把一 些复用重新映射其他功能数量达到最优,可以把一 些复用重新映射其他功能数量达到最优,可以把一 些复用重新映射其他些引脚上。 STM32 中有很多内置外设的输入出引脚都具重映射 中有很多内置外设的输入出引脚都具重映射 (remap) 的功能 。
详细步骤为:
开GPIO时钟;使能对应复用功能时钟;使能复用时钟AFIO;开启重映射。
而部分重映射有自己对应的设置;
中断优先级:1.如果两个中断的抢占优先级和响应都一样,则看哪个中断更早先执行;2.高优先级的抢占优先级可以打断低的抢占优先级,而抢占优先级相同,高优先级的响应优先级不可以打断低响应优先级的中断。
结合实例说明一下:假定设置中断优先级组为 2,然后设置中断 ,然后设置中断 3(RTC 3(RTC 中断 )的抢占优先级 为 2,响应优先级为1中断 。中断 6(外部中断 (外部中断 0)的抢占优先级为 3,,响应优先级为 0。中断 。中断 7(外 部中断 1)的抢占优先级为 的抢占优先级为 2,响应优先级为 0。那么这 。那么这 3个中断 的优先级顺序为:个中断 的优先级顺序为:7> 中 断 3> 中断 6。
上面例子中的断 3和中断 和中断 7都可以打断 中断6的中断 。而7和中断 和中断 3却不可以相互打断。
中断分组表:
中断优先级分组函数:NVIC_PriorityGroupConfig其实就是对寄存器SCB->SIRCR设置,
NVIC_IRQChannelPreemptionPriority :定义该中断的抢占优先级;
NVIC_IRQChannelChnnel:定义是什么中断;
NVIC_IRQChannelSubPriority:定义中断子优先级表(中断优先级);
NVIC_IRQChannelCmd:使能中断。
当中得delay.h文件
CM3内核处理器有一个SYSTICK时钟
sys.h文件宏定义了IO口的操作,包括读入和输出。
usart文件对主要写串口得操作
printf函数得支持,
STM32IO口:一共又8种模式
输出:1.推挽输出:可以输出高、低电平,由IC决定 推挽电路是两个参数相同的三极管或MOSFET,以推挽方式存在于电路中,各负责正负半周的波形放大任务,电路工作时,两只对称的功率开关管每次只有一个导通,所以导通损耗小、效率高。输出既可以向负载灌电流,也可以从负载抽取电流。推拉式输出级既提高电路的负载能力,又提高开关速度。
2.开漏输出:输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行。适合于做电流型的驱动,其吸收电流的能力相对强(一般20mA以内)。
3浮空输入:空输入状态下,IO的电平状态是不确定的,完全由外部输入决定,如果在该引脚悬空的情况下,读取该端口的电平是不确定的。有可能被干扰导致输入不准确。
4上拉输入/下拉输入/模拟输入
5:复用开漏输出、复用推挽输出:GPIO口被用作第二功能时的配置情况所需要设置。
具体软件配置:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | B | C, ENABLE):使能APB2总线外设时钟
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//输入输出类型
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//响应速度如翻转(只有在输出有用)有5,10,50
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//根据表设置为复用推挽
GPIO_Init(GPIOA , &GPIO_InitStructure);
//PA10作为US1的RX端,负责接收数据
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//可以为浮空或上拉
GPIO_Init(GPIOA, &GPIO_InitStructure);
//串口初始化
USART_InitTypeDef USART_InitStructure;
//将结构体设置为缺省状态
USART_StructInit(&USART_InitStructure);
//波特率设置为115200
USART_InitStructure.USART_BaudRate = 115200;
//一帧数据的宽度设置为8bits
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
//在帧结尾传输1个停止位
USART_InitStructure.USART_StopBits = USART_StopBits_1;
//奇偶失能模式,无奇偶校验
USART_InitStructure.USART_Parity = USART_Parity_No;
//发送/接收使能
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
//硬件流控制失能
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
//设置串口1
USART_Init(USART1, &USART_InitStructure);
//打开串口1的中断响应函数
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
//打开串口1
USART_Cmd(USART1, ENABLE);
既然设置了中断响应函数,那么就要有中断设置:
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //选择中断分组2
NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQChannel; //选择串口1中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0; //抢占式中断优先级设置
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0; //响应式中断优先级设置为0
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //使能中断
NVIC_Init(&NVIC_InitStructure);
void USART1_IRQHandler(void) //串口1中断
{
char RX_dat; //定义字符变量
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //判断发生接收中断
{USART_ClearITPendingBit(USART1, USART_IT_RXNE); //清除中断标志
GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)0x01); //开始传输
RX_dat=USART_ReceiveData(USART1) & 0x7F; //接收数据,整理除去前两位
USART_SendData(USART1, RX_dat); //发送数据
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET){}//等待发送结束
}
}
配置
EXTI线,使中断线和
IO
针脚线连接上
STM32的每一个IO口都可以作为外部中盾的中断输入口,
配置EXTI线,使中断线和IO针脚线连接上
1、 将EXTI线连接到IO端口上
将EXTI线4连接到端口GPIOD的第4个针脚上
GPIO_EXTILineConfig(GPIO_PortSourceGPIOD,GPIO_PinSource4);
2、配置中断边沿
/*配置EXTI线4上出现下降沿,则产生中断*/
EXTI_InitStructure.EXTI_Line = EXTI_Line2; 配置的4号针脚,
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中断模式分为中断和事件
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发 (可以是上,下,上下)
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //中断线使能
EXTI_Init(&EXTI_InitStructure); //初始化中断
EXTI_GenerateSWInterrupt(EXTI_Line4); //EXTI_Line4中断使能
中断函数:
void EXTI2_RQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line2)!= RESET)
{ 中断实现逻辑
EXTI_ClearITPendingBit(EXTI_Line2); //清除标志
}
}
同一条中断线只能控制一个IO
定时器中断实验:
定时器分通用定时器(2,3,4,5)TIMx_CNT计数,TIMx_PSC预分频(1-65535),每个定时器有4个独立通道可以用来1.输入捕获,2.输出比较,3.PWM生成,4.单脉冲模式输出
可设置多种中断模式:1.更行:计数器的上或者下一处,计数器初始化(通过软件或内/外触发)2.触发时间(计数器启动,停止,初始化或者由内,外触发计数)3.输入捕获4.输出比较。5支持针对定位的增量 (正交 )编码器和霍尔传感电路
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQChannel;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = arr; //设置自动重装的值也就是计数的值
TIM_TimeBaseStructure.TIM_Prescaler = psc;//时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1 ;//时钟线的分频
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//模式为向上计数
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; //溢出中断计算就是溢出多少次才给你一个溢出中断
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //初始化TM2
TIM_ARRPreloadConfig(TIM2, ENABLE); //使能TM2
TIM_Cmd(TIM2, ENABLE);
TIM_TimeBaseStructure.TIM_Prescaler = 2;//分频2 72M/(2+1)/2=24MHz
TIM_TimeBaseStructure.TIM_Period = 65535; //计数值65535
((1+TIM_Prescaler )/72M)*(1+TIM_Period )=((1+2)/72M)*(1+65535)=0.00273秒=366.2Hz */
void TIM2_IRQHandler(void)
{
u8 ReadValue;
//检测是否发生溢出更新事件
if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
//清除TIM2的中断待处理位
实现逻辑
TIM_ClearITPendingBit(TIM2 , TIM_FLAG_Update);
}
}
PWM输出:除了6,7定时器都可以用来输出PWM,高级定时器1,8更可以同时产生7路PWM输出。
定时器的相关设置功能一样
PWM的设置
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //配置为PWM模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = CCR1;
TIM_OCInitStructure.TIM_OCPolarity =TIM_OCPolarity_High; //输出极性为高
TIM_OC1Init(TIM3, &TIM_OCInitStructure);//初始化
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_Cmd(TIM3,ENABLE);//使能定时器
PWM模式1- 在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为有效电平,否则为
无效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为无效电平(OC1REF=0),否
则为有效电平(OC1REF=1)。
PWM模式2- 在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为无效电平,否则为
有效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为有效电平,否则为无效电
平。
SPI:串行外围设备接口
一共4条通讯线MISO,MOSI,SCLK,CS。SPI的特点是能同时收发串行数据提供频率可编程时钟,发送结束中断标志,写保护等
【 CPHA相位】
首先说明一点,capture strobe = latch = read = sample,都是表示数据采样,数据有效的时刻。
相位,对应着数据采样是在第几个边沿(edge),是第一个边沿还是第二个边沿,0对应着第一个边沿,1对应着第二个边沿。
对于:
CPHA=0,表示第一个边沿:
对于CPOL=0,idle时候的是低电平,第一个边沿就是从低变到高,所以是上升沿;
对于CPOL=1,idle时候的是高电平,第一个边沿就是从高变到低,所以是下降沿;
CPHA=1,表示第二个边沿:
对于CPOL=0,idle时候的是低电平,第二个边沿就是从高变到低,所以是下降沿;
对于CPOL=1,idle时候的是高电平,第一个边沿就是从低变到高,所以是上升沿;
【 CPOL极性】
先说什么是SCLK时钟的空闲时刻,其就是当SCLK在数发送8个bit比特数据之前和之后的状态,于此对应的,SCLK在发送数据的时候,就是正常的工作的时候,有效active的时刻了。
先说英文,其精简解释为:Clock Polarity = IDLE state of SCK。
再用中文详解:
SPI的CPOL,表示当SCLK空闲idle的时候,其电平的值是低电平0还是高电平1:
CPOL=0,时钟空闲idle时候的电平是低电平,所以当SCLK有效的时候,就是高电平,就是所谓的active-high;
CPOL=1,时钟空闲idle时候的电平是高电平,所以当SCLK有效的时候,就是低电平,就是所谓的active-l
void SPI1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1, ENABLE );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //通讯模式,半双工,全双工,串行发和收这里是全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//设置STM32为主机
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//数据帧格式为8位
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//时钟极性空闲为高
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//相位在第二个边沿被采集
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//设置NSS管脚由硬件还是软件控制(这里是软件)
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;//SPI的波特率 频率=时钟/分频
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//数据传输高位在前
SPI_InitStructure.SPI_CRCPolynomial = 7;//设置CRC教研多项式
SPI_InitStructure.SPI_BaudRatePrescaler = SpeedSet ;//传送速率
SPI_Init(SPI1, &SPI_InitStructure); //¸
SPI_Cmd(SPI1, ENABLE); //
SPI1_ReadWriteByte(0xff);//
}
对于原子的MISO设置为复用推挽
对于STM32的这一类管脚来说(如USART_RX)即可以设置成为输入模式,也可以设置成为复用的推挽输出。其工作都是正常的,不过建议大家还是设置成为输入端口的好,容易理解。
具体产生这一问题的原因是:从功能上来说,MISO应该配置为输入模式才对,但为什么也可以配置为GPIO_Mode_AF_PP?请看下面的GPIO复用功能配置框图。当一个GPIO端口配置为GPIO_Mode_AF_PP是,这个端口的内部结构框图如下:图中可以看到,片上外设的复用功能输出信号会连接到输出控制电路,然后在端口上产生输出信号。但是在芯片内部,MISO是SPI模块的输入引脚,而不是输出引脚,也就是说图中的"复用功能输出信号"根本不存在,因此"输出控制电路"不能对外产生输出信号。
u8 SPI1_ReadWriteByte(u8 TxData)
{
u8 retry=0;
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //
{
retry++;
if(retry>200)return 0;
}
SPI_I2S_SendData(SPI1, TxData); //
retry=0;
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)//
{
retry++;
if(retry>200)return 0;
}
return SPI_I2S_ReceiveData(SPI1); //
}
一般的读写如下
u8 SPI_ReadSR(void)
{
u8 byte=0;
SPI_FLASH_CS=0; //置0片选使能器件
SPI1_ReadWriteByte(W25X_ReadStatusReg); // 向FLASH的寄存器发送读命令
byte=SPI1_ReadWriteByte(0Xff); //发送一个无效的命令来读取数据(应为要发送时钟)
SPI_FLASH_CS=1; //
return byte;
}
//
//
void SPI_Write_SR(u8 sr)
{
SPI_FLASH_CS=0; //置0片选使能器件
SPI1_ReadWriteByte(W25X_WriteStatusReg); // 向FLASH的寄存器发送写命令
SPI1_ReadWriteByte(sr); //写入数据
SPI_FLASH_CS=1; // 取消使能器件
}
MPU6050的初始化当中包含了MPU6050.h头文件
mpu.setClockSource(MPU6050_CLOCK_PLL_ZGYRO); // Set Clock to ZGyro
mpu.setFullScaleGyroRange(MPU6050_GYRO_FS); // Set Gyro Sensitivity to config.h
mpu.setFullScaleAccelRange(MPU6050_ACCEL_FS_2); // +- 2G
mpu.setDLPFMode(MPU6050_DLPF_BW_256); // Set Gyro Low Pass Filter
mpu.setRate(0); // 0=1kHz, 1=500Hz, 2=333Hz, 3=250Hz, 4=200Hz
mpu.setSleepEnabled(false);
mpu.i2cErrors = 0;
USB初始化:
Set_System();
Set_USBClock();
USB_Interrupts_Config();
USB_Init();
I2C:(硬件I2C)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE); 开启I2C时钟
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; //
I2C工作模式配置:
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;//设置为I2C模式(还有其模式)
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;//设置I2C的占空比:注意该参数只有I2C工作在快速模式下(即SCK时钟频率高于100kHz)下才有意义。
I2C_InitStructure.I2C_OwnAddress1 =I2C1_OWN_ADDRESS7; //设置自己的地址
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;//设置为有应答
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//设置相连IC的地址位,与外设有关
在I2C通信时,只有在时钟信号SCL为高电平时,数据线上对应的数据才是有效数据,所以若高电平持续时间短的话,读写时间短,数据可能没有读写完,导致数据传输不准确;若高电平持续时间长的话,读写时间相应变长,可能导致时间的浪费,通信效率变低。所以合理的占空比对I2C通信是很必要的!
void I2C_EE_ByteWrite(u8* pBuffer, u8 WriteAddr)
{
/* Send STRAT condition */
I2C_GenerateSTART(I2C1, ENABLE);
/* Test on EV5 and clear it */
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
/* Send EEPROM address for write */
I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);
/* Test on EV6 and clear it */
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
/* Send the EEPROM's internal address to write to */
I2C_SendData(I2C1, WriteAddr);
/* Test on EV8 and clear it */
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
/* Send the byte to be written */
I2C_SendData(I2C1, *pBuffer);
/* Test on EV8 and clear it */
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
/* Send STOP condition */
I2C_GenerateSTOP(I2C1, ENABLE);
}
使用IO口软件模拟I2C
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //2个IO口设置为推挽输出
void IIC_Start(void)
{
SDA_OUT(); //配置SCL口的输出速率50
IIC_SDA=1;
IIC_SCL=1;
delay_us(4);
IIC_SDA=0;//START:when CLK is high,DATA change form high to low
delay_us(4);
IIC_SCL=0;
}
void IIC_Stop(void)
{
SDA_OUT();//
IIC_SCL=0;
IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
delay_us(4);
IIC_SCL=1;
IIC_SDA=1;//
delay_us(4);
}
等待从机应答信号;
u8 IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
SDA_IN(); //配置SDA口为浮空输入
IIC_SDA=1;delay_us(1);
IIC_SCL=1;delay_us(1);
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL=0;
return 0;
}
//产生应答;就是SDA输出0
void IIC_Ack(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
//不产生应答 就是SDA输出1
void IIC_NAck(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
IIC_SCL=0;//
for(t=0;t<8;t++)
{
IIC_SDA=(txd&0x80)>>7;
txd<<=1;
delay_us(2); //
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
delay_us(2);
}
}
//
u8 IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();
for(i=0;i<8;i++ )
{
IIC_SCL=0;
delay_us(2);
IIC_SCL=1;
receive<<=1;
if(READ_SDA)receive++;
delay_us(1);
}
if (!ack)
IIC_NAck();//
else
IIC_Ack(); //
return receive;
}
R/W(0为写1为读)
IIC写的过程:
IIC读的过程:
主机发送数据流程
(1)主机在检测到总线为“空闲状态”(即 SDA、SCL 线均为高电平)时,发送一个启动信号“S”,开始一次通信的开始
(2)主机接着发送一个命令字节。该字节由 7 位的外围器件地址和 1 位读写控制位 R/W组成(此时 R/W=0)
(3)相对应的从机收到命令字节后向主机回馈应答信号 ACK(ACK=0)
(4)主机收到从机的应答信号后开始发送第一个字节的数据
(5)从机收到数据后返回一个应答信号 ACK
(6)主机收到应答信号后再发送下一个数据字节
(7)当主机发送最后一个数据字节并收到从机的 ACK 后,通过向从机发送一个停止信号P结束本次通信并释放总线。从机收到P信号后也退出与主机之间的通信
主机接收数据流程
(1)主机发送启动信号后,接着发送命令字节(其中 R/W=1)
(2)对应的从机收到地址字节后,返回一个应答信号并向主机发送数据
(3)主机收到数据后向从机反馈一个应答信号
(4)从机收到应答信号后再向主机发送下一个数据
(5)当主机完成接收数据后,向从机发送一个“非应答信号(ACK=1)”,从机收到ASK=1 的非应答信号后便停止发送
(6)主机发送非应答信号后,再发送一个停止信号,释放总线结束通信
注意都是要写入命令和地址(都是写入)
应答就是SDA口读取,0为成功1为失败。
USB部分
{
pInformation = &Device_Info;
pInformation->ControlState = 2;
pProperty = &Device_Property;
pUser_Standard_Requests = &User_Standard_Requests;
/* Initialize devices one by one */
pProperty->Init(); //从这里进入开始进入到下面的程序
}
{
/* Update the serial number string descriptor with the data from the unique
ID*/
Get_SerialNum();//获取要发送给设备当设备ID
pInformation->Current_Configuration = 0;
/* Connect the device */
PowerOn();
/* Perform basic device initialization operations */
USB_SIL_Init();
/* configure the USART to the default settings */
// USART_Config_Default();
bDeviceState = UNCONNECTED;
}
{
USB_Istr();
}
{
CTR_HP();
}
/* DIR = 1 & (CTR_TX | CTR_RX) => 2 int pending */