串口DMA方式发送数据
需要用到DMA方式处理一些数据。可我对DMA不太了解,上午没事就拿串口开刀。参考原子的代码,做了串口DMA方式发送数据的实验。
以下注释是实验的产物。
1、DMA初始化
void DMA_Configuration(void)
{
DMA_DeInit(DMA1_Channel4); //将DMA的通道1寄存器重设为缺省值
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR; //DMA外设基地址。DMA将数据写入这个寄存器。写入后直接发送
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff; //DMA内存基地址,在内存中。相当于数据源。
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //外设作为数据传输的目的地。传输方式从内存到外设。USART为外设
DMA_InitStructure.DMA_BufferSize = sizeof(SendBuff); //DMA通道的DMA缓存的大小。一次传输的数据量,不大于65535
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //USART1->DR地址是固定的
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增。读一个数据后,自动移动到下一个数据
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据宽度,即DR的位数
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位。元数据宽度
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//DMA_Mode_Normal; //工作在循环模式,手动停止
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道,中优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //不是内存到内存传输。
DMA_Init(DMA1_Channel4, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器
}
2、主函数中调用
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); //使能UART DMA传输
/* Enable USART1 TX DMA1 Channel */
DMA_Cmd(DMA1_Channel4, DISABLE ); //关闭USART1 TX DMA1 所指示的DMA通道
//DMA_Configuration();
DMA_InitStructure.DMA_BufferSize = sizeof(SendBuff); //DMA通道的DMA缓存的大小
DMA_Init(DMA1_Channel4, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器
DMA_Cmd(DMA1_Channel4, ENABLE); //使能USART1 TX DMA1 所指示的通道
//扫描按键停止DMA
while(1)
{
t=KEY_Scan();
if(t==1)//KEY0按下
{
USART_DMACmd(USART1, USART_DMAReq_Tx, DISABLE);
DMA_Cmd(DMA1_Channel4, DISABLE );
}
}
162楼
定时器触发DMA方式的DAC
使用定时器触发DAC转换,弄了几天终于搞定。
今天这个实验的整个工作过程是这样的:
1、先定义一个数据源,作为DMA时的数据源
uint8_t Escalator8bit[5000] = {0x0,0x11,0x22,0x33,0x44,0x55, 0x66,0x77,0x88,0x99,0xaa,0xbb,0xCC,0xdd,0xee, 0xFF};
2、配置GPIO DAC1 DAC2分别对应PA4和PA5
void DAC_GPIOConfig(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //必须是模拟输出模式
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
3、使能时钟。
GPIOA和DAC不用说了,那是必须的。由于使用了TIM6触发DAC1转换,需要使能TIM6时钟。
使用了DMA2的3号通道,需要使能DMA2时钟。
DAC使用那个通道进行DMA,参看STM32参考手册中文版第148和149页关于DMA请求映像的描述
void DAC_RCCConfig(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE); //别开错时钟了
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
}
4、TIM6设置
TIM_PrescalerConfig(TIM6, 0xF, TIM_PSCReloadMode_Update); //TIM6预分频
TIM_SetAutoreload(TIM6, 0xFF); //自动装载的值。
TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update); //产生什么样的触发信号。选择产生更新信号
5、DAC的初始化
//DAC初始化
DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO; //选择触发源
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
//使能OutputBuffer可提高带负载能力。接耳机,实测:关闭OutputBuffer时声音很小几乎听不见。使能后声音还可以
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
DAC_Init(DAC_Channel_1, &DAC_InitStructure);
6、DMA配置
DMA_DeInit(DMA2_Channel3); //DMA2的3号通道。必须是这个通道
DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR8R1_Address; //外设地址,即DAC通道 1的 8位右对齐数据保持寄存器(DAC_DHR8R1)地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&Escalator8bit; //源数据地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //外设是数据目标
DMA_InitStructure.DMA_BufferSize = sizeof(Escalator8bit); //Escalator8bit的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //禁止外设地址自动增长
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //源数据地址自动增长
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据宽度
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //存储器数据宽度
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //循环模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA2_Channel3, &DMA_InitStructure);
7、之后就可以使能DMA功能和定时器触发了
DAC_Cmd(DAC_Channel_1, ENABLE); //使能DAC
DAC_DMACmd(DAC_Channel_1, ENABLE); //使能DAC的DMA功能
TIM_Cmd(TIM6, ENABLE); //使能TIM6 按照手册要求,TIM6必须在DAC使能之后再使能
接上耳机,会听见一个很单调的声音。大约1K不到的频率。
谁玩过MIG 29或者LOCK ON这个游戏的话,会很熟悉这个声音。
8、测试代码
My_DAC2_Init();
while(1)
{
//printf("当前DAC输出值为:%d mv\r\n",(3300*DAC_GetDataOutputValue(DAC_Channel_1))/4095);
if(TIM_GetFlagStatus (TIM6,TIM_FLAG_Update) == SUCCESS )
{
TIM_ClearFlag(TIM6,TIM_FLAG_Update);
printf("当前DAC输出值为:%d mv\r\n",(3300*DAC_GetDataOutputValue(DAC_Channel_1))/4095);
}
}
上图:(稍后补上)
我的耳机:
数据图:
使用定时器触发DAC转换,弄了几天终于搞定。
今天这个实验的整个工作过程是这样的:
1、先定义一个数据源,作为DMA时的数据源
uint8_t Escalator8bit[5000] = {0x0,0x11,0x22,0x33,0x44,0x55, 0x66,0x77,0x88,0x99,0xaa,0xbb,0xCC,0xdd,0xee, 0xFF};
2、配置GPIO DAC1 DAC2分别对应PA4和PA5
void DAC_GPIOConfig(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //必须是模拟输出模式
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
3、使能时钟。
GPIOA和DAC不用说了,那是必须的。由于使用了TIM6触发DAC1转换,需要使能TIM6时钟。
使用了DMA2的3号通道,需要使能DMA2时钟。
DAC使用那个通道进行DMA,参看STM32参考手册中文版第148和149页关于DMA请求映像的描述
void DAC_RCCConfig(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE); //别开错时钟了
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
}
4、TIM6设置
TIM_PrescalerConfig(TIM6, 0xF, TIM_PSCReloadMode_Update); //TIM6预分频
TIM_SetAutoreload(TIM6, 0xFF); //自动装载的值。
TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update); //产生什么样的触发信号。选择产生更新信号
5、DAC的初始化
//DAC初始化
DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO; //选择触发源
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
//使能OutputBuffer可提高带负载能力。接耳机,实测:关闭OutputBuffer时声音很小几乎听不见。使能后声音还可以
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
DAC_Init(DAC_Channel_1, &DAC_InitStructure);
6、DMA配置
DMA_DeInit(DMA2_Channel3); //DMA2的3号通道。必须是这个通道
DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR8R1_Address; //外设地址,即DAC通道 1的 8位右对齐数据保持寄存器(DAC_DHR8R1)地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&Escalator8bit; //源数据地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //外设是数据目标
DMA_InitStructure.DMA_BufferSize = sizeof(Escalator8bit); //Escalator8bit的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //禁止外设地址自动增长
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //源数据地址自动增长
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据宽度
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //存储器数据宽度
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //循环模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA2_Channel3, &DMA_InitStructure);
7、之后就可以使能DMA功能和定时器触发了
DAC_Cmd(DAC_Channel_1, ENABLE); //使能DAC
DAC_DMACmd(DAC_Channel_1, ENABLE); //使能DAC的DMA功能
TIM_Cmd(TIM6, ENABLE); //使能TIM6 按照手册要求,TIM6必须在DAC使能之后再使能
接上耳机,会听见一个很单调的声音。大约1K不到的频率。
谁玩过MIG 29或者LOCK ON这个游戏的话,会很熟悉这个声音。
8、测试代码
My_DAC2_Init();
while(1)
{
//printf("当前DAC输出值为:%d mv\r\n",(3300*DAC_GetDataOutputValue(DAC_Channel_1))/4095);
if(TIM_GetFlagStatus (TIM6,TIM_FLAG_Update) == SUCCESS )
{
TIM_ClearFlag(TIM6,TIM_FLAG_Update);
printf("当前DAC输出值为:%d mv\r\n",(3300*DAC_GetDataOutputValue(DAC_Channel_1))/4095);
}
}
上图:(稍后补上)
我的耳机:
数据图:
163楼
LCD和SRAM同时工作测试代码
就这么一个实验,同时工作不冲突。
工程包含了LCD的驱动和SRAM驱动。Ctrl+F5直接运行即可。
本来想加入串口程序,后来有问题就删掉了。有兴趣的童鞋可以加进去吧用电脑件事SRAM的数据读写
工程使用12M晶振,使用8M晶振的童鞋需要改一下。更改方式请参看jobs的进程贴
代码包如下:
http://share.eepw.com.cn/share/download/id/75363
就这么一个实验,同时工作不冲突。
工程包含了LCD的驱动和SRAM驱动。Ctrl+F5直接运行即可。
本来想加入串口程序,后来有问题就删掉了。有兴趣的童鞋可以加进去吧用电脑件事SRAM的数据读写
工程使用12M晶振,使用8M晶振的童鞋需要改一下。更改方式请参看jobs的进程贴
代码包如下:
http://share.eepw.com.cn/share/download/id/75363
164楼
一晚上,试验dac输出wav。没搞定,郁闷。没有示波器,看输出完全靠耳朵,真麻烦啊。暂停mp3解码,等时机到了,工具全了再来搞。
165楼
开始玩CAN
晚上弄了下CAN 准备实现LoopBack模式的数据收发。就是自己收自己发送的数据。
LoopBack是CAN控制器的一种工作模式。板子只有一个CAN控制器,而且电阻也焊错了(原理图错误)。现在只能使用这种模式了。
在STM32参考手册里边的地428页这么讲的:
通过对CAN_BTR寄存器的LBKM位置’1’,来选择环回模式。在环回模式下,bxCAN把发送的报
文当作接收的报文并保存(如果可以通过接收过滤)在接收邮箱里。
环回模式可用于自测试。为了避免外部的影响,在环回模式下CAN内核忽略确认错误(在数据/远
程帧的确认位时刻,不检测是否有显性位)。在环回模式下,bxCAN在内部把Tx输出回馈到Rx输
入上,而完全忽略CANRX引脚的实际状态。发送的报文可以在CANTX引脚上检测到。
这只是一种只检测模式。
这种模式工作示意图如下:
如果有示波器,还可以看见CAN发送引脚的波形输出
今天只是学习下,没代码
晚上弄了下CAN 准备实现LoopBack模式的数据收发。就是自己收自己发送的数据。
LoopBack是CAN控制器的一种工作模式。板子只有一个CAN控制器,而且电阻也焊错了(原理图错误)。现在只能使用这种模式了。
在STM32参考手册里边的地428页这么讲的:
通过对CAN_BTR寄存器的LBKM位置’1’,来选择环回模式。在环回模式下,bxCAN把发送的报
文当作接收的报文并保存(如果可以通过接收过滤)在接收邮箱里。
环回模式可用于自测试。为了避免外部的影响,在环回模式下CAN内核忽略确认错误(在数据/远
程帧的确认位时刻,不检测是否有显性位)。在环回模式下,bxCAN在内部把Tx输出回馈到Rx输
入上,而完全忽略CANRX引脚的实际状态。发送的报文可以在CANTX引脚上检测到。
这只是一种只检测模式。
这种模式工作示意图如下:
如果有示波器,还可以看见CAN发送引脚的波形输出
今天只是学习下,没代码
166楼
连着上了几天班,早出晚归的一直没时间弄板子。
今天参考野火程序,弄了下CAN。还不错 Loopback模式正常收发数据。
配置CAN为中断接收模式
参考了野火的程序实现。
这里基本就是我对野火程序的理解,和拷贝
1、设置NVIC 使能CAN中断接收
static void CAN_NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable CAN1 RX0 interrupt IRQ channel */
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn; //中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; // 主优先级为0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 次优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
2、引脚GPIO设置
板子上的CAN引脚被从定义到了PB8和PB9.
需要使用AFIO的Renap功能
static void CAN_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE);
/* CAN1 Periph clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
/* Configure CAN pin: RX */ // PB8
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Configure CAN pin: TX */ // PB9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure);
//将CAN1引脚重定义到PB8 PB9 使用引脚服用功能
GPIO_PinRemapConfig(GPIO_Remap1_CAN1, ENABLE);
}
3、将CAN配置为LoopBack模式
配置完成后发送一帧数据,并接收。以检查是否配置成功,此处的接收使用了查询方式
TestStatus CAN_Polling(void)
{
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CanTxMsg TxMessage;
CanRxMsg RxMessage;
uint32_t i = 0;
uint8_t TransmitMailbox = 0;
/* CAN register init */
CAN_DeInit(CAN1);
CAN_StructInit(&CAN_InitStructure);
/* CAN cell init */
CAN_InitStructure.CAN_TTCM=DISABLE; // 时间触发通信禁止
CAN_InitStructure.CAN_ABOM=DISABLE; // 离线退出是在中断置位清0后退出
CAN_InitStructure.CAN_AWUM=DISABLE; // 自动唤醒模式:清零sleep
CAN_InitStructure.CAN_NART=DISABLE; // 自动重新传送豹纹,知道发送成功
CAN_InitStructure.CAN_RFLM=DISABLE; // FIFO没有锁定,新报文覆盖旧报文
CAN_InitStructure.CAN_TXFP=DISABLE; // 发送报文优先级确定:标志符
CAN_InitStructure.CAN_Mode=CAN_Mode_LoopBack; // 回环模式
CAN_InitStructure.CAN_SJW=CAN_SJW_1tq; // 1tq、BS1、BS2的值跟波特率有关
CAN_InitStructure.CAN_BS1=CAN_BS1_8tq;
CAN_InitStructure.CAN_BS2=CAN_BS2_7tq;
CAN_InitStructure.CAN_Prescaler=5; // 分频系数为5
CAN_Init(CAN1, &CAN_InitStructure); // 初始化CAN1
/* CAN 过滤器初始化 */
CAN_FilterInitStructure.CAN_FilterNumber=0;
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; //标识符过滤
//使用32位过滤器,接收报文标识符的每一位都必须跟过滤器标识符相同
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000; //设置标识符
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=0; //接收FIFO
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //使能过滤器
CAN_FilterInit(&CAN_FilterInitStructure);
/* transmit */
TxMessage.StdId=0x11; // 设定标准标识符(11位,扩展的为29位)
TxMessage.RTR=CAN_RTR_DATA; // 传输消息的帧类型为数据帧(还有远程帧)
TxMessage.IDE=CAN_ID_STD; // 消息标志符实验标准标识符
TxMessage.DLC=2; // 发送两帧,一帧8位
TxMessage.Data[0]=0xCA; // 第一帧数据
TxMessage.Data[1]=0xFE; // 第二帧数据
TransmitMailbox=CAN_Transmit(CAN1, &TxMessage); //处理好数据后,将数据贞挂号等待发送。
i = 0;
// 用于检查消息传输是否正常
while((CAN_TransmitStatus(CAN1, TransmitMailbox) != CANTXOK) && (i != 0xFF))
{
i++;
}
i = 0;
// 检查返回的挂号的信息数目
while((CAN_MessagePending(CAN1, CAN_FIFO0) < 1) && (i != 0xFF))
{
i++;
}
/* receive */
RxMessage.StdId=0x00;
RxMessage.IDE=CAN_ID_STD;
RxMessage.DLC=0;
RxMessage.Data[0]=0x00;
RxMessage.Data[1]=0x00;
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
if (RxMessage.StdId!=0x11)
{
return FAILED;
}
if (RxMessage.IDE!=CAN_ID_STD)
{
return FAILED;
}
if (RxMessage.DLC!=2)
{
return FAILED;
}
/* 判断发送的信息和接收的信息是否相等 */
if ((RxMessage.Data[0]<<8|RxMessage.Data[1])!=0xCAFE)
{
return FAILED;
}
//printf("receive data:0X%X,0X%X",RxMessage.Data[0], RxMessage.Data[1]);
return PASSED; /* Test Passed */
}
4、配置CAN的中断接收
TestStatus CAN_Interrupt(void)
{
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CanTxMsg TxMessage;
uint32_t i = 0;
/* CAN register init */
CAN_DeInit(CAN1);
CAN_StructInit(&CAN_InitStructure);
/* CAN cell init */
CAN_InitStructure.CAN_TTCM=DISABLE; // 时间触发通信禁止
CAN_InitStructure.CAN_ABOM=DISABLE; // 离线退出是在中断置位清0后退出
CAN_InitStructure.CAN_AWUM=DISABLE; // 自动唤醒模式:清零sleep
CAN_InitStructure.CAN_NART=DISABLE; // 自动重新传送豹纹,知道发送成功
CAN_InitStructure.CAN_RFLM=DISABLE; // FIFO没有锁定,新报文覆盖旧报文
CAN_InitStructure.CAN_TXFP=DISABLE; // 发送报文优先级确定:标志符
CAN_InitStructure.CAN_Mode=CAN_Mode_LoopBack; // 回环模式
CAN_InitStructure.CAN_SJW=CAN_SJW_1tq; // 1tq、BS1、BS2的值跟波特率有关
CAN_InitStructure.CAN_BS1=CAN_BS1_8tq;
CAN_InitStructure.CAN_BS2=CAN_BS2_7tq;
CAN_InitStructure.CAN_Prescaler=1; // 分频系数为1
CAN_Init(CAN1, &CAN_InitStructure); // 初始化CAN1
/* CAN filter init */
CAN_FilterInitStructure.CAN_FilterNumber=1;
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0;
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
/* CAN FIFO0 message pending interrupt enable */
CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE); //中断使能
/* transmit 1 message */
TxMessage.StdId=0x00; // 标准标识符为0
TxMessage.ExtId=0x1234; // 设置扩展标示符(29位)
TxMessage.IDE=CAN_ID_EXT; // 使用扩展标识符
TxMessage.RTR=CAN_RTR_DATA; // 消息类型为数据帧,一帧8位
TxMessage.DLC=2; // 发送两帧信息
TxMessage.Data[0]=0xDE; // 第一帧信息
TxMessage.Data[1]=0xCA; // 第二帧信息
CAN_Transmit(CAN1, &TxMessage);
/* initialize the value that will be returned */
ret = 0xFF;
/* receive message with interrupt handling */
i=0;
while((ret == 0xFF) && (i < 0xFFF))
{
i++;
}
if (i == 0xFFF)
{
ret=0;
}
/* disable interrupt handling */
CAN_ITConfig(CAN1, CAN_IT_FMP0, DISABLE);
return (TestStatus)ret;
}
5、CAN的中断处理函数
检查是否接收到正确的消息。如果消息正确则设置标识符为1 否则设置为0
void USB_LP_CAN1_RX0_IRQHandler(void)
{
CanRxMsg RxMessage;
RxMessage.StdId=0x00;
RxMessage.ExtId=0x00;
RxMessage.IDE=0;
RxMessage.DLC=0;
RxMessage.FMI=0;
RxMessage.Data[0]=0x00;
RxMessage.Data[1]=0x00;
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
if((RxMessage.ExtId==0x1234) && (RxMessage.IDE==CAN_ID_EXT)
&& (RxMessage.DLC==2) && ((RxMessage.Data[1]|RxMessage.Data[0]<<8)==0xDECA))
{
ret = 1;
}
else
{
ret = 0;
}
}
6、这个实验比zangchao的简单多了。只是完成了LoopBack,没有进行协议转换和双机通讯。还比较弱智的说
7、例行性的上图
CAN 回环数据串口监视
CAN LoopBack时的寄存器
查看系统寄存器的方法
今天参考野火程序,弄了下CAN。还不错 Loopback模式正常收发数据。
配置CAN为中断接收模式
参考了野火的程序实现。
这里基本就是我对野火程序的理解,和拷贝
1、设置NVIC 使能CAN中断接收
static void CAN_NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable CAN1 RX0 interrupt IRQ channel */
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn; //中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; // 主优先级为0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 次优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
2、引脚GPIO设置
板子上的CAN引脚被从定义到了PB8和PB9.
需要使用AFIO的Renap功能
static void CAN_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE);
/* CAN1 Periph clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
/* Configure CAN pin: RX */ // PB8
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Configure CAN pin: TX */ // PB9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure);
//将CAN1引脚重定义到PB8 PB9 使用引脚服用功能
GPIO_PinRemapConfig(GPIO_Remap1_CAN1, ENABLE);
}
3、将CAN配置为LoopBack模式
配置完成后发送一帧数据,并接收。以检查是否配置成功,此处的接收使用了查询方式
TestStatus CAN_Polling(void)
{
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CanTxMsg TxMessage;
CanRxMsg RxMessage;
uint32_t i = 0;
uint8_t TransmitMailbox = 0;
/* CAN register init */
CAN_DeInit(CAN1);
CAN_StructInit(&CAN_InitStructure);
/* CAN cell init */
CAN_InitStructure.CAN_TTCM=DISABLE; // 时间触发通信禁止
CAN_InitStructure.CAN_ABOM=DISABLE; // 离线退出是在中断置位清0后退出
CAN_InitStructure.CAN_AWUM=DISABLE; // 自动唤醒模式:清零sleep
CAN_InitStructure.CAN_NART=DISABLE; // 自动重新传送豹纹,知道发送成功
CAN_InitStructure.CAN_RFLM=DISABLE; // FIFO没有锁定,新报文覆盖旧报文
CAN_InitStructure.CAN_TXFP=DISABLE; // 发送报文优先级确定:标志符
CAN_InitStructure.CAN_Mode=CAN_Mode_LoopBack; // 回环模式
CAN_InitStructure.CAN_SJW=CAN_SJW_1tq; // 1tq、BS1、BS2的值跟波特率有关
CAN_InitStructure.CAN_BS1=CAN_BS1_8tq;
CAN_InitStructure.CAN_BS2=CAN_BS2_7tq;
CAN_InitStructure.CAN_Prescaler=5; // 分频系数为5
CAN_Init(CAN1, &CAN_InitStructure); // 初始化CAN1
/* CAN 过滤器初始化 */
CAN_FilterInitStructure.CAN_FilterNumber=0;
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; //标识符过滤
//使用32位过滤器,接收报文标识符的每一位都必须跟过滤器标识符相同
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000; //设置标识符
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=0; //接收FIFO
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //使能过滤器
CAN_FilterInit(&CAN_FilterInitStructure);
/* transmit */
TxMessage.StdId=0x11; // 设定标准标识符(11位,扩展的为29位)
TxMessage.RTR=CAN_RTR_DATA; // 传输消息的帧类型为数据帧(还有远程帧)
TxMessage.IDE=CAN_ID_STD; // 消息标志符实验标准标识符
TxMessage.DLC=2; // 发送两帧,一帧8位
TxMessage.Data[0]=0xCA; // 第一帧数据
TxMessage.Data[1]=0xFE; // 第二帧数据
TransmitMailbox=CAN_Transmit(CAN1, &TxMessage); //处理好数据后,将数据贞挂号等待发送。
i = 0;
// 用于检查消息传输是否正常
while((CAN_TransmitStatus(CAN1, TransmitMailbox) != CANTXOK) && (i != 0xFF))
{
i++;
}
i = 0;
// 检查返回的挂号的信息数目
while((CAN_MessagePending(CAN1, CAN_FIFO0) < 1) && (i != 0xFF))
{
i++;
}
/* receive */
RxMessage.StdId=0x00;
RxMessage.IDE=CAN_ID_STD;
RxMessage.DLC=0;
RxMessage.Data[0]=0x00;
RxMessage.Data[1]=0x00;
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
if (RxMessage.StdId!=0x11)
{
return FAILED;
}
if (RxMessage.IDE!=CAN_ID_STD)
{
return FAILED;
}
if (RxMessage.DLC!=2)
{
return FAILED;
}
/* 判断发送的信息和接收的信息是否相等 */
if ((RxMessage.Data[0]<<8|RxMessage.Data[1])!=0xCAFE)
{
return FAILED;
}
//printf("receive data:0X%X,0X%X",RxMessage.Data[0], RxMessage.Data[1]);
return PASSED; /* Test Passed */
}
4、配置CAN的中断接收
TestStatus CAN_Interrupt(void)
{
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CanTxMsg TxMessage;
uint32_t i = 0;
/* CAN register init */
CAN_DeInit(CAN1);
CAN_StructInit(&CAN_InitStructure);
/* CAN cell init */
CAN_InitStructure.CAN_TTCM=DISABLE; // 时间触发通信禁止
CAN_InitStructure.CAN_ABOM=DISABLE; // 离线退出是在中断置位清0后退出
CAN_InitStructure.CAN_AWUM=DISABLE; // 自动唤醒模式:清零sleep
CAN_InitStructure.CAN_NART=DISABLE; // 自动重新传送豹纹,知道发送成功
CAN_InitStructure.CAN_RFLM=DISABLE; // FIFO没有锁定,新报文覆盖旧报文
CAN_InitStructure.CAN_TXFP=DISABLE; // 发送报文优先级确定:标志符
CAN_InitStructure.CAN_Mode=CAN_Mode_LoopBack; // 回环模式
CAN_InitStructure.CAN_SJW=CAN_SJW_1tq; // 1tq、BS1、BS2的值跟波特率有关
CAN_InitStructure.CAN_BS1=CAN_BS1_8tq;
CAN_InitStructure.CAN_BS2=CAN_BS2_7tq;
CAN_InitStructure.CAN_Prescaler=1; // 分频系数为1
CAN_Init(CAN1, &CAN_InitStructure); // 初始化CAN1
/* CAN filter init */
CAN_FilterInitStructure.CAN_FilterNumber=1;
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0;
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
/* CAN FIFO0 message pending interrupt enable */
CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE); //中断使能
/* transmit 1 message */
TxMessage.StdId=0x00; // 标准标识符为0
TxMessage.ExtId=0x1234; // 设置扩展标示符(29位)
TxMessage.IDE=CAN_ID_EXT; // 使用扩展标识符
TxMessage.RTR=CAN_RTR_DATA; // 消息类型为数据帧,一帧8位
TxMessage.DLC=2; // 发送两帧信息
TxMessage.Data[0]=0xDE; // 第一帧信息
TxMessage.Data[1]=0xCA; // 第二帧信息
CAN_Transmit(CAN1, &TxMessage);
/* initialize the value that will be returned */
ret = 0xFF;
/* receive message with interrupt handling */
i=0;
while((ret == 0xFF) && (i < 0xFFF))
{
i++;
}
if (i == 0xFFF)
{
ret=0;
}
/* disable interrupt handling */
CAN_ITConfig(CAN1, CAN_IT_FMP0, DISABLE);
return (TestStatus)ret;
}
5、CAN的中断处理函数
检查是否接收到正确的消息。如果消息正确则设置标识符为1 否则设置为0
void USB_LP_CAN1_RX0_IRQHandler(void)
{
CanRxMsg RxMessage;
RxMessage.StdId=0x00;
RxMessage.ExtId=0x00;
RxMessage.IDE=0;
RxMessage.DLC=0;
RxMessage.FMI=0;
RxMessage.Data[0]=0x00;
RxMessage.Data[1]=0x00;
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
if((RxMessage.ExtId==0x1234) && (RxMessage.IDE==CAN_ID_EXT)
&& (RxMessage.DLC==2) && ((RxMessage.Data[1]|RxMessage.Data[0]<<8)==0xDECA))
{
ret = 1;
}
else
{
ret = 0;
}
}
6、这个实验比zangchao的简单多了。只是完成了LoopBack,没有进行协议转换和双机通讯。还比较弱智的说
7、例行性的上图
CAN 回环数据串口监视
CAN LoopBack时的寄存器
查看系统寄存器的方法
169楼
体验硬件CRC
看看手册,STM32中有一个硬件CRC发生器。使用也比较简单:
1、开启CRC时钟
2、向CRC_DR寄存器中写入要计算的数据
3、从CRC_DR中读出数据
下边是代码:
1、初始化函数,开时钟
void CRC_Init(void)
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE);
}
2、计算数据块的CRC
CRCValue = CRC_CalcBlockCRC((uint32_t *)DataBuffer, BUFFER_SIZE); //计算一个数据块的CRC
按F12跟着进去,感觉这个CRC_CalcBlockCRC有问题。好像只返回了最后一个数据的CRC。并没有真正计算
查看手册才发现有这样的描述:每一次写入数据寄存器,其计算结果是前一次CRC计算结果和新计算结果的组合(对整个32位字进行CRC计算,而不是逐字节地计算)。
所以,计算结果就是这个样子的。
如果需要获得单独这组数据的计算结果,在计算之前应先使用CRC_ResetDR()复位CRC_DR寄存器。
3、计算单个数据的CRC
(1)寄存器方式
CRC->DR = DataBuffer[i]; //计算一个数据的CRC
CRCValue = CRC->DR;
(2)库函数方式
CRCValue = CRC_CalcCRC(DataBuffer[i]);
对比两种方式结果是一样的
注:根据前面的说明,要获得单个数据的CRC,在计算前也要复位CRC_DR寄存器。
4、获得CRC结果后,可以使用printf将结果打印到电脑查看:
printf("32-位数据 CRC 校验码为:0X%X\r\n", CRCValue);
4、要抓的图太多了。我想已经够明白了,不需要再贴图了
看看手册,STM32中有一个硬件CRC发生器。使用也比较简单:
1、开启CRC时钟
2、向CRC_DR寄存器中写入要计算的数据
3、从CRC_DR中读出数据
下边是代码:
1、初始化函数,开时钟
void CRC_Init(void)
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE);
}
2、计算数据块的CRC
CRCValue = CRC_CalcBlockCRC((uint32_t *)DataBuffer, BUFFER_SIZE); //计算一个数据块的CRC
按F12跟着进去,感觉这个CRC_CalcBlockCRC有问题。好像只返回了最后一个数据的CRC。并没有真正计算
查看手册才发现有这样的描述:每一次写入数据寄存器,其计算结果是前一次CRC计算结果和新计算结果的组合(对整个32位字进行CRC计算,而不是逐字节地计算)。
所以,计算结果就是这个样子的。
如果需要获得单独这组数据的计算结果,在计算之前应先使用CRC_ResetDR()复位CRC_DR寄存器。
3、计算单个数据的CRC
(1)寄存器方式
CRC->DR = DataBuffer[i]; //计算一个数据的CRC
CRCValue = CRC->DR;
(2)库函数方式
CRCValue = CRC_CalcCRC(DataBuffer[i]);
对比两种方式结果是一样的
注:根据前面的说明,要获得单个数据的CRC,在计算前也要复位CRC_DR寄存器。
4、获得CRC结果后,可以使用printf将结果打印到电脑查看:
printf("32-位数据 CRC 校验码为:0X%X\r\n", CRCValue);
4、要抓的图太多了。我想已经够明白了,不需要再贴图了
回复
有奖活动 | |
---|---|
【有奖活动】分享技术经验,兑换京东卡 | |
话不多说,快进群! | |
请大声喊出:我要开发板! | |
【有奖活动】EEPW网站征稿正在进行时,欢迎踊跃投稿啦 | |
奖!发布技术笔记,技术评测贴换取您心仪的礼品 | |
打赏了!打赏了!打赏了! |
打赏帖 | |
---|---|
vscode+cmake搭建雅特力AT32L021开发环境被打赏30分 | |
【换取逻辑分析仪】自制底板并驱动ArduinoNanoRP2040ConnectLCD扩展板被打赏47分 | |
【分享评测,赢取加热台】RISC-V GCC 内嵌汇编使用被打赏38分 | |
【换取逻辑分析仪】-基于ADI单片机MAX78000的简易MP3音乐播放器被打赏48分 | |
我想要一部加热台+树莓派PICO驱动AHT10被打赏38分 | |
【换取逻辑分析仪】-硬件SPI驱动OLED屏幕被打赏36分 | |
换逻辑分析仪+上下拉与多路选择器被打赏29分 | |
Let'sdo第3期任务合集被打赏50分 | |
换逻辑分析仪+Verilog三态门被打赏27分 | |
换逻辑分析仪+Verilog多输出门被打赏24分 |