STM8s如何操作共用体的使用

在串口通信中,我们往往会解析很多协议,而且在协议中会出现不同数据类型的协议;当然我们也可以把协议都定义成某一种数据格式,但是那样会出现协议的复杂度,但是不那样处理的话,程序在处理协议时会变得很难。
为了解决这个问题,本人就应用了结构体和联合体的方式来解决了,反正个人认为自己的方法很实用;
具体方法是:
1、将不同类型的协议分成不同的结构体
struct ChaX_Data& &&&//检测数据协议
& && &&&float & & & & & & & & & & & & P0;//功率
& & & & float & & & & & & & & & & & & DN;//电能
& & & & u16& & & & & & & & & & & & Ia;//A相电流
& & & & u16 & & & & & & & & & & & & Ic;//C相电流
& & & & u16 & & & & & & & & & & & & Ua;//AB相电压
& & & & u16 & & & & & & & & & & & & Ub;//BC相电压
& & & & u16 & & & & & & & & & & & & Uc;//AC相电压
struct Time_bit& && & //时间设置协议
& & & & u8&&//年
& & & & u8 //月
& && &&&u8&&//日
& && &&&u8&&//时
& && &&&u8& &//分
& && &&&u8& &//秒
2、将不同的协议结构体组合在同一个联合体内
& & & & struct ChaX_Data & & & & ChaX;
& & & & struct Time_bit & & & & T
& & & & u8 & & & & & & & & & & & & Buff[30];
} UART_RX;
3、串口收到数据程序处理
u8 RX_Num = 0;
void Uart_RX_SYS()
&&if(USART_GetFlagStatus(UART4, USART_FLAG_RXNE) != RESET)
UART_RX . Buff [RX_Num]= USART_ReceiveData(UART4);
RX_Num ++;
当然我这只是一个简单的比方,具体程序还得更加复杂。
4、应用方式:直接在程序中应用
如果是检测数据协议分析 我们可以这样应用
float Flaot_P
u16 U16_Ia;
Flaot_Power = UART_RX . ChaX . P0;
U16_Ia = UART_RX . ChaX . Ia;
如果时间设置协议 我们可以这样应用
Set_ min = UART_RX . Time.
由于本人从小语文就不是很好,所以表达的不是很清楚 希望各位多多谅解!
&&&&&&&&&&
EEWORLD 官方微信
Powered by2359人阅读
在前两篇文章中我们介绍了IO口模拟串口发送数据和接收数据,前两种方法都是使用定时器来进行发送和接收,没有用到中断,优点是逻辑简单,但是缺点很明显,只能进行单个字节的发送和接收,而且不能同时工作。因此在实际工程中没有什么作用,仅供学习使用。使用中断方式我们可以发送和接收多个字节的数据。
1、使用中断方式进行IO口模拟串口发送和接收数据的原理
这篇文章我将使用中断的方式进行发送和接收,同样的,由于原理缺陷,这篇文章介绍的方法无法同时接收和发送,而且由于发送会延时,是一个不太好的方法,仅供学习使用。
注意:这篇文章实现的IO口模拟串口无法同时接收和发送数据!如有需要在实际项目中使用IO口模拟串口工作,请移步:
1.1、发送数据的原理
我们使用定时器更新中断来进行数据的发送,首先在发送函数中开启中断,然后在中断函数中逐位发送,直到发送10位(一个字节,我们暂时没有使用校验位)后关闭更新中断。
1.2、接收数据的原理
我们使用单片机的外部中断(IO中断)来开启比较中断,在比较中断中逐位接收,直到接收了10位后关闭比较中断,并保存接收的有效字节个数。
2、实现过程
同样的,在实现过程中,我们在工程文件夹SimUART中共分了4个文件夹(分别为System:存放系统文件;Project:存放项目文件;User:存放main.c和UserApp.c;My_Lib:存放其它常用的文件)。根据我们将用到的单片机的资源,我们在My_Lib中分了二个文件夹,分别是——IO:存放与IO口相关函数的文件;Time:与定时器和中断相关函数的文件。下面我贴出相关函数的.c文件,而.h文件省略不写,有需要的同学可以根据文章后面的网址下载使用。我的编程环境是IAR,需要自己建立IAR工程。下面详细介绍(Project和System省略不写,其中System只用了stm8s.h)。
2.1、一切从main()函数开始
同样的,我们建立完工程后需要从main()函数开始,为便于理解,我将使用逻辑伪代码,逻辑伪代码如下:
int main( void )
单片机时钟初始化;
IO口初始化;
定时器初始化;
中断初始化;
if( 需要发送的数据数 & 0 )
需要发送的数据数 = 0;
我们首先需要进行初始化的配置All_Config()【在UserApp.c中】,代码如下:
//head file
#include &UserApp.h&
#include &IO.h&
#include &User.h&
#include &Time.h&
#include &Delay.h&
u16 SimUART_SendData = 0xFF;
u8 SimUART_SendData_BitNum = 10;
RxData_ValidNum = 0;
u16 RxDataValue_Temp = 0x0000;
//初始化函数
void All_Config( void )
Clock_Config();
IO_Init();
TIM2_Init();
EXTI_Init();
其中User.h是我将自己常用的宏写在了一个文件里面,对应于main.c。在没有接外部时钟的时候,STM8S003F在启动时主时钟默认为HSI RC时钟的8分频,我们这里的初始化仅指定为16MHZ高速内部RC振荡器(HSI),也可以省略不写,Clock_Config()【在UserApp.c中】函数代码如下:
//初始化时钟 选择内部16M晶振
void Clock_Config()
CLK-&CKDIVR &= ~( BIT(4) | BIT(3) );
}我选择单片机的PD2作为我的模拟串口的数据发送口,选择PD3作为我的模拟串口的数据接收口,IO_Init()【在IO.c中】函数代码如下:
//head file
#include &IO.h&
#include &User.h&
void IO_Init()
//TXD:TXD位推挽输出
SimUART_PORT-&ODR |=
SimUART_PIN_TX; //
SimUART_PORT-&DDR |=
SimUART_PIN_TX; //
SimUART_PORT-&CR1 |=
SimUART_PIN_TX; //
SimUART_PORT-&CR2 &= ~SimUART_PIN_TX; //
//RXD:悬浮输入 高电平 PD3
SimUART_PORT-&IDR |=
SimUART_PIN_RX; //
SimUART_PORT-&DDR &= ~SimUART_PIN_RX; //
SimUART_PORT-&CR1 &= ~SimUART_PIN_RX; //
SimUART_PORT-&CR2 &= ~SimUART_PIN_RX; //
}其中在IO.h中的宏定义为:
#define SimUART_PORT GPIOD
#define SimUART_PIN_TX 0X04
#define SimUART_PIN_RX 0X08
#define SimUART_PIN_RX_0 0X00 //PD3
#define SimUART_PIN_RX_1 0X08 //PD3定时器的初始化和前面一样,具体操作可以见这里。代码如下:
void TIM2_Init()
CLK-&PCKENR1 |= CLK_PCKENR1_TIM2;&span style=&white-space:pre&&
&/span&//使能 TIM2
TIM2-&PSCR
&span style=&white-space:pre&& &/span&//16分频 1MHZ 1us
TIM2-&ARRH
= ARRValue_9600 && 8;
&span style=&white-space:pre&& &/span&//自动装载 每52us复位一次TIM2
TIM2-&ARRL
= ARRValue_9600;
&span style=&white-space:pre&& &/span&//每1us递减1
TIM2-&CNTRH = 0;
&span style=&white-space:pre&&
&/span&//定时器清零
TIM2-&CNTRL = 0;
TIM2-&CR1 |= TIM2_CR1_CEN;
&span style=&white-space:pre&&
&/span&//开启定时器
}其中Time.h中的宏定义为:
#define ARRValue_9600
104中断初始化EXTI_Init()【在UserApp.c中】代码如下:
//初始化中断
void Interrupt_Init()
//允许更新中断
//TIM2-&CR1 &= ~TIM2_CR1_UDIS;
//允许更新
可以不管默认为0
TIM2-&IER |= TIM2_IER_UIE;
//更新中断使能
//IO口下降中断 初始化
SimUART_PORT-&CR2 |= 0x08;
//使能外部中断
EXTI-&CR1 = 0x80;
//仅下降沿触发
//禁止比较中断
TIM2-&IER &= ~TIM2_IER_CC1IE;
//禁止捕获/比较1
根据上面的原理,我们知道:更新中断是在发送函数中打开的,因此更新初始化中使能;IO中断是通过下降沿(串口数据的起始位为低电平)打开的,因此设置成使能和下降沿触发;比较中断实在IO中断中打开的,因此设置成禁止。
2.2、模拟串口发送数据
完成时钟、IO口、定时器、中断的初始化以后我们就可以开始主体程序的设计了,逻辑伪代码如下:
//发送 函数
void SimUART_SendByte(u8 SendData)
等待一个字节发送完毕;
第一步:清除 更新更新中断标志位(保证不进入更新中断);
第二步:数据调整(起始位为0,数据位不变,停止位和其它位为1);
第三步: 开启更新中断;
//定时器更新中断
发送接收到的数据
#pragma vector = 对应向量标志位
__interrupt void SimUART_Update_IRQHandler(void)
第一步:清除 更新中断标志位(保证不进入更新中断);
发送一个位计数;
if( 发送位为1 )
发送高电平;
发送低电平;
移位,发送下一个位;
//完成了一个位的发送
if( 发送了10个位 )
进入发送函数,首先应该清除更新中断标志位,然后写功能代码,结束前需要打开更新中断,从而去执行更新中断的代码。我们需要考虑为何需要一个延时来等待一个字节完成发送。在中断函数中我们是让一个字节发送完成以后才关闭中断的,如果不延时,可能发生一个字节还没有发送完成,却进入下一个更新中断的情况,因此需要等待,我们直接用一个标志位就能解决。对应向量标志位通过查芯片手册和头文件可以得到。发送函数在UserApp.c中,更新中断在Time.c中,发送部分代码如下:
void SimUART_SendByte(u8 SendData)
while( SimUART_SendData_BitNum & 10 );
//清 更新中断标志位
TIM2-&SR1 &= ~TIM2_SR1_UIF;
//00 0000 保证最低位(起始)为0,除数据位后全部为1
SimUART_SendData = ( ( SendData && 1 )| (0xFE00) );
SimUART_SendData_BitNum = 0;
//开启更新中断
TIM2-& IER |= TIM2_IER_UIE;
}//定时器更新中断
发送接收到的数据
#pragma vector = TIM2_Updata_vector
__interrupt void SimUART_Update_IRQHandler(void)
//第一步,清中断标志位
TIM2-&SR1 &= ~TIM2_SR1_UIF;
//发送一个位 计数
SimUART_SendData_BitNum++;
if( ((SimUART_SendData) & 0X0001) )
//如果是高电平,发高电平
SimUART_PORT-&ODR |= SimUART_PIN_TX;
//如果是低电平,发低电平
SimUART_PORT-&ODR &= ~SimUART_PIN_TX;
//发送一个位
SimUART_SendData &&= 1;
if( 10 &= SimUART_SendData_BitNum )
TIM2-& IER &= ~TIM2_IER_UIE;
2.3、模拟串口接收数据
接收字节也是一个字节一个字节的接收的,由于单片机的始终可能存在误差(这是不可避免的),因此我们需要像个方法来除去这个误差,我们可以增大采样速度(减少采样时间)来排除,比如:我采集5次(发送数据波特率为9600,采集就要是这个的5倍速度),如果有三个是高电平我们就认为该位是高电平。另一种方法是,我们让采样点始终在一个电平的中间,这就用到了IO中断来开启比较中断。具体实现的逻辑伪代码如下:
//接收数据
//判断一个数据的开始
IO外部中断
#pragma vector = 对应向量标志位
__interrupt void SimUART_IO_IRQHandler(void)
关闭 IO中断 ;
设置 比较中断 ;
清除 比较中断 中断标志位;
打开 比较中断 ;
//接收数据
#pragma vector = 对应向量标志位
__interrupt void SimUART_Capture_IRQHandler(void)
清除 比较中断 标志位;
接收位个数计数;
if( 接收位为1 )
将相应的位置1;
if( 接收了10个位 )
接收位个数清零;
关 比较中断;
清除 IO中断标志位;
开 IO中断;
接收到的有效字节个数计数;
两个中断函数均在Time.c中,具体实现代码如下:
//判断一个数据的开始
IO外部中断
#pragma vector = EXTI3_PD_vector
__interrupt void SimUART_IO_IRQHandler(void)
//关闭IO中断
SimUART_PORT-&CR2 &= ~0x08;
//设置比较中断
TIM2-& CCMR1 &= 0x00;
//还是有问题
//TIM2-& CCR1H = 0;
TIM2-& CCR1L = TIM2-&CNTRL + ( ARRValue_9600/2 );
RxDataValue = 0x0000;
//TIM2-&CCER1 |= 0x00;
TIM2-&SR1 &= ~TIM2_SR1_CC1IF;
//清中断标志位
TIM2-&IER |=
TIM2_IER_CC1IE;
//使能 捕获/比较中断1
//接收数据
#pragma vector = TIM2_Capture_vector
__interrupt void SimUART_Capture_IRQHandler(void)
//第一步,清中断标志位(防止始终进入中断)
TIM2-&SR1 &= ~TIM2_SR1_CC1IF;
//清中断标志位
RxDataNum++;
if( SimUART_PIN_RX_1 == (SimUART_PORT-&IDR & SimUART_PIN_RX_1 ) )//其次,接收10个位
RxDataValue |= ( 0x01 && (RxDataNum) );//该位 置1
//第二步,读IO输入
//首先,判断是否接收了10个位
if(10 == RxDataNum)
//如果是则
RxDataNum = 0;
TIM2-&IER &=
~TIM2_IER_CC1IE;
//1、关 比较中断
//STM8S没有外中断标志位,STM8L有标志位,因此暂时不需要清中断标志位
SimUART_PORT-&CR2 |= 0x08;
//2、开 IO中断
//处理有效数据
(RxDataValue & 0x0402) == 0x0400 )
RxDataValue_Temp = ( RxDataValue && 2 );
RxData_ValidNum++;//接收字有效 节数
2.4、补全main()函数
int main( void )
All_Config();
_asm(&rim&);
//开总中断
//发送循环
if( RxData_ValidNum & 0x00 )
SimUART_SendByte( RxDataValue_Temp );
RxData_ValidNum = 0x00;
//return 0;
至此我们使用中断的方法来进行IO口模拟串口(未使用库函数)收发数据的功能已经实现,在本文章中,为了方便,我使用我的发送数据来验证我接收数据的正确性,因此先写的发送数据,再写的接收数据。正如前面所说,我是一个位一个位的发送和接受,在发送过程中有发送延时,这样的后果是如果是一个字符串的收发是没问题的,但是由于没有使用缓存区(即一个数组),导致我们收发数据不能分布于各个任务中,代码在实际项目中可能会出现一些问题,例如已接受就得发送,否则会出现错误,这回影响单片机在执行任务时产生问题。我将在后面进行介绍我们在实际工程中能够使用的全双工串口程序。值得注意的是,收发应该是单独存在的,我这里是为了方便反而让我的发送程序发送接收到的数据。
相关代码可以移步下面的地址下载使用,欢迎大家和我一起学习和交流。
/***************************************************************************************************************************************************************************************/
1、修改时间:
作者:Alan
说明:完成文章。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:60816次
积分:1227
积分:1227
排名:千里之外
原创:49篇
译文:13篇
评论:16条
(2)(1)(1)(2)(8)(5)(2)(1)(1)(11)(10)(12)(5)(2)(1)(3)此程序结果为什么是60501
char c[2];
w.c[0]='A';
w.c[1]='a'; clrscr();
printf("%o\n",w.a);
下面是结果截图:
<img onerror="imgDelByClass('comimg_box');" class="piczoom mpic" alt="此
因为是公用体,所以,a变量和c数组的地址相同
赋值语句w.c[0]='A'; w.c[1]='a';使得这个公共存储空间的低字存有65(对应二进制是),高字节存有97(对应二进制是),所以a变量的值就是这两个字节中存放的二进制数0001,输出时是其八进制形式,即:
0 110 000 101 000 001==> 060501。
具体问题具体分析!还有人说1个夹走1个等于0,其实没必要深究的,但也许就是因为你深究结果你发现了别人没发现的东西!那你就成了科学家了!哈哈
个位加9后各位等于6的数应该是7,即这道加法题目中两数个位分别为7和6;
因为个位相加后进了1到十位,所以两数十位相加应为8-1=7,但其中一个十位数是3,错...
关于普及岛语言与价位问题?
答: 如果你理解能力强考数据库,其中比如关系数据库之类的需要去领悟
如果你记忆能力强考网络,各种各样的名词,背下来就得分。
还有官方指定的教材要有,上机最好做一做...
答: 新年好!首先,你必须了解计算机的组成和结构以及操作系统的运作原理,这是基础
如果你想学习开发多线程、WINDOWS应用、动态链接库、WINDOWS组件的话,建...
大家还关注
确定举报此问题
举报原因(必选):
广告或垃圾信息
激进时政或意识形态话题
不雅词句或人身攻击
侵犯他人隐私
其它违法和不良信息
报告,这不是个问题
报告原因(必选):
这不是个问题
这个问题分类似乎错了
这个不是我熟悉的地区
相关问答:123456789101112131415结构体套了个共用体?模拟位操作?!~~~什么意思 求指点【c语言吧】_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:537,746贴子:
结构体套了个共用体?模拟位操作?!~~~什么意思 求指点收藏
上代码typedef struct{
__IO uint32_t MASKED_ACCESS[4096];/*!& Offset: 0x0000 to 0x3FFC Port data Register for pins
/* PIOn_0 to PIOn_11 (R/W)
uint32_t RESERVED0[4095];
__IO uint32_t DATA;
/*!& Offset: 0x3FFC Port data Register (R/W)
uint32_t RESERVED1[4096];
__IO uint32_t DIR;
/*!& Offset: 0x8000 Data direction Register (R/W)
__IO uint32_t IS;
/*!& Offset: 0x8004 Interrupt sense Register (R/W)
__IO uint32_t IBE;
/*!& Offset: 0x8008 Interrupt both edges Register (R/W)
__IO uint32_t IEV;
/*!& Offset: 0x800C Interrupt event Register
__IO uint32_t IE;
/*!& Offset: 0x8010 Interrupt mask Register (R/W)
__IO uint32_t RIS;
/*!& Offset: 0x8014 Raw interrupt status Register (R/ )
__IO uint32_t MIS;
/*!& Offset: 0x8018 Masked interrupt status Register (R/ )
__IO uint32_t IC;
/*!& Offset: 0x801C Interrupt clear Register (R/W)
*/} LPC_GPIO_TypeD
涉及到汇编的知识, 不知道你懂不懂汇编,不然很难跟你讲
基本还是懂一点的~~~
共用体套用结构体,MCU底层编程常用的一种小技巧!
.............
   千   年    梦 往   , 事    弹 如 曲   指 水 已   间 淡 终 旧   , 如 , 梦     烟 人 初     。 亦 醒       散 已       , 千         年          。 
登录百度帐号推荐应用

我要回帖

更多关于 共用体的使用 的文章

 

随机推荐