我落入了PKK Milander JSC生产的具有32位RISC内核ARM Cortex-M3 128kB Flash和32kB RAM的俄罗斯微控制器K1986BE92QI的手下,我立即想进行实际的研究和测试。
该微控制器是中国人对速卖通羡慕的包装。 芯片位于厚铝箔盒中,铝箔盒用铝箔纸包裹,并用泡沫橡胶包裹,整个“三明治”放在纸板箱中,内壁覆盖有铝箔。 通常,要防止高空静电。


微控制器上有标签和产品选择协议,非常好。

首先,有必要开发调试板的电路图并确定组件。 他停下了最少的组件:用于从USB端口供电的3.3v稳压器,8MHz石英谐振器,miniUSB连接器,复位按钮,上拉电阻和Sip连接器。 我认为对于微控制器的初步实验就足够了。 还要设置smd开关以选择内置引导加载程序的模式。 微控制器允许您使用两个串行接口UART或JTAG / SWD之一来选择下载程序的方法,而JTAG允许您在微控制器中调试程序。 程序加载方法的选择由输出PF4,PF5,PF6上的逻辑电平决定。 下表列出了所有可能的选项:

该微控制器是在塑料外壳LQFP64中制成的芯片,其引脚宽度为0.3 mm,引脚之间为0.2 mm,这表明不可能使用LUT技术制造出质量合格的印刷电路板,但是经验证明事实恰恰相反。 在几个小时内,就用Sprint Layout制作了PCB图,将其印刷在Lamond高密度纸上并转移到玻璃纤维上。 蚀刻是在每只眼睛的过氧化物和柠檬酸溶液中进行的,大约需要一个小时,令人惊讶的是,导体的质量在第一时间是可以接受的,这令人高兴。

这样就创建了电路板,焊接了所有组件,并保留以进行编程。 我们将使用Keil-MDK ARM uVision 5.0的开发环境, 标准外围设备库 + 软件包由微控制器制造商分发给它。 我不想用UART编程,所以我决定使用ST-Link v2在线编程器/调试器,或更确切地说,从未知的中国制造商那里克隆它。 Keil开箱即用地支持它,但是微控制器(尽管文档说它支持SWD接口)却忘记了如何以及在何处进行连接。 在Internet上搜索“ JTAG-SWD适配器”后,发现SWDIO线连接到JTAG-TMS,SWCLK线连接到JTAG-TCK,“哦,奇迹!” 一切正常,一个测试程序被闪存到微控制器中。

这就是快乐的结束,因为在固件之后,微控制器虽然可以工作,但似乎已停止与调试器一起工作。 显然,在闪烁之后,尽管在JTAG-A所在的程序端口B中甚至没有初始化,但JTAG-A端口线也被重新定义用于其他功能。 我不想理解这一点,因为还有一个JTAG-B。 当连接到备用JTAG接口时,一切都像时钟一样工作。 将来,我们将使用它进行编程和调试。
我自己的第一个任务是使用Modbus协议将控制器连接到SCADA系统。 为了不浪费时间,我们采用了跨平台的freemodbus freemodbus库,并将其移植到我们的微控制器中。
要在Milander的微控制器上的Keil中创建项目,您需要首先安装软件包。 只需双击文件即可完成。 Next Keil将自己做所有事情。
因此,我们创建了一个新项目。 我们选择所需的微控制器和库组件:

在项目树中,创建Modbus Slave组,并从其中的Freemodbus库添加以下文件:

并且不要忘记在项目选项中向编译器指示库目录的以下路径。

现在,您可以开始使用标准外设库将Freemodbus库专门移植到我们的微控制器。 为此,您需要在portersial.c文件中指定UART端口xMBPortSerialInit的初始化功能
BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity ) { // F RST_CLK_PCLKcmd(RST_CLK_PCLK_PORTF, ENABLE); // PORT_InitTypeDef uart2_port_set; // F UART // PORT_StructInit(&uart2_port_set); // uart2_port_set.PORT_FUNC = PORT_FUNC_OVERRID; // uart2_port_set.PORT_SPEED = PORT_SPEED_MAXFAST; // uart2_port_set.PORT_MODE = PORT_MODE_DIGITAL; // PF1 UART_TX () uart2_port_set.PORT_Pin = PORT_Pin_1; uart2_port_set.PORT_OE = PORT_OE_OUT; PORT_Init(MDR_PORTF, &uart2_port_set); // PF0 UART_RX () uart2_port_set.PORT_Pin = PORT_Pin_0; uart2_port_set.PORT_OE = PORT_OE_IN; // UART // UART2 RST_CLK_PCLKcmd(RST_CLK_PCLK_UART2, ENABLE); // UART UART_InitTypeDef UART_InitStructure; // UART = 1 UART_BRGInit(MDR_UART2,UART_HCLKdiv1); // UART // – 115200 UART_InitStructure.UART_BaudRate = ulBaudRate; // – 8 UART_InitStructure.UART_WordLength = UART_WordLength8b; // - UART_InitStructure.UART_StopBits = UART_StopBits1; // UART_InitStructure.UART_Parity = UART_Parity_No; // FIFO , // .. UART_InitStructure.UART_FIFOMode = UART_FIFO_OFF; // UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_RXE | UART_HardwareFlowControl_TXE; // UART2 UART_Init(MDR_UART2, &UART_InitStructure); // UART UART_Cmd(MDR_UART2, ENABLE); return TRUE; }
读写功能:
BOOL xMBPortSerialPutByte( CHAR ucByte ) { // UART_SendData(MDR_UART2,ucByte); return TRUE; } BOOL xMBPortSerialGetByte( CHAR * pucByte ) { // *pucByte = (uint8_t) UART_ReceiveData(MDR_UART2); return TRUE; }
UART中断处理程序
void USART2_IRQHandler(void) { /* ---------------------------------------------------*/ if((UART_GetITStatus(MDR_UART2,UART_IT_RX)) != RESET) { prvvUARTRxISR( ); } /* ------------------------------------------------*/ if((UART_GetITStatus(MDR_UART2,UART_IT_TX)) !=RESET) { prvvUARTTxReadyISR( ); } }
接下来,我们编辑portimer.c文件,在其中配置了计时器,该计时器生成临时报告以跟踪modbus协议数据包的结尾。
BOOL xMBPortTimersInit( USHORT usTim1Timerout50us ) { MDR_RST_CLK->PER_CLOCK |= (1<<14); // TIM1 MDR_RST_CLK->TIM_CLOCK = 0x0; MDR_RST_CLK->TIM_CLOCK |= (1<<24); // TIM1_CLK_EN MDR_RST_CLK->TIM_CLOCK |= 0x07; // HCLK/8 MDR_TIMER1->CNTRL = 0x00000002; // MDR_TIMER1->CNT = 0x00000000; // MDR_TIMER1->PSG = 0x2; //f/1 while((MDR_TIMER1->CNTRL & 0x004) != 0) {__NOP();} // MDR_TIMER1->ARR = usTim1Timerout50us; // while((MDR_TIMER1->CNTRL & 0x004) != 0) {__NOP();} // MDR_TIMER1->IE = 0x00000002; //(CNT==ARR)->IE NVIC->ISER[0] = (1<<14); // Global EN for IRQ14 MDR_TIMER1->CNTRL |= (1<<0); //Timer1 ON return TRUE; } inline void vMBPortTimersEnable( ) { /* */ MDR_TIMER1->CNTRL |= (1<<0); //Timer1 ON } inline void vMBPortTimersDisable( ) { /* */ MDR_TIMER1->CNTRL &= ~(1<<0); //Timer1 OFF } static void prvvTIMERExpiredISR( void ) { ( void )pxMBPortCBTimerExpired( ); } void Timer1_IRQHandler(void) { // MDR_TIMER1->STATUS &= ~0x002; //IE FLAG=0 prvvTIMERExpiredISR( ); }
在main.c中,我们将添加modbus寄存器处理功能,我们将使用存根对未使用的寄存器进行混音。
/* ----------------------- Defines ------------------------------------------*/ #define REG_INPUT_START 1000 #define REG_INPUT_NREGS 4 /* ----------------------- Static variables ---------------------------------*/ static USHORT usRegInputStart = REG_INPUT_START; static USHORT usRegInputBuf[REG_INPUT_NREGS]; eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs ) { eMBErrorCode eStatus = MB_ENOERR; int iRegIndex; if( ( usAddress >= REG_INPUT_START ) && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) ) { iRegIndex = ( int )( usAddress - usRegInputStart ); while( usNRegs > 0 ) { *pucRegBuffer++ = ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 ); *pucRegBuffer++ = ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF ); iRegIndex++; usNRegs--; } } else { eStatus = MB_ENOREG; } return eStatus; } eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode ) { return MB_ENOREG; } eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode ) { return MB_ENOREG; } eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete ) { return MB_ENOREG; }
这是移植的结尾,它仅保留在int main(void)函数中以初始化库并在循环中调用eMBPoll();
int main (void) { eMBErrorCode eStatus; // UART , portserial.c xMBPortSerialInit eStatus = eMBInit( MB_RTU, 0x0A, 0, 19200, MB_PAR_NONE ); /* Enable the Modbus Protocol Stack. */ eStatus = eMBEnable( ); while(1) { eStatus = eMBPoll( ); // if (eStatus!= MB_ENOREG){}; /* Here we simply count the number of poll cycles. */ usRegInputBuf[0]++; } }
我们编译所有内容都没有错误,但是没有任何效果。 在调试模式下,我们发现正在处理数据包,并且程序在传输初始化时挂起。 从UART发送器打开中断时,不会调用该中断,并且程序将进入无限循环。 在研究了微控制器上“ UART的描述”部分的规范后,发现了一个注释:
发射机中断在边缘而不是信号电平上起作用。 如果在将数据写入发送器FIFO缓冲区之前允许模块及其中断,则不会产生中断。 仅当FIFO缓冲区为空时才会发生中断。
好吧,没关系,我们正在寻找转移的起点,在mbrtu.c文件中我们找到了几行代码
/* Activate the transmitter. */ eSndState = STATE_TX_XMIT; vMBPortSerialEnable( FALSE, TRUE );
然后将字节强制发送到UART发送器,为此我们添加了以下行:“ xMBRTUTransmitFSM();” 一切开始正常运行,数据包运行,读取寄存器,然后这是技术问题。